Decompiled source of UltraBeat v0.0.1

plugins/UltraBeat/UltraBeat.dll

Decompiled 7 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json.Linq;
using UnityEngine;
using UnityEngine.Audio;
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(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("UltraBeat")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("UltraBeat")]
[assembly: AssemblyTitle("UltraBeat")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
public class ConductorScript
{
	private ManualLogSource l;

	public AudioSource music;

	private Dictionary<string, SongMap> Songs = new Dictionary<string, SongMap>();

	private SongMap song;

	private float songPosition;

	private float start_dspTime;

	public int beatNumber = 0;

	private float lastBeat;

	private int startBeat;

	public bool active = false;

	private Dictionary<string, bool> enabledBeats = new Dictionary<string, bool>();

	private float lastTime = -1f;

	public event Action<int, Dictionary<string, bool>> onBeat;

	public ConductorScript(string mapDirectory, ManualLogSource l)
	{
		//IL_007e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0085: Expected O, but got Unknown
		this.l = l;
		string[] files = Directory.GetFiles(mapDirectory, "*.json");
		string[] array = files;
		foreach (string path in array)
		{
			string text = File.ReadAllText(path);
			JObject val = JObject.Parse(text);
			string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path);
			JObject val2 = (JObject)val["maps"];
			Dictionary<string, bool[]> beats = val2.Properties().ToDictionary((JProperty prop) => prop.Name, (JProperty prop) => ((IEnumerable<JToken>)prop.Value).Select((JToken value) => Extensions.Value<int>((IEnumerable<JToken>)value) == 1).ToArray());
			Songs[fileNameWithoutExtension] = new SongMap(fileNameWithoutExtension, beats, (float)val["offset"], (float)val["bpm"]);
		}
		l.LogInfo((object)("Successfully loaded " + files.Length + " beat maps."));
	}

	public void NewSource(AudioSource music)
	{
		this.music = music;
		Reset();
	}

	public void Pause()
	{
		l.LogInfo((object)"Paused conductor.");
		music = null;
		song = null;
		active = false;
	}

	public void Update()
	{
		if (song == null)
		{
			return;
		}
		if (music.time < lastTime)
		{
			Reset();
		}
		lastTime = music.time;
		songPosition = music.time - song.offset;
		if (beatNumber > song.beats.Values.First().Length - 1)
		{
			l.LogError((object)"Beats exceeded mapping length!");
		}
		if (!(songPosition > lastBeat))
		{
			return;
		}
		foreach (KeyValuePair<string, bool[]> beat in song.beats)
		{
			enabledBeats[beat.Key] = beat.Value[beatNumber];
		}
		this.onBeat?.Invoke(beatNumber, enabledBeats);
		beatNumber++;
		lastBeat += song.Crotchet;
	}

	public float percentageEnabled(string mapKey)
	{
		float num = (float)song.leftEnabled[mapKey][Mathf.Max(0, beatNumber - 1)] * song.Crotchet;
		float num2 = (float)song.rightEnabled[mapKey][Mathf.Min(song.rightEnabled[mapKey].Length - 1, beatNumber)] * song.Crotchet;
		return (num >= num2) ? 1f : ((songPosition - num) / (num2 - num));
	}

	public float percentageBeat(float nBeats)
	{
		return (songPosition - lastBeat) / (song.Crotchet * nBeats);
	}

	private void Reset()
	{
		if (Songs.ContainsKey(((Object)music.clip).name))
		{
			lastBeat = -1f;
			beatNumber = 0;
			song = Songs[((Object)music.clip).name];
			enabledBeats.Clear();
			l.LogInfo((object)("Resetting with song: " + ((Object)music.clip).name + ", offset: " + song.offset + ", bpm: " + song.bpm));
			active = true;
		}
		else
		{
			song = null;
			active = false;
		}
	}
}
internal class SongMap
{
	public string clipName;

	public float offset;

	public float bpm;

	private static readonly int SECONDS_PER_MIN = 60;

	public Dictionary<string, bool[]> beats;

	public Dictionary<string, int[]> leftEnabled;

	public Dictionary<string, int[]> rightEnabled;

	public float Crotchet => (float)SECONDS_PER_MIN / bpm;

	public SongMap(string clipName, Dictionary<string, bool[]> beats, float offset, float bpm)
	{
		this.clipName = clipName;
		this.beats = beats;
		this.offset = offset;
		this.bpm = bpm;
		if (!beatLengthsMatch(beats))
		{
			Debug.LogWarning((object)("Beat maps on " + clipName + " mapping are different lengths! This may cause unexpected results"));
		}
		leftEnabled = getLeftEnabled();
		rightEnabled = getRightEnabled();
	}

