Decompiled source of Boss VS Intro v1.3.0

BossVSIntro.dll

Decompiled 3 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("BossVSIntro")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("Boss VS Intro")]
[assembly: AssemblyTitle("BossVSIntro")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace BossVSIntro
{
	[BepInPlugin("com.scout.bossvsintro", "Boss VS Intro", "1.3.0")]
	public class BossVSIntroPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.scout.bossvsintro";

		public const string PluginName = "Boss VS Intro";

		public const string PluginVersion = "1.3.0";

		internal static BossVSIntroPlugin Instance;

		internal static ManualLogSource Log;

		internal static string ImagesRoot;

		internal static string SoundsRoot;

		internal static string TextRoot;

		internal static string PluginDir;

		private void Awake()
		{
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			PluginDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
			ImagesRoot = Path.Combine(PluginDir, "Images");
			SoundsRoot = Path.Combine(PluginDir, "Sounds");
			TextRoot = Path.Combine(PluginDir, "Text");
			new Harmony("com.scout.bossvsintro").PatchAll();
			Log.LogInfo((object)"Boss VS Intro v1.3.0 loaded.");
			SceneManager.sceneLoaded += delegate
			{
				BossVSIntroPatch.ClearOutroWatch();
			};
			BossVSIntroPatch.BeginPreloadSounds();
		}

		private void Update()
		{
			BossVSIntroPatch.EnsureRunner();
		}
	}
	internal static class VSConfig
	{
		public static float introCooldown = 5f;

		public static float slideInDuration = 0.45f;

		public static float vsPopDuration = 0.35f;

		public static float vsStartScale = 3.5f;

		public static float vsHeightFraction = 0.35f;

		public static float shakeDuration = 0.4f;

		public static float colorFadeDuration = 0.35f;

		public static float shakeAmplitude = 38f;

		public static float holdDuration = 1f;

		public static float slideOutDuration = 0.4f;

		public static float centerGap = -1920f;

		public static string triggerSoundFile = "Trigger.wav";

		public static float soundVolume = 1f;

		public static float vsDelay = 1f;

		public static string playerFileName = "Player.png";

		public static string vsFileName = "VS.png";

		public static string bossFolderName = "Bosses";

		public static string bossFallbackName = "Default.png";

		public static string backgroundFolderName = "Backgrounds";

		public static string backgroundFallbackName = "Default.png";

		public static string backgroundPlayerFolder = "Player";

		public static string backgroundBossesFolder = "Bosses";

		public static string playerTextFileName = "Player.txt";

		public static string textFallbackName = "Default.txt";

		public static float textFontSize = 72f;

		public static float textYFraction = -0.32f;

		public static float textSlideDuration = 0.35f;

		public static float textOutlineWidth = 0.1f;

		public static float positionTolerance = 6f;

		public static float barVisibleTimeout = 5f;

		public static string crossOutFileName = "CROSSEDOUT.png";

		public static string iconFolderName = "Icons";

		public static float iconHeightFraction = 1f;

		public static float crossOutHeightFraction = 0.5f;

		public static float outroCrossDelay = 0.5f;

		public static float outroHoldDuration = 1f;

		public static string ultrakilledTextFileName = "ULTRAKILLED.txt";

		public static string objectDeathBosses = "1000_THR_DEFENCE_SYSTEM";

		public static string earthmoverBossName = "1000_THR_EARTHMOVER";

		public static string earthmoverStyleName = "RAISON D'ETRE";

		public static bool logOutroWatch = false;

		public static string audioTriggerOutros = "LEVEL_4_4|V2|Versus2Outro";

		public static float outroTriggerDelay = 1f;

		public static float outroBossTextX = 0f;

		public static float outroBossTextY = -256f;

		public static string outroSoundFile = "Outro.wav";
	}
	public class VSRunner : MonoBehaviour
	{
		private void Update()
		{
			BossVSIntroPatch.CheckOutroWatch();
			BossVSIntroPatch.CheckAudioTrigger();
		}
	}
	public class RectFollower : MonoBehaviour
	{
		public RectTransform target;

		private RectTransform self;

		private void Awake()
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Expected O, but got Unknown
			self = (RectTransform)((Component)this).transform;
		}

		private void LateUpdate()
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)target != (Object)null && (Object)(object)self != (Object)null)
			{
				self.anchoredPosition = target.anchoredPosition;
			}
		}
	}
	[HarmonyPatch(typeof(BossHealthBarTemplate))]
	public class BossVSIntroPatch
	{
		private class WatchedBoss
		{
			public Component eid;

			public BossHealthBar bar;

			public string bossName;

			public Vector3 bossPos;

			public string level;

			public bool objectDeath;
		}

		private class TextSpec
		{
			public string title;

			public bool hasPos;

			public Vector2 pos;

			public bool hasSize;

			public float size;

			public bool hasColor;

			public Color color;
		}

		private static bool introPlaying;

		private static float lastIntroTime = -9999f;

		private static readonly List<WatchedBoss> watched = new List<WatchedBoss>();

		private static bool outroPlaying;

		private static Type eidType;

		private static FieldInfo eidDeadField;

		private static bool eidWarned;

		private static bool frozeForCard;

		private static float frozeAt;

		private static float freezeCheckNext;

		private static bool earthmoverArmed;

		private static string earthmoverName;

		private static Vector3 earthmoverPos;

		private static bool audioArmed;

		private static string audioBossName;

		private static Vector3 audioBossPos;

		private static string audioClip;

		private static Canvas introCanvas;

		private static AudioSource introAudio;

		private static readonly Dictionary<string, Sprite> spriteCache = new Dictionary<string, Sprite>();

		private static AudioClip triggerClip;

		private static AudioClip outroClip;

		private static MonoBehaviour runner;

		private static float audioNextScan;

		private static Type sceneHelperType;

		private static readonly Dictionary<string, List<(int idx, Vector3 pos)>> EncounterMap = BuildEncounterMap();

		private static TMP_FontAsset cachedFont;

		private static readonly Dictionary<Sprite, Sprite> whiteCache = new Dictionary<Sprite, Sprite>();

		private static MonoBehaviour Runner
		{
			get
			{
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				//IL_001c: Expected O, but got Unknown
				if ((Object)(object)runner == (Object)null)
				{
					GameObject val = new GameObject("BossVSIntroRunner");
					Object.DontDestroyOnLoad((Object)(object)val);
					runner = (MonoBehaviour)(object)val.AddComponent<VSRunner>();
				}
				return runner;
			}
		}

		public static void EnsureRunner()
		{
			MonoBehaviour val = Runner;
		}

		[HarmonyPostfix]
		[HarmonyPatch("Initialize", new Type[]
		{
			typeof(BossHealthBar),
			typeof(SliderLayer[])
		})]
		private static void InitializePostfix(BossHealthBarTemplate __instance, BossHealthBar bossBar, SliderLayer[] colorLayers)
		{
			if (!((Object)(object)__instance == (Object)null) && !((Object)(object)bossBar == (Object)null) && !string.IsNullOrEmpty(bossBar.bossName))
			{
				EnsureRunner();
				Runner.StartCoroutine(WaitForBarThenTrigger(__instance, bossBar, bossBar.bossName));
			}
		}

		private static IEnumerator WaitForBarThenTrigger(BossHealthBarTemplate template, BossHealthBar bossBar, string bossName)
		{
			if ((Object)(object)template == (Object)null)
			{
				yield break;
			}
			float deadline = Time.unscaledTime + VSConfig.barVisibleTimeout;
			while (Time.unscaledTime < deadline)
			{
				if ((Object)(object)template == (Object)null)
				{
					yield break;
				}
				if (((Component)template).gameObject.activeInHierarchy)
				{
					break;
				}
				yield return null;
			}
			if ((Object)(object)template == (Object)null || !((Component)template).gameObject.activeInHierarchy)
			{
				yield break;
			}
			while (outroPlaying)
			{
				yield return null;
			}
			if (!introPlaying && !(Time.unscaledTime - lastIntroTime < VSConfig.introCooldown))
			{
				Vector3 bossPos = (((Object)(object)bossBar != (Object)null) ? ((Component)bossBar).transform.position : ((Component)template).transform.position);
				if ((Object)(object)bossBar != (Object)null)
				{
					ArmOutroWatch(bossBar, bossName, bossPos);
				}
				Runner.StartCoroutine(PlayVSIntro(bossName, bossPos));
			}
		}

		private static void ArmOutroWatch(BossHealthBar bossBar, string bossName, Vector3 bossPos)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_024b: Unknown result type (might be due to invalid IL or missing references)
			//IL_024c: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (Sanitize(bossName) == Sanitize(VSConfig.earthmoverBossName))
				{
					earthmoverArmed = true;
					earthmoverName = bossName;
					earthmoverPos = bossPos;
					if (VSConfig.logOutroWatch)
					{
						BossVSIntroPlugin.Log.LogInfo((object)("[OutroWatch] armed '" + bossName + "' (earthmover / style bonus)"));
					}
					return;
				}
				string text = AudioTriggerClipFor(Sanitize(CurrentLevelName()), Sanitize(bossName));
				if (text != null)
				{
					audioArmed = true;
					audioBossName = bossName;
					audioBossPos = bossPos;
					audioClip = text;
					if (VSConfig.logOutroWatch)
					{
						BossVSIntroPlugin.Log.LogInfo((object)("[OutroWatch] armed '" + bossName + "' (sound '" + text + "')"));
					}
					return;
				}
				if (eidType == null)
				{
					eidType = AccessTools.TypeByName("EnemyIdentifier");
				}
				if (eidType != null && eidDeadField == null)
				{
					eidDeadField = eidType.GetField("dead", BindingFlags.Instance | BindingFlags.Public);
				}
				if (eidType == null || eidDeadField == null)
				{
					if (!eidWarned)
					{
						eidWarned = true;
						BossVSIntroPlugin.Log.LogWarning((object)"Couldn't find EnemyIdentifier.dead");
					}
					return;
				}
				Component val = ((Component)bossBar).GetComponentInParent(eidType);
				if ((Object)(object)val == (Object)null)
				{
					val = ((Component)bossBar).GetComponentInChildren(eidType);
				}
				if ((Object)(object)val == (Object)null)
				{
					if (VSConfig.logOutroWatch)
					{
						BossVSIntroPlugin.Log.LogWarning((object)("[OutroWatch] '" + bossName + "' has NO EnemyIdentifier"));
					}
					return;
				}
				foreach (WatchedBoss item in watched)
				{
					if ((Object)(object)item.eid == (Object)(object)val)
					{
						return;
					}
				}
				bool flag = IsObjectDeathBoss(bossName);
				watched.Add(new WatchedBoss
				{
					eid = val,
					bar = bossBar,
					bossName = bossName,
					bossPos = bossPos,
					level = Sanitize(CurrentLevelName()),
					objectDeath = flag
				});
				if (VSConfig.logOutroWatch)
				{
					BossVSIntroPlugin.Log.LogInfo((object)string.Format("[OutroWatch] armed '{0}' (watched, death signal: {1}). count={2}", bossName, flag ? "bar gone" : "dead flag", watched.Count));
				}
			}
			catch (Exception ex)
			{
				BossVSIntroPlugin.Log.LogError((object)("Outro watch failed to arm: " + ex.Message));
			}
		}

		private static bool IsObjectDeathBoss(string bossName)
		{
			string text = Sanitize(bossName);
			string[] array = VSConfig.objectDeathBosses.Split(new char[1] { ',' });
			foreach (string text2 in array)
			{
				if (Sanitize(text2.Trim()) == text)
				{
					return true;
				}
			}
			return false;
		}

		public static void ClearOutroWatch()
		{
			watched.Clear();
			earthmoverArmed = false;
			audioArmed = false;
		}

		private static string AudioTriggerClipFor(string levelStem, string bossStem)
		{
			string[] array = VSConfig.audioTriggerOutros.Split(new char[1] { ',' });
			foreach (string text in array)
			{
				string[] array2 = text.Split(new char[1] { '|' });
				if (array2.Length == 3 && Sanitize(array2[0].Trim()) == levelStem && Sanitize(array2[1].Trim()) == bossStem)
				{
					return array2[2].Trim();
				}
			}
			return null;
		}

		public static void CheckAudioTrigger()
		{
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			if (!audioArmed || Time.unscaledTime < audioNextScan)
			{
				return;
			}
			audioNextScan = Time.unscaledTime + 0.1f;
			AudioSource[] array = Object.FindObjectsOfType<AudioSource>();
			AudioSource[] array2 = array;
			foreach (AudioSource val in array2)
			{
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val.clip == (Object)null) && val.isPlaying && ((Object)val.clip).name == audioClip)
				{
					if (VSConfig.logOutroWatch)
					{
						BossVSIntroPlugin.Log.LogInfo((object)("[OutroWatch] '" + audioBossName + "' sound '" + audioClip + "' started -> playing card"));
					}
					audioArmed = false;
					Runner.StartCoroutine(OutroAfterDelay(audioBossName, audioBossPos));
					break;
				}
			}
		}

		public static void OnStyleBonus(string styleName)
		{
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			if (earthmoverArmed && !string.IsNullOrEmpty(styleName) && !(styleName != VSConfig.earthmoverStyleName))
			{
				earthmoverArmed = false;
				if (VSConfig.logOutroWatch)
				{
					BossVSIntroPlugin.Log.LogInfo((object)("[OutroWatch] '" + earthmoverName + "' style bonus -> playing card"));
				}
				Runner.StartCoroutine(OutroAfterDelay(earthmoverName, earthmoverPos));
			}
		}

		public static void CheckOutroWatch()
		{
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_0272: Unknown result type (might be due to invalid IL or missing references)
			if (watched.Count == 0)
			{
				return;
			}
			string text = Sanitize(CurrentLevelName());
			for (int num = watched.Count - 1; num >= 0; num--)
			{
				WatchedBoss watchedBoss = watched[num];
				if (watchedBoss.level != text)
				{
					if (VSConfig.logOutroWatch)
					{
						BossVSIntroPlugin.Log.LogInfo((object)("[OutroWatch] dropped '" + watchedBoss.bossName + "' (level '" + watchedBoss.level + "' != '" + text + "')"));
					}
					watched.RemoveAt(num);
					continue;
				}
				if (watchedBoss.objectDeath)
				{
					if ((Object)(object)watchedBoss.bar == (Object)null || !((Component)watchedBoss.bar).gameObject.activeInHierarchy)
					{
						if (VSConfig.logOutroWatch)
						{
							BossVSIntroPlugin.Log.LogInfo((object)("[OutroWatch] '" + watchedBoss.bossName + "' bar gone -> playing card"));
						}
						watched.RemoveAt(num);
						Runner.StartCoroutine(OutroAfterDelay(watchedBoss.bossName, watchedBoss.bossPos));
					}
					continue;
				}
				if ((Object)(object)watchedBoss.eid == (Object)null)
				{
					if (VSConfig.logOutroWatch)
					{
						BossVSIntroPlugin.Log.LogInfo((object)("[OutroWatch] dropped '" + watchedBoss.bossName + "' (eid null)"));
					}
					watched.RemoveAt(num);
					continue;
				}
				bool flag;
				try
				{
					flag = (bool)eidDeadField.GetValue(watchedBoss.eid);
				}
				catch (Exception ex)
				{
					if (VSConfig.logOutroWatch)
					{
						BossVSIntroPlugin.Log.LogWarning((object)("[OutroWatch] '" + watchedBoss.bossName + "' dead-read threw (" + ex.Message + ") — dropped"));
					}
					watched.RemoveAt(num);
					continue;
				}
				if (flag)
				{
					if (VSConfig.logOutroWatch)
					{
						BossVSIntroPlugin.Log.LogInfo((object)("[OutroWatch] '" + watchedBoss.bossName + "' read dead=true -> playing card"));
					}
					watched.RemoveAt(num);
					Runner.StartCoroutine(OutroAfterDelay(watchedBoss.bossName, watchedBoss.bossPos));
				}
			}
		}

		private static IEnumerator OutroAfterDelay(string bossName, Vector3 bossPos)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			float t = 0f;
			while (t < VSConfig.outroTriggerDelay)
			{
				t += Time.unscaledDeltaTime;
				yield return null;
			}
			while (introPlaying || outroPlaying)
			{
				yield return null;
			}
			outroPlaying = true;
			yield return PlayOutro(bossName, bossPos);
		}

		public static void BeginPreloadSounds()
		{
			Runner.StartCoroutine(PreloadSounds());
		}

		private static IEnumerator PreloadSounds()
		{
			yield return LoadClip(Path.Combine(BossVSIntroPlugin.SoundsRoot, VSConfig.triggerSoundFile), delegate(AudioClip c)
			{
				triggerClip = c;
			});
			yield return LoadClip(Path.Combine(BossVSIntroPlugin.SoundsRoot, VSConfig.outroSoundFile), delegate(AudioClip c)
			{
				outroClip = c;
			});
		}

		private static IEnumerator LoadClip(string fullPath, Action<AudioClip> onLoaded)
		{
			if (!File.Exists(fullPath))
			{
				BossVSIntroPlugin.Log.LogWarning((object)("Sound not found (splash will be silent for this one): " + fullPath));
				onLoaded(null);
				yield break;
			}
			AudioType type = GuessAudioType(fullPath);
			UnityWebRequest req = UnityWebRequestMultimedia.GetAudioClip("file://" + fullPath, type);
			try
			{
				yield return req.SendWebRequest();
				if ((int)req.result != 1)
				{
					BossVSIntroPlugin.Log.LogError((object)("Failed to load sound '" + fullPath + "': " + req.error));
					onLoaded(null);
					yield break;
				}
				onLoaded(DownloadHandlerAudioClip.GetContent(req));
			}
			finally
			{
				((IDisposable)req)?.Dispose();
			}
		}

		private static AudioType GuessAudioType(string path)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			string text = Path.GetExtension(path).ToLowerInvariant();
			string text2 = text;
			if (!(text2 == ".ogg"))
			{
				if (text2 == ".mp3")
				{
					return (AudioType)13;
				}
				return (AudioType)20;
			}
			return (AudioType)14;
		}

		private static void PlaySound(AudioClip clip)
		{
			if (!((Object)(object)clip == (Object)null) && !((Object)(object)introAudio == (Object)null))
			{
				introAudio.PlayOneShot(clip, VSConfig.soundVolume);
			}
		}

		private static string Sanitize(string name)
		{
			StringBuilder stringBuilder = new StringBuilder();
			string text = name.ToUpperInvariant();
			foreach (char c in text)
			{
				if (char.IsLetterOrDigit(c))
				{
					stringBuilder.Append(c);
				}
				else if (c == ' ' || c == '-' || c == '_')
				{
					stringBuilder.Append('_');
				}
			}
			return stringBuilder.ToString();
		}

		public static string CurrentLevelName()
		{
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (sceneHelperType == null)
				{
					sceneHelperType = AccessTools.TypeByName("SceneHelper");
				}
				if (sceneHelperType != null)
				{
					PropertyInfo property = sceneHelperType.GetProperty("CurrentScene", BindingFlags.Static | BindingFlags.Public);
					if (property != null && property.GetValue(null) is string text && !string.IsNullOrEmpty(text))
					{
						return text;
					}
					FieldInfo field = sceneHelperType.GetField("CurrentScene", BindingFlags.Static | BindingFlags.Public);
					if (field != null && field.GetValue(null) is string text2 && !string.IsNullOrEmpty(text2))
					{
						return text2;
					}
				}
			}
			catch
			{
			}
			Scene activeScene = SceneManager.GetActiveScene();
			return ((Scene)(ref activeScene)).name;
		}

		private static Sprite ResolveBackgroundSprite(string sideFolder, string bossName, Vector3 bossPos)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			string text = Sanitize(bossName);
			string text2 = Sanitize(CurrentLevelName());
			string path = Path.Combine(BossVSIntroPlugin.ImagesRoot, VSConfig.backgroundFolderName, sideFolder);
			int num = EncounterIndex(text2, text, bossPos);
			List<string> list = new List<string>();
			if (num >= 2)
			{
				list.Add(Path.Combine(path, text2, $"{text}_{num}.png"));
				list.Add(Path.Combine(path, $"{text}_{num}.png"));
			}
			list.Add(Path.Combine(path, text2, text + ".png"));
			list.Add(Path.Combine(path, text + ".png"));
			list.Add(Path.Combine(path, text2 + ".png"));
			list.Add(Path.Combine(path, text2, VSConfig.backgroundFallbackName));
			list.Add(Path.Combine(path, VSConfig.backgroundFallbackName));
			foreach (string item in list)
			{
				Sprite val = LoadSprite(item);
				if ((Object)(object)val != (Object)null)
				{
					return val;
				}
			}
			return null;
		}

		private static Sprite ResolveBossSprite(string bossName, Vector3 bossPos)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			string text = Sanitize(bossName);
			string text2 = Sanitize(CurrentLevelName());
			string path = Path.Combine(BossVSIntroPlugin.ImagesRoot, VSConfig.bossFolderName);
			int num = EncounterIndex(text2, text, bossPos);
			List<string> list = new List<string>();
			if (num >= 2)
			{
				list.Add(Path.Combine(path, text2, $"{text}_{num}.png"));
				list.Add(Path.Combine(path, $"{text}_{num}.png"));
			}
			list.Add(Path.Combine(path, text2, text + ".png"));
			list.Add(Path.Combine(path, text + ".png"));
			list.Add(Path.Combine(path, text2, VSConfig.bossFallbackName));
			list.Add(Path.Combine(path, VSConfig.bossFallbackName));
			foreach (string item in list)
			{
				Sprite val = LoadSprite(item);
				if ((Object)(object)val != (Object)null)
				{
					return val;
				}
			}
			return null;
		}

		private static Dictionary<string, List<(int, Vector3)>> BuildEncounterMap()
		{
			Dictionary<string, List<(int, Vector3)>> dictionary = new Dictionary<string, List<(int, Vector3)>>();
			Add(dictionary, "LEVEL_0_5", "CERBERUS_GUARDIAN_OF_HELL", 1, 207.5f, -6.5f, 394.5f);
			Add(dictionary, "LEVEL_0_5", "CERBERUS_GUARDIAN_OF_HELL", 2, 207.5f, -6.5f, 369.5f);
			Add(dictionary, "LEVEL_1_E", "HIDEOUS_MASS", 1, 280.25f, 1.5f, 450.75f);
			Add(dictionary, "LEVEL_1_E", "HIDEOUS_MASS", 2, 346f, 31f, 568f);
			Add(dictionary, "LEVEL_4_4", "V2", 1, 117.5f, 661f, 263f);
			Add(dictionary, "LEVEL_4_4", "V2", 2, 200f, 610.5f, 1150f);
			Add(dictionary, "LEVEL_7_1", "SMINOTAUR", 1, -242.5f, -1f, 60.25f);
			Add(dictionary, "LEVEL_7_1", "SMINOTAUR", 2, -225.5f, 95f, -414.75f);
			return dictionary;
		}

		private static void Add(Dictionary<string, List<(int, Vector3)>> m, string level, string boss, int idx, float x, float y, float z)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			string key = level + "|" + boss;
			if (!m.TryGetValue(key, out var value))
			{
				value = (m[key] = new List<(int, Vector3)>());
			}
			value.Add((idx, new Vector3(x, y, z)));
		}

		private static int EncounterIndex(string levelStem, string bossStem, Vector3 pos)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			string key = levelStem + "|" + bossStem;
			if (!EncounterMap.TryGetValue(key, out List<(int, Vector3)> value))
			{
				return 1;
			}
			int num = -1;
			float num2 = float.MaxValue;
			foreach (var item in value)
			{
				float num3 = Vector3.Distance(pos, item.Item2);
				if (num3 < num2)
				{
					num2 = num3;
					(num, _) = item;
				}
			}
			return (num <= 0 || !(num2 <= VSConfig.positionTolerance)) ? 1 : num;
		}

		private static Sprite LoadSprite(string fullPath)
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Expected O, but got Unknown
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			if (spriteCache.TryGetValue(fullPath, out var value))
			{
				return value;
			}
			if (!File.Exists(fullPath))
			{
				return null;
			}
			try
			{
				byte[] array = File.ReadAllBytes(fullPath);
				Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
				ImageConversion.LoadImage(val, array);
				((Texture)val).filterMode = (FilterMode)1;
				((Texture)val).wrapMode = (TextureWrapMode)1;
				Sprite val2 = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f);
				spriteCache[fullPath] = val2;
				return val2;
			}
			catch (Exception ex)
			{
				BossVSIntroPlugin.Log.LogError((object)("Failed to load image '" + fullPath + "': " + ex.Message));
				return null;
			}
		}

		private static Canvas GetOrCreateCanvas()
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected O, but got Unknown
			if ((Object)(object)introCanvas != (Object)null)
			{
				return introCanvas;
			}
			GameObject val = new GameObject("BossVSIntroCanvas");
			Object.DontDestroyOnLoad((Object)(object)val);
			introCanvas = val.AddComponent<Canvas>();
			introCanvas.renderMode = (RenderMode)0;
			introCanvas.sortingOrder = 32767;
			CanvasScaler val2 = val.AddComponent<CanvasScaler>();
			val2.uiScaleMode = (ScaleMode)0;
			val2.scaleFactor = 1f;
			val.AddComponent<GraphicRaycaster>();
			introAudio = val.AddComponent<AudioSource>();
			introAudio.playOnAwake = false;
			introAudio.spatialBlend = 0f;
			introAudio.ignoreListenerPause = true;
			return introCanvas;
		}

		private static string LoadText(string fullPath)
		{
			try
			{
				if (!File.Exists(fullPath))
				{
					return null;
				}
				string text = File.ReadAllText(fullPath).Trim();
				return (text.Length == 0) ? null : text;
			}
			catch (Exception ex)
			{
				BossVSIntroPlugin.Log.LogError((object)("Failed to read text '" + fullPath + "': " + ex.Message));
				return null;
			}
		}

		private static TextSpec ResolvePlayerText()
		{
			return ParseTextFile(Path.Combine(BossVSIntroPlugin.TextRoot, VSConfig.playerTextFileName));
		}

		private static TextSpec ResolveEnemyText(string bossName, Vector3 bossPos)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			string text = Sanitize(bossName);
			string text2 = Sanitize(CurrentLevelName());
			string textRoot = BossVSIntroPlugin.TextRoot;
			int num = EncounterIndex(text2, text, bossPos);
			List<string> list = new List<string>();
			if (num >= 2)
			{
				list.Add(Path.Combine(textRoot, text2, $"{text}_{num}.txt"));
				list.Add(Path.Combine(textRoot, $"{text}_{num}.txt"));
			}
			list.Add(Path.Combine(textRoot, text2, text + ".txt"));
			list.Add(Path.Combine(textRoot, text + ".txt"));
			list.Add(Path.Combine(textRoot, text2, VSConfig.textFallbackName));
			list.Add(Path.Combine(textRoot, VSConfig.textFallbackName));
			foreach (string item in list)
			{
				TextSpec textSpec = ParseTextFile(item);
				if (textSpec != null)
				{
					return textSpec;
				}
			}
			return null;
		}

		private static TextSpec ParseTextFile(string fullPath)
		{
			//IL_0207: Unknown result type (might be due to invalid IL or missing references)
			//IL_0209: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
			string text = LoadText(fullPath);
			if (text == null)
			{
				return null;
			}
			TextSpec textSpec = new TextSpec();
			string[] array = text.Split(new char[1] { '\n' });
			foreach (string text2 in array)
			{
				string text3 = text2.Trim();
				int num = text3.IndexOf(':');
				if (num < 0)
				{
					continue;
				}
				string text4 = text3.Substring(0, num).Trim().ToLowerInvariant();
				string text5 = text3.Substring(num + 1).Trim().Trim(new char[1] { '"' });
				switch (text4)
				{
				case "title":
					textSpec.title = text5;
					break;
				case "position":
				{
					if (TryVec2(text5, out var v))
					{
						textSpec.pos = v;
						textSpec.hasPos = true;
					}
					break;
				}
				case "font size":
				case "fontsize":
				case "size":
				{
					if (float.TryParse(text5, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
					{
						textSpec.size = result;
						textSpec.hasSize = true;
					}
					break;
				}
				case "color":
				case "colour":
				{
					if (TryColor(text5, out var c))
					{
						textSpec.color = c;
						textSpec.hasColor = true;
					}
					break;
				}
				}
			}
			return string.IsNullOrEmpty(textSpec.title) ? null : textSpec;
		}

		private static bool TryVec2(string s, out Vector2 v)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			v = Vector2.zero;
			string[] array = s.Split(new char[1] { ',' });
			if (array.Length < 2)
			{
				return false;
			}
			if (float.TryParse(array[0].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && float.TryParse(array[1].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result2))
			{
				v = new Vector2(result, result2);
				return true;
			}
			return false;
		}

		private static bool TryColor(string s, out Color c)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			c = Color.white;
			string[] array = s.Split(new char[1] { ',' });
			if (array.Length < 3)
			{
				return false;
			}
			if (!byte.TryParse(array[0].Trim(), out var result) || !byte.TryParse(array[1].Trim(), out var result2) || !byte.TryParse(array[2].Trim(), out var result3))
			{
				return false;
			}
			byte result4 = byte.MaxValue;
			if (array.Length >= 4)
			{
				byte.TryParse(array[3].Trim(), out result4);
			}
			c = Color32.op_Implicit(new Color32(result, result2, result3, result4));
			return true;
		}

		private static TMP_FontAsset GetFont()
		{
			if ((Object)(object)cachedFont != (Object)null)
			{
				return cachedFont;
			}
			TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll<TMP_FontAsset>();
			if (array != null)
			{
				TMP_FontAsset[] array2 = array;
				foreach (TMP_FontAsset val in array2)
				{
					if ((Object)(object)val != (Object)null && ((Object)val).name.IndexOf("VCR", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						return cachedFont = val;
					}
				}
			}
			if ((Object)(object)TMP_Settings.defaultFontAsset != (Object)null)
			{
				return cachedFont = TMP_Settings.defaultFontAsset;
			}
			if (array != null && array.Length != 0)
			{
				cachedFont = array[0];
			}
			return cachedFont;
		}

		private static TextMeshProUGUI MakeText(Transform parent, string content, string name, float fontSize, Color color)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Expected O, but got Unknown
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name);
			val.transform.SetParent(parent, false);
			TextMeshProUGUI val2 = val.AddComponent<TextMeshProUGUI>();
			TMP_FontAsset font = GetFont();
			if ((Object)(object)font != (Object)null)
			{
				((TMP_Text)val2).font = font;
			}
			((TMP_Text)val2).text = content;
			((TMP_Text)val2).fontSize = fontSize;
			((TMP_Text)val2).fontStyle = (FontStyles)1;
			((TMP_Text)val2).alignment = (TextAlignmentOptions)514;
			((TMP_Text)val2).enableWordWrapping = false;
			((TMP_Text)val2).overflowMode = (TextOverflowModes)0;
			((Graphic)val2).color = color;
			((Graphic)val2).raycastTarget = false;
			if (VSConfig.textOutlineWidth > 0f)
			{
				Material fontMaterial = ((TMP_Text)val2).fontMaterial;
				fontMaterial.SetColor(ShaderUtilities.ID_OutlineColor, Color.black);
				fontMaterial.SetFloat(ShaderUtilities.ID_OutlineWidth, VSConfig.textOutlineWidth);
			}
			RectTransform rectTransform = ((TMP_Text)val2).rectTransform;
			Vector2 val3 = default(Vector2);
			((Vector2)(ref val3))..ctor(0.5f, 0.5f);
			rectTransform.pivot = val3;
			Vector2 anchorMin = (rectTransform.anchorMax = val3);
			rectTransform.anchorMin = anchorMin;
			rectTransform.sizeDelta = new Vector2(fontSize * 12f, fontSize * 2f);
			return val2;
		}

		private static Sprite WhiteSilhouette(Sprite src)
		{
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Expected O, but got Unknown
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)src == (Object)null)
			{
				return null;
			}
			if (whiteCache.TryGetValue(src, out var value))
			{
				return value;
			}
			Texture2D texture = src.texture;
			Color32[] pixels = texture.GetPixels32();
			for (int i = 0; i < pixels.Length; i++)
			{
				pixels[i] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, pixels[i].a);
			}
			Texture2D val = new Texture2D(((Texture)texture).width, ((Texture)texture).height, (TextureFormat)4, false);
			val.SetPixels32(pixels);
			val.Apply();
			((Texture)val).filterMode = (FilterMode)1;
			((Texture)val).wrapMode = (TextureWrapMode)1;
			Sprite val2 = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f);
			whiteCache[src] = val2;
			return val2;
		}

		private static Image MakeFlash(Image fighter, Sprite fighterSprite)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("VS_Flash");
			val.transform.SetParent(((Component)fighter).transform, false);
			Image val2 = val.AddComponent<Image>();
			val2.sprite = WhiteSilhouette(fighterSprite);
			((Graphic)val2).raycastTarget = false;
			val2.preserveAspect = true;
			RectTransform rectTransform = ((Graphic)val2).rectTransform;
			rectTransform.anchorMin = Vector2.zero;
			rectTransform.anchorMax = Vector2.one;
			Vector2 offsetMin = (rectTransform.offsetMax = Vector2.zero);
			rectTransform.offsetMin = offsetMin;
			SetFlashAlpha(val2, 0f);
			return val2;
		}

		private static void SetFlashAlpha(Image flash, float a)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)flash == (Object)null))
			{
				((Graphic)flash).color = new Color(1f, 1f, 1f, a);
			}
		}

		private static Sprite ResolveIconSprite(string bossName, Vector3 bossPos)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			string text = Sanitize(bossName);
			string text2 = Sanitize(CurrentLevelName());
			string path = Path.Combine(BossVSIntroPlugin.ImagesRoot, VSConfig.iconFolderName);
			int num = EncounterIndex(text2, text, bossPos);
			List<string> list = new List<string>();
			if (num >= 2)
			{
				list.Add(Path.Combine(path, text2, $"{text}_{num}.png"));
				list.Add(Path.Combine(path, $"{text}_{num}.png"));
			}
			list.Add(Path.Combine(path, text2, text + ".png"));
			list.Add(Path.Combine(path, text + ".png"));
			list.Add(Path.Combine(path, text2, VSConfig.bossFallbackName));
			list.Add(Path.Combine(path, VSConfig.bossFallbackName));
			foreach (string item in list)
			{
				Sprite val = LoadSprite(item);
				if ((Object)(object)val != (Object)null)
				{
					return val;
				}
			}
			return null;
		}

		private static Vector2 ShakeOffset(float since)
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			if (since < 0f || since >= VSConfig.shakeDuration)
			{
				return Vector2.zero;
			}
			float num = VSConfig.shakeAmplitude * (1f - since / VSConfig.shakeDuration);
			return new Vector2(Random.Range(0f - num, num), Random.Range(0f - num, num));
		}

		private static IEnumerator PlayOutro(string bossName, Vector3 bossPos)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			outroPlaying = true;
			Sprite iconSprite = ResolveIconSprite(bossName, bossPos);
			Sprite crossSprite = LoadSprite(Path.Combine(BossVSIntroPlugin.ImagesRoot, VSConfig.crossOutFileName));
			TextSpec bossSpec = ResolveEnemyText(bossName, bossPos);
			TextSpec ultraSpec = ParseTextFile(Path.Combine(BossVSIntroPlugin.TextRoot, VSConfig.ultrakilledTextFileName));
			if ((Object)(object)iconSprite == (Object)null)
			{
				BossVSIntroPlugin.Log.LogWarning((object)("Missing icon for '" + bossName + "' in Images/" + VSConfig.iconFolderName + "/ — kill card skipped."));
			}
			if ((Object)(object)crossSprite == (Object)null)
			{
				BossVSIntroPlugin.Log.LogWarning((object)("Missing Images/" + VSConfig.crossOutFileName + " — kill card skipped."));
			}
			if ((Object)(object)iconSprite == (Object)null || (Object)(object)crossSprite == (Object)null)
			{
				outroPlaying = false;
				yield break;
			}
			float previousTimeScale = Time.timeScale;
			Time.timeScale = 0f;
			frozeForCard = true;
			frozeAt = Time.unscaledTime;
			Image iconImg = null;
			Image crossImg = null;
			TextMeshProUGUI bossTxt = null;
			TextMeshProUGUI ultraTxt = null;
			IEnumerator anim = AnimateOutro(iconSprite, crossSprite, bossSpec, ultraSpec, delegate(Image i)
			{
				iconImg = i;
			}, delegate(Image i)
			{
				crossImg = i;
			}, delegate(TextMeshProUGUI tx)
			{
				bossTxt = tx;
			}, delegate(TextMeshProUGUI tx)
			{
				ultraTxt = tx;
			});
			while (true)
			{
				Time.timeScale = 0f;
				object current;
				try
				{
					if (!anim.MoveNext())
					{
						break;
					}
					current = anim.Current;
					goto IL_025f;
				}
				catch (Exception ex)
				{
					Exception e = ex;
					BossVSIntroPlugin.Log.LogError((object)$"Kill card threw, bailing safely: {e}");
				}
				break;
				IL_025f:
				yield return current;
			}
			if ((Object)(object)iconImg != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)iconImg).gameObject);
			}
			if ((Object)(object)crossImg != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)crossImg).gameObject);
			}
			if ((Object)(object)bossTxt != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)bossTxt).gameObject);
			}
			if ((Object)(object)ultraTxt != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)ultraTxt).gameObject);
			}
			Time.timeScale = previousTimeScale;
			frozeForCard = false;
			outroPlaying = false;
		}

		private static IEnumerator AnimateOutro(Sprite iconSprite, Sprite crossSprite, TextSpec bossSpec, TextSpec ultraSpec, Action<Image> outIcon, Action<Image> outCross, Action<TextMeshProUGUI> outBossTxt, Action<TextMeshProUGUI> outUltraTxt)
		{
			Canvas canvas = GetOrCreateCanvas();
			float screenH = Screen.height;
			float resScale = screenH / 1080f;
			PlaySound(outroClip);
			Image iconImg = MakeImage(((Component)canvas).transform, iconSprite, "VS_OutroIcon", screenH * VSConfig.iconHeightFraction);
			Image crossImg = MakeImage(((Component)canvas).transform, crossSprite, "VS_OutroCross", screenH * VSConfig.crossOutHeightFraction);
			outIcon(iconImg);
			outCross(crossImg);
			((Transform)((Graphic)crossImg).rectTransform).SetAsLastSibling();
			RectTransform iconRT = ((Graphic)iconImg).rectTransform;
			RectTransform crossRT = ((Graphic)crossImg).rectTransform;
			float bossSize = ((bossSpec != null && bossSpec.hasSize) ? bossSpec.size : VSConfig.textFontSize) * resScale;
			Vector2 bossTxtPos = new Vector2(VSConfig.outroBossTextX, VSConfig.outroBossTextY) * resScale;
			TextMeshProUGUI bossTxt = null;
			RectTransform bossTxtRT = null;
			if (bossSpec != null)
			{
				bossTxt = MakeText(color: bossSpec.hasColor ? bossSpec.color : Color.white, parent: ((Component)canvas).transform, content: bossSpec.title, name: "VS_OutroBossText", fontSize: bossSize);
				bossTxtRT = ((TMP_Text)bossTxt).rectTransform;
				bossTxtRT.anchoredPosition = bossTxtPos;
				((Component)bossTxt).gameObject.SetActive(false);
				outBossTxt(bossTxt);
			}
			string ultraTitle = ((ultraSpec != null && !string.IsNullOrEmpty(ultraSpec.title)) ? ultraSpec.title : "ULTRAKILLED");
			float ultraSize = ((ultraSpec != null && ultraSpec.hasSize) ? ultraSpec.size : VSConfig.textFontSize) * resScale;
			Color ultraCol = ((ultraSpec != null && ultraSpec.hasColor) ? ultraSpec.color : Color.white);
			Vector2 ultraPos = ((ultraSpec != null && ultraSpec.hasPos) ? (ultraSpec.pos * resScale) : (bossTxtPos + new Vector2(0f, 0f - bossSize * 1.25f)));
			TextMeshProUGUI ultraTxt = MakeText(((Component)canvas).transform, ultraTitle, "VS_OutroKilledText", ultraSize, ultraCol);
			RectTransform ultraTxtRT = ((TMP_Text)ultraTxt).rectTransform;
			ultraTxtRT.anchoredPosition = ultraPos;
			((Component)ultraTxt).gameObject.SetActive(false);
			outUltraTxt(ultraTxt);
			Vector2 iconBase = Vector2.zero;
			Vector2 crossBase = Vector2.zero;
			iconRT.anchoredPosition = iconBase;
			crossRT.anchoredPosition = crossBase;
			((Transform)iconRT).localScale = Vector3.one * VSConfig.vsStartScale;
			((Transform)crossRT).localScale = Vector3.one * VSConfig.vsStartScale;
			((Component)crossImg).gameObject.SetActive(false);
			float t1 = VSConfig.vsPopDuration;
			float t2 = t1 + VSConfig.outroCrossDelay;
			float t3 = t2 + VSConfig.vsPopDuration;
			float t4 = t3 + VSConfig.shakeDuration;
			float t5 = t4 + VSConfig.outroHoldDuration;
			float tEnd = t5 + VSConfig.slideOutDuration;
			bool crossShown = false;
			bool bossShown = false;
			bool ultraShown = false;
			float T = 0f;
			while (T < tEnd)
			{
				T += Time.unscaledDeltaTime;
				float ki = EaseOutCubic(Mathf.Clamp01(T / VSConfig.vsPopDuration));
				((Transform)iconRT).localScale = Vector3.one * Mathf.LerpUnclamped(VSConfig.vsStartScale, 1f, ki);
				if (!bossShown && T >= t1 && (Object)(object)bossTxt != (Object)null)
				{
					((Component)bossTxt).gameObject.SetActive(true);
					bossShown = true;
				}
				if (T >= t2)
				{
					if (!crossShown)
					{
						((Component)crossImg).gameObject.SetActive(true);
						crossShown = true;
					}
					float kc = EaseOutCubic(Mathf.Clamp01((T - t2) / VSConfig.vsPopDuration));
					((Transform)crossRT).localScale = Vector3.one * Mathf.LerpUnclamped(VSConfig.vsStartScale, 1f, kc);
				}
				if (!ultraShown && T >= t3)
				{
					((Component)ultraTxt).gameObject.SetActive(true);
					ultraShown = true;
				}
				Vector2 off = ShakeOffset(T - t1) + ShakeOffset(T - t3);
				if (T >= t5)
				{
					float ko = EaseInCubic(Mathf.Clamp01((T - t5) / VSConfig.slideOutDuration));
					off += new Vector2(0f, screenH * 1.2f * ko);
				}
				iconRT.anchoredPosition = iconBase + off;
				crossRT.anchoredPosition = crossBase + off;
				if ((Object)(object)bossTxtRT != (Object)null)
				{
					bossTxtRT.anchoredPosition = bossTxtPos + off;
				}
				ultraTxtRT.anchoredPosition = ultraPos + off;
				yield return null;
			}
		}

		private static Image MakeImage(Transform parent, Sprite sprite, string name, float targetHeight)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Expected O, but got Unknown
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name);
			val.transform.SetParent(parent, false);
			Image val2 = val.AddComponent<Image>();
			val2.sprite = sprite;
			((Graphic)val2).raycastTarget = false;
			val2.preserveAspect = true;
			RectTransform rectTransform = ((Graphic)val2).rectTransform;
			rectTransform.anchorMin = new Vector2(0.5f, 0.5f);
			rectTransform.anchorMax = new Vector2(0.5f, 0.5f);
			rectTransform.pivot = new Vector2(0.5f, 0.5f);
			float num = (float)((Texture)sprite.texture).width / (float)((Texture)sprite.texture).height;
			rectTransform.sizeDelta = new Vector2(targetHeight * num, targetHeight);
			rectTransform.anchoredPosition = Vector2.zero;
			((Transform)rectTransform).localScale = Vector3.one;
			return val2;
		}

		private static float EaseOutCubic(float x)
		{
			return 1f - Mathf.Pow(1f - x, 3f);
		}

		private static float EaseInCubic(float x)
		{
			return x * x * x;
		}

		private static IEnumerator PlayVSIntro(string bossName, Vector3 bossPos)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			introPlaying = true;
			string playerPath = Path.Combine(BossVSIntroPlugin.ImagesRoot, VSConfig.playerFileName);
			string vsPath = Path.Combine(BossVSIntroPlugin.ImagesRoot, VSConfig.vsFileName);
			Sprite playerSprite = LoadSprite(playerPath);
			Sprite vsSprite = LoadSprite(vsPath);
			Sprite bossSprite = ResolveBossSprite(bossName, bossPos);
			Sprite playerBgSprite = ResolveBackgroundSprite(VSConfig.backgroundPlayerFolder, bossName, bossPos);
			Sprite enemyBgSprite = ResolveBackgroundSprite(VSConfig.backgroundBossesFolder, bossName, bossPos);
			TextSpec playerText = ResolvePlayerText();
			TextSpec enemyText = ResolveEnemyText(bossName, bossPos);
			if ((Object)(object)playerSprite == (Object)null)
			{
				BossVSIntroPlugin.Log.LogWarning((object)("Missing player image: " + playerPath));
			}
			if ((Object)(object)vsSprite == (Object)null)
			{
				BossVSIntroPlugin.Log.LogWarning((object)("Missing VS image: " + vsPath));
			}
			if ((Object)(object)bossSprite == (Object)null)
			{
				BossVSIntroPlugin.Log.LogWarning((object)("Missing boss image for '" + bossName + "' (no level override, no generic, no Default.png)."));
			}
			if ((Object)(object)playerSprite == (Object)null || (Object)(object)vsSprite == (Object)null || (Object)(object)bossSprite == (Object)null)
			{
				introPlaying = false;
				yield break;
			}
			lastIntroTime = Time.unscaledTime;
			float previousTimeScale = Time.timeScale;
			Time.timeScale = 0f;
			frozeForCard = true;
			frozeAt = Time.unscaledTime;
			Image playerImg = null;
			Image bossImg = null;
			Image vsImg = null;
			Image playerBgImg = null;
			Image enemyBgImg = null;
			TextMeshProUGUI playerTxt = null;
			TextMeshProUGUI enemyTxt = null;
			IEnumerator anim = AnimateCard(playerSprite, bossSprite, vsSprite, playerBgSprite, enemyBgSprite, playerText, enemyText, delegate(Image img)
			{
				playerImg = img;
			}, delegate(Image img)
			{
				bossImg = img;
			}, delegate(Image img)
			{
				vsImg = img;
			}, delegate(Image img)
			{
				playerBgImg = img;
			}, delegate(Image img)
			{
				enemyBgImg = img;
			}, delegate(TextMeshProUGUI t)
			{
				playerTxt = t;
			}, delegate(TextMeshProUGUI t)
			{
				enemyTxt = t;
			});
			while (true)
			{
				Time.timeScale = 0f;
				object current;
				try
				{
					if (!anim.MoveNext())
					{
						break;
					}
					current = anim.Current;
					goto IL_0345;
				}
				catch (Exception ex)
				{
					Exception e = ex;
					BossVSIntroPlugin.Log.LogError((object)$"Boss VS Intro splash threw, bailing safely: {e}");
				}
				break;
				IL_0345:
				yield return current;
			}
			if ((Object)(object)playerImg != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)playerImg).gameObject);
			}
			if ((Object)(object)bossImg != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)bossImg).gameObject);
			}
			if ((Object)(object)vsImg != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)vsImg).gameObject);
			}
			if ((Object)(object)playerBgImg != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)playerBgImg).gameObject);
			}
			if ((Object)(object)enemyBgImg != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)enemyBgImg).gameObject);
			}
			if ((Object)(object)playerTxt != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)playerTxt).gameObject);
			}
			if ((Object)(object)enemyTxt != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)enemyTxt).gameObject);
			}
			Time.timeScale = previousTimeScale;
			frozeForCard = false;
			introPlaying = false;
		}

		private static IEnumerator AnimateCard(Sprite playerSprite, Sprite bossSprite, Sprite vsSprite, Sprite playerBgSprite, Sprite enemyBgSprite, TextSpec playerText, TextSpec enemyText, Action<Image> outPlayer, Action<Image> outBoss, Action<Image> outVs, Action<Image> outPlayerBg, Action<Image> outEnemyBg, Action<TextMeshProUGUI> outPlayerTxt, Action<TextMeshProUGUI> outEnemyTxt)
		{
			Canvas canvas = GetOrCreateCanvas();
			PlaySound(triggerClip);
			float screenW = Screen.width;
			float screenH = Screen.height;
			Image playerImg = MakeImage(((Component)canvas).transform, playerSprite, "VS_Player", screenH);
			Image bossImg = MakeImage(((Component)canvas).transform, bossSprite, "VS_Boss", screenH);
			Image vsImg = MakeImage(((Component)canvas).transform, vsSprite, "VS_Badge", screenH * VSConfig.vsHeightFraction);
			((Graphic)playerImg).color = Color.black;
			((Graphic)bossImg).color = Color.black;
			Image playerFlash = MakeFlash(playerImg, playerSprite);
			Image bossFlash = MakeFlash(bossImg, bossSprite);
			outPlayer(playerImg);
			outBoss(bossImg);
			outVs(vsImg);
			RectTransform playerRT = ((Graphic)playerImg).rectTransform;
			RectTransform bossRT = ((Graphic)bossImg).rectTransform;
			RectTransform vsRT = ((Graphic)vsImg).rectTransform;
			float resScale = screenH / 1080f;
			float halfGap = VSConfig.centerGap * resScale * 0.5f;
			float playerHalfW = playerRT.sizeDelta.x * 0.5f;
			float bossHalfW = bossRT.sizeDelta.x * 0.5f;
			Vector2 playerStart = new Vector2(0f - screenW, 0f);
			Vector2 bossStart = new Vector2(screenW, 0f);
			Vector2 playerEnd = new Vector2(0f - (playerHalfW + halfGap), 0f);
			Vector2 bossEnd = new Vector2(bossHalfW + halfGap, 0f);
			playerRT.anchoredPosition = playerStart;
			bossRT.anchoredPosition = bossStart;
			vsRT.anchoredPosition = Vector2.zero;
			((Transform)playerRT).SetAsLastSibling();
			((Transform)bossRT).SetAsLastSibling();
			((Transform)vsRT).SetAsLastSibling();
			if ((Object)(object)playerBgSprite != (Object)null)
			{
				Image pbg = MakeImage(((Component)canvas).transform, playerBgSprite, "VS_PlayerBg", screenH);
				((Graphic)pbg).rectTransform.anchoredPosition = playerStart;
				((Transform)((Graphic)pbg).rectTransform).SetAsFirstSibling();
				((Component)pbg).gameObject.AddComponent<RectFollower>().target = playerRT;
				outPlayerBg(pbg);
			}
			if ((Object)(object)enemyBgSprite != (Object)null)
			{
				Image ebg = MakeImage(((Component)canvas).transform, enemyBgSprite, "VS_EnemyBg", screenH);
				((Graphic)ebg).rectTransform.anchoredPosition = bossStart;
				((Transform)((Graphic)ebg).rectTransform).SetAsFirstSibling();
				((Component)ebg).gameObject.AddComponent<RectFollower>().target = bossRT;
				outEnemyBg(ebg);
			}
			((Component)vsImg).gameObject.SetActive(false);
			float resScaleTxt = screenH / 1080f;
			float textY = screenH * VSConfig.textYFraction;
			RectTransform playerTxtRT = null;
			RectTransform enemyTxtRT = null;
			Vector2 playerTxtStart = Vector2.zero;
			Vector2 playerTxtEnd = Vector2.zero;
			Vector2 enemyTxtStart = Vector2.zero;
			Vector2 enemyTxtEnd = Vector2.zero;
			if (playerText != null)
			{
				float size = (playerText.hasSize ? playerText.size : VSConfig.textFontSize) * resScaleTxt;
				Color col = (playerText.hasColor ? playerText.color : Color.white);
				playerTxtEnd = (Vector2)(playerText.hasPos ? (playerText.pos * resScaleTxt) : new Vector2(playerEnd.x, textY));
				playerTxtStart = new Vector2(0f - screenW, playerTxtEnd.y);
				TextMeshProUGUI t2 = MakeText(((Component)canvas).transform, playerText.title, "VS_PlayerText", size, col);
				((TMP_Text)t2).rectTransform.anchoredPosition = playerTxtStart;
				playerTxtRT = ((TMP_Text)t2).rectTransform;
				outPlayerTxt(t2);
			}
			if (enemyText != null)
			{
				float size2 = (enemyText.hasSize ? enemyText.size : VSConfig.textFontSize) * resScaleTxt;
				Color col2 = (enemyText.hasColor ? enemyText.color : Color.white);
				enemyTxtEnd = (Vector2)(enemyText.hasPos ? (enemyText.pos * resScaleTxt) : new Vector2(bossEnd.x, textY));
				enemyTxtStart = new Vector2(screenW, enemyTxtEnd.y);
				TextMeshProUGUI t3 = MakeText(((Component)canvas).transform, enemyText.title, "VS_EnemyText", size2, col2);
				((TMP_Text)t3).rectTransform.anchoredPosition = enemyTxtStart;
				enemyTxtRT = ((TMP_Text)t3).rectTransform;
				outEnemyTxt(t3);
			}
			float t4 = 0f;
			while (t4 < VSConfig.slideInDuration)
			{
				t4 += Time.unscaledDeltaTime;
				float k = EaseOutCubic(Mathf.Clamp01(t4 / VSConfig.slideInDuration));
				playerRT.anchoredPosition = Vector2.LerpUnclamped(playerStart, playerEnd, k);
				bossRT.anchoredPosition = Vector2.LerpUnclamped(bossStart, bossEnd, k);
				yield return null;
			}
			playerRT.anchoredPosition = playerEnd;
			bossRT.anchoredPosition = bossEnd;
			t4 = 0f;
			while (t4 < VSConfig.vsDelay)
			{
				t4 += Time.unscaledDeltaTime;
				yield return null;
			}
			((Component)vsImg).gameObject.SetActive(true);
			float popDuration = Mathf.Max(VSConfig.vsPopDuration, VSConfig.textSlideDuration);
			t4 = 0f;
			while (t4 < popDuration)
			{
				t4 += Time.unscaledDeltaTime;
				float kv = EaseOutCubic(Mathf.Clamp01(t4 / VSConfig.vsPopDuration));
				((Transform)vsRT).localScale = Vector3.one * Mathf.LerpUnclamped(VSConfig.vsStartScale, 1f, kv);
				float kt = EaseOutCubic(Mathf.Clamp01(t4 / VSConfig.textSlideDuration));
				if ((Object)(object)playerTxtRT != (Object)null)
				{
					playerTxtRT.anchoredPosition = Vector2.LerpUnclamped(playerTxtStart, playerTxtEnd, kt);
				}
				if ((Object)(object)enemyTxtRT != (Object)null)
				{
					enemyTxtRT.anchoredPosition = Vector2.LerpUnclamped(enemyTxtStart, enemyTxtEnd, kt);
				}
				yield return null;
			}
			((Transform)vsRT).localScale = Vector3.one;
			if ((Object)(object)playerTxtRT != (Object)null)
			{
				playerTxtRT.anchoredPosition = playerTxtEnd;
			}
			if ((Object)(object)enemyTxtRT != (Object)null)
			{
				enemyTxtRT.anchoredPosition = enemyTxtEnd;
			}
			((Graphic)playerImg).color = Color.white;
			((Graphic)bossImg).color = Color.white;
			SetFlashAlpha(playerFlash, 1f);
			SetFlashAlpha(bossFlash, 1f);
			t4 = 0f;
			while (t4 < VSConfig.shakeDuration)
			{
				t4 += Time.unscaledDeltaTime;
				float decay = 1f - Mathf.Clamp01(t4 / VSConfig.shakeDuration);
				float amp = VSConfig.shakeAmplitude * decay;
				playerRT.anchoredPosition = playerEnd + new Vector2(Random.Range(0f - amp, amp), Random.Range(0f - amp, amp));
				bossRT.anchoredPosition = bossEnd + new Vector2(Random.Range(0f - amp, amp), Random.Range(0f - amp, amp));
				float flash = 1f - Mathf.Clamp01(t4 / VSConfig.colorFadeDuration);
				SetFlashAlpha(playerFlash, flash);
				SetFlashAlpha(bossFlash, flash);
				yield return null;
			}
			playerRT.anchoredPosition = playerEnd;
			bossRT.anchoredPosition = bossEnd;
			SetFlashAlpha(playerFlash, 0f);
			SetFlashAlpha(bossFlash, 0f);
			t4 = 0f;
			while (t4 < VSConfig.holdDuration)
			{
				t4 += Time.unscaledDeltaTime;
				yield return null;
			}
			Vector2 vsUpEnd = new Vector2(0f, screenH);
			t4 = 0f;
			while (t4 < VSConfig.slideOutDuration)
			{
				t4 += Time.unscaledDeltaTime;
				float k2 = EaseInCubic(Mathf.Clamp01(t4 / VSConfig.slideOutDuration));
				playerRT.anchoredPosition = Vector2.LerpUnclamped(playerEnd, playerStart, k2);
				bossRT.anchoredPosition = Vector2.LerpUnclamped(bossEnd, bossStart, k2);
				vsRT.anchoredPosition = Vector2.LerpUnclamped(Vector2.zero, vsUpEnd, k2);
				if ((Object)(object)playerTxtRT != (Object)null)
				{
					playerTxtRT.anchoredPosition = Vector2.LerpUnclamped(playerTxtEnd, playerTxtStart, k2);
				}
				if ((Object)(object)enemyTxtRT != (Object)null)
				{
					enemyTxtRT.anchoredPosition = Vector2.LerpUnclamped(enemyTxtEnd, enemyTxtStart, k2);
				}
				yield return null;
			}
		}
	}
	[HarmonyPatch(typeof(AddStylePoints), "Activate")]
	public class EarthmoverStylePatch
	{
		private static void Postfix(AddStylePoints __instance)
		{
			if (!((Object)(object)__instance == (Object)null))
			{
				BossVSIntroPatch.OnStyleBonus(__instance.styleName);
			}
		}
	}
}