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