	private bool beatLengthsMatch(Dictionary<string, bool[]> beats)
	{
		int num = beats.Values.First().Length;
		foreach (KeyValuePair<string, bool[]> beat in this.beats)
		{
			if (beat.Value.Length != num)
			{
				return false;
			}
		}
		return true;
	}

	private Dictionary<string, int[]> getLeftEnabled()
	{
		Dictionary<string, int[]> dictionary = new Dictionary<string, int[]>();
		foreach (KeyValuePair<string, bool[]> beat in beats)
		{
			List<int> list = new List<int>();
			int item = 0;
			for (int i = 0; i < beat.Value.Length; i++)
			{
				if (beat.Value[i])
				{
					item = i;
				}
				list.Add(item);
			}
			dictionary[beat.Key] = list.ToArray();
		}
		return dictionary;
	}

	private Dictionary<string, int[]> getRightEnabled()
	{
		Dictionary<string, int[]> dictionary = new Dictionary<string, int[]>();
		foreach (KeyValuePair<string, bool[]> beat in beats)
		{
			List<int> list = new List<int>();
			int item = beat.Value.Length;
			for (int num = beat.Value.Length - 1; num >= 0; num--)
			{
				if (beat.Value[num])
				{
					item = num;
				}
				list.Add(item);
			}
			list.Reverse();
			dictionary[beat.Key] = list.ToArray();
		}
		return dictionary;
	}
}
namespace UltraBeat
{
	public enum PauseLength
	{
		None,
		Mini,
		Short,
		Long
	}
	[BepInPlugin("UltraBeat.recessive.ultrakill", "UltraBeat", "0.0.1")]
	public class Class1 : BaseUnityPlugin
	{
		[HarmonyPatch]
		public class Patch
		{
			[HarmonyPrefix]
			[HarmonyPatch(typeof(TimeController), "HitStop")]
			public static bool HitStopPrefix(float length)
			{
				return true;
			}

			[HarmonyPrefix]
			[HarmonyPatch(typeof(TimeController), "TrueStop")]
			public static bool Prefix(float length, TimeController __instance, float ___currentStop, AudioMixer[] ___audmix)
			{
				if (!Instance.conductor.active)
				{
					return true;
				}
				if (!(length > ___currentStop))
				{
					return false;
				}
				___currentStop = length;
				if (__instance.controlPitch)
				{
					for (int i = 0; i < ___audmix.Length; i++)
					{
						___audmix[i].SetFloat("lowPassVolume", 0f);
					}
				}
				Time.timeScale = 0f;
				if (length > 0.25f)
				{
					Instance.timePause = PauseLength.Long;
				}
				else
				{
					Instance.timePause = PauseLength.Short;
				}
				return false;
			}

			private static IEnumerator TimeIsStopped(float length, bool trueStop, TimeController __instance, float ___currentStop, AudioMixer[] ___audmix)
			{
				yield return (object)new WaitForSecondsRealtime(length);
				ContinueTime(length, trueStop, __instance, ___currentStop, ___audmix);
			}

			private static void ContinueTime(float length, bool trueStop, TimeController __instance, float ___currentStop, AudioMixer[] ___audmix)
			{
				if (!(length >= ___currentStop))
				{
					return;
				}
				Time.timeScale = __instance.timeScale * __instance.timeScaleModifier;
				if (trueStop && __instance.controlPitch)
				{
					for (int i = 0; i < ___audmix.Length; i++)
					{
						___audmix[i].SetFloat("lowPassVolume", -80f);
					}
				}
				___currentStop = 0f;
			}
		}

		public ConductorScript conductor;

		public PauseLength timePause = PauseLength.None;

		private AudioMixer[] audmix;

		private AudioSource source;

		private bool lastCheck = true;

		public static Class1 Instance { get; private set; }

		private void Awake()
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Expected O, but got Unknown
			Instance = this;
			Harmony val = new Harmony("UltraBeat.recessive.ultrakill");
			val.PatchAll();
			SceneManager.sceneLoaded += SceneLoaded;
			string text = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "BepInEx", "config", "UltraBeat");
			if (!Directory.Exists(text))
			{
				DownloadMaps(text, ((BaseUnityPlugin)this).Logger, InitConductor);
			}
			else
			{
				InitConductor(text);
			}
		}

		private void InitConductor(string mapDirectory)
		{
			conductor = new ConductorScript(mapDirectory, ((BaseUnityPlugin)this).Logger);
			conductor.onBeat += beat;
		}

		private static async Task DownloadMaps(string mapDirectory, ManualLogSource Logger, Action<string> callback)
		{
			Logger.LogWarning((object)"Expected map directory not found!");
			Logger.LogMessage((object)("Creating map directory: " + mapDirectory));
			Directory.CreateDirectory(mapDirectory);
			string mapUrl = "https://api.github.com/repos/Recessive/UltraBeat/contents/maps";
			Logger.LogMessage((object)"Downloading beatmaps from https://github.com/Recessive/UltraBeat/tree/master/maps");
			HttpClient client = new HttpClient
			{
				DefaultRequestHeaders = { { "User-Agent", "C# App" } }
			};
			try
			{
				JArray files = JArray.Parse(await client.GetStringAsync(mapUrl));
				foreach (JToken file in files)
				{
					if (((object)file[(object)"type"]).ToString() == "file")
					{
						string fileName = ((object)file[(object)"name"]).ToString();
						string downloadUrl = ((object)file[(object)"download_url"]).ToString();
						string outputPath = Path.Combine(mapDirectory, fileName);
						await File.WriteAllBytesAsync(outputPath, await client.GetByteArrayAsync(downloadUrl));
						Logger.LogMessage((object)("Downloaded: " + fileName));
					}
				}
			}
			catch (Exception ex2)
			{
				Exception ex = ex2;
				Logger.LogError((object)("Error: " + ex.Message));
			}
			Logger.LogMessage((object)"Successfully downloaded all maps!");
			callback(mapDirectory);
		}

		private void Update()
		{
			if (conductor != null)
			{
				bool flag = CheckSource();
				if (flag && !lastCheck)
				{
					conductor.NewSource(source);
				}
				if (!flag && lastCheck)
				{
					conductor.Pause();
				}
				else
				{
					conductor.Update();
				}
				lastCheck = flag;
			}
		}

		private void beat(int beat, Dictionary<string, bool> enabled)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected O, but got Unknown
			if (enabled["fast"])
			{
				Revolver val = (Revolver)Object.FindObjectOfType(typeof(Revolver));
				if ((Object)(object)val != (Object)null && !val.altVersion)
				{
					FieldInfo fieldInfo = AccessTools.Field(typeof(Revolver), "shootReady");
					FieldInfo fieldInfo2 = AccessTools.Field(typeof(Revolver), "gunReady");
					fieldInfo.SetValue(val, true);
					fieldInfo2.SetValue(val, true);
				}
			}
			if (enabled["fast"] && timePause == PauseLength.Mini)
			{
				Unfreeze();
				timePause = PauseLength.None;
			}
			if (enabled["freeze_short"] && timePause == PauseLength.Short)
			{
				BigUnfreeze();
				timePause = PauseLength.None;
			}
			if (enabled["freeze_long"] && timePause == PauseLength.Long)
			{
				BigUnfreeze();
				timePause = PauseLength.None;
			}
		}

		private void BigUnfreeze()
		{
			AudioMixer[] array = (AudioMixer[])(object)new AudioMixer[4]
			{
				MonoSingleton<AudioMixerController>.Instance.allSound,
				MonoSingleton<AudioMixerController>.Instance.goreSound,
				MonoSingleton<AudioMixerController>.Instance.musicSound,
				MonoSingleton<AudioMixerController>.Instance.doorSound
			};
			for (int i = 0; i < array.Length; i++)
			{
				array[i].SetFloat("lowPassVolume", -80f);
			}
			Unfreeze();
		}

		private void Unfreeze()
		{
			Time.timeScale = MonoSingleton<TimeController>.Instance.timeScale * MonoSingleton<TimeController>.Instance.timeScaleModifier;
		}

		private bool CheckSource()
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			MusicManager instance = MonoSingleton<MusicManager>.Instance;
			if ((Object)(object)instance != (Object)null && (Object)(object)instance.targetTheme.clip != (Object)null)
			{
				source = instance.targetTheme;
				return true;
			}
			CustomMusicPlayer val = (CustomMusicPlayer)Object.FindObjectOfType(typeof(CustomMusicPlayer));
			if ((Object)(object)val != (Object)null && (Object)(object)val.source.clip != (Object)null)
			{
				source = val.source;
				return true;
			}
			source = null;
			return false;
		}

		private void SceneLoaded(Scene scene, LoadSceneMode loadMode)
		{
		}
	}
}