Decompiled source of TouhouNuclearOverlay v1.6.2

plugins/TouhouNuclearOverlay.dll

Decompiled 19 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using UnityEngine;
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: AssemblyVersion("0.0.0.0")]
namespace TouhouNuclearOverlay;

[BepInPlugin("com.cutyimodo.touhounuclearoverlay", "Touhou Nuclear Overlay", "1.6.2")]
public class Plugin : BaseUnityPlugin
{
	public const string GUID = "com.cutyimodo.touhounuclearoverlay";

	public const string NAME = "Touhou Nuclear Overlay";

	public const string VERSION = "1.6.2";

	private static Plugin Instance;

	private static bool overlayActive = false;

	private static GameObject overlayRoot;

	private static float scrollOffset = 0f;

	private static Texture2D cautionTex;

	private static Texture2D radTex;

	private static Texture2D radTiledTex;

	private static object dockedApparatus = null;

	private static bool hasCheckedApparatus = false;

	private static Type lungPropType;

	private static Type hudManagerType;

	private static bool vanillaWarningDisabled = false;

	private static float suppressTimer = 0f;

	private static Type startOfRoundType;

	private static int overlayPhase = 0;

	private static float phaseTimer = 0f;

	private const float BG_OPACITY = 0f;

	private const float CAUTION_HEIGHT = 0.15f;

	private const float CAUTION_TOP_OFFSET = 0.05f;

	private const float CAUTION_BOT_OFFSET = 0.05f;

	private const float CAUTION_UV_X = 2f;

	private const float CAUTION_BREATHE = 1f;

	private const float CAUTION_BREATHE_SPEED = 1.5f;

	private const float CAUTION_SCROLL = 0.15f;

	private const float RAD_SIZE_RATIO = 5f / 18f;

	private const float RAD_GAP_RATIO = 1f / 3f;

	private const float RAD_Y_CENTER = 0.5f;

	private const float RAD_OPACITY = 0.5f;

	private const float RAD_SCROLL = 0.2f;

	private const float FADE_IN = 1f;

	private const float HOLD = 8f;

	private const float FADE_OUT = 1f;

	private static AudioClip alertClip;

	private static AudioSource alertSource;

	private static bool audioLoaded = false;

	private static CanvasGroup canvasGroupRef;

	private static RawImage topImg;

	private static RawImage bottomImg;

	private static List<RawImage> radImages = new List<RawImage>();

	private void Awake()
	{
		Instance = this;
		cautionTex = LoadEmbeddedTexture("TouhouNuclearOverlay.caution.png");
		radTex = LoadEmbeddedTexture("TouhouNuclearOverlay.radiation.png");
		alertClip = LoadEmbeddedWav("TouhouNuclearOverlay.alert.wav");
		if ((Object)(object)alertClip != (Object)null)
		{
			audioLoaded = true;
			((BaseUnityPlugin)this).Logger.LogInfo((object)$"Alert sound loaded ({alertClip.length}s)");
		}
		else
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)"Alert sound not found in embedded resources");
		}
		((BaseUnityPlugin)this).Logger.LogInfo((object)"Plugin Touhou Nuclear Overlay v1.6.2 loaded!");
	}

	private static Texture2D LoadEmbeddedTexture(string resourceName)
	{
		//IL_0055: Unknown result type (might be due to invalid IL or missing references)
		//IL_005b: Expected O, but got Unknown
		Assembly executingAssembly = Assembly.GetExecutingAssembly();
		using Stream stream = executingAssembly.GetManifestResourceStream(resourceName);
		if (stream == null)
		{
			Debug.LogWarning((object)("[TouhouNuclearOverlay] Resource not found: " + resourceName));
			return Texture2D.whiteTexture;
		}
		byte[] array = new byte[stream.Length];
		stream.Read(array, 0, array.Length);
		Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
		Type type = typeof(Texture2D).Assembly.GetType("UnityEngine.ImageConversion");
		if (type == null)
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				type = assembly.GetType("UnityEngine.ImageConversion");
				if (type != null)
				{
					break;
				}
			}
		}
		if (type != null)
		{
			MethodInfo method = type.GetMethod("LoadImage", new Type[2]
			{
				typeof(Texture2D),
				typeof(byte[])
			});
			if (method != null)
			{
				method.Invoke(null, new object[2] { val, array });
			}
		}
		((Texture)val).wrapMode = (TextureWrapMode)0;
		return val;
	}

	private static object GetSOR()
	{
		if (startOfRoundType == null)
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				startOfRoundType = assembly.GetType("StartOfRound");
				if (startOfRoundType != null)
				{
					break;
				}
			}
		}
		if (startOfRoundType == null)
		{
			return null;
		}
		PropertyInfo property = startOfRoundType.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public);
		if (property != null)
		{
			return property.GetValue(null);
		}
		FieldInfo field = startOfRoundType.GetField("Instance", BindingFlags.Static | BindingFlags.Public);
		if (field != null)
		{
			return field.GetValue(null);
		}
		return null;
	}

	private static bool GetBool(object obj, string name)
	{
		if (obj == null)
		{
			return false;
		}
		Type type = obj.GetType();
		PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public);
		if (property != null)
		{
			return (bool)property.GetValue(obj);
		}
		FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public);
		if (field != null)
		{
			return (bool)field.GetValue(obj);
		}
		return false;
	}

	private void Update()
	{
		//IL_0283: Unknown result type (might be due to invalid IL or missing references)
		//IL_0288: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e3: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e8: Unknown result type (might be due to invalid IL or missing references)
		//IL_0308: Unknown result type (might be due to invalid IL or missing references)
		//IL_0326: Unknown result type (might be due to invalid IL or missing references)
		//IL_03a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_03aa: Unknown result type (might be due to invalid IL or missing references)
		//IL_03c9: Unknown result type (might be due to invalid IL or missing references)
		//IL_03f0: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			object sOR = GetSOR();
			if (sOR != null && GetBool(sOR, "shipHasLanded") && !hasCheckedApparatus)
			{
				FindDockedApparatus();
				hasCheckedApparatus = true;
			}
			if (sOR != null && !GetBool(sOR, "shipHasLanded") && !GetBool(sOR, "shipIsLeaving") && hasCheckedApparatus)
			{
				hasCheckedApparatus = false;
				dockedApparatus = null;
				CleanupOverlay();
			}
			if (sOR == null && hasCheckedApparatus)
			{
				hasCheckedApparatus = false;
				dockedApparatus = null;
				CleanupOverlay();
			}
			if (dockedApparatus != null && !GetBool(dockedApparatus, "isLungDocked"))
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Apparatus was taken! Triggering overlay.");
				dockedApparatus = null;
				TriggerOverlay();
			}
			if (overlayPhase <= 0)
			{
				return;
			}
			phaseTimer += Time.deltaTime;
			scrollOffset += Time.deltaTime;
			suppressTimer += Time.deltaTime;
			if (suppressTimer >= 0.5f)
			{
				suppressTimer = 0f;
				SuppressVanillaRadiationWarning();
			}
			float alpha = 0f;
			switch (overlayPhase)
			{
			case 1:
				alpha = Mathf.Clamp01(phaseTimer / 1f);
				if (phaseTimer >= 1f)
				{
					overlayPhase = 2;
					phaseTimer = 0f;
				}
				break;
			case 2:
				alpha = 1f;
				if (phaseTimer >= 8f)
				{
					overlayPhase = 3;
					phaseTimer = 0f;
				}
				break;
			case 3:
				alpha = 1f - Mathf.Clamp01(phaseTimer / 1f);
				if (phaseTimer >= 1f)
				{
					CleanupOverlay();
				}
				break;
			}
			if ((Object)(object)canvasGroupRef != (Object)null)
			{
				canvasGroupRef.alpha = alpha;
			}
			float num = 0.5f + 0.5f * Mathf.Sin(Time.time * 1.5f * (float)Math.PI);
			float num2 = 0f + 1f * num;
			if ((Object)(object)topImg != (Object)null)
			{
				Rect uvRect = topImg.uvRect;
				((Rect)(ref uvRect)).x = scrollOffset * 0.15f;
				topImg.uvRect = uvRect;
				((Graphic)topImg).color = new Color(1f, 1f, 1f, num2);
			}
			if ((Object)(object)bottomImg != (Object)null)
			{
				Rect uvRect2 = bottomImg.uvRect;
				((Rect)(ref uvRect2)).x = scrollOffset * 0.15f * 0.8f;
				bottomImg.uvRect = uvRect2;
				((Graphic)bottomImg).color = new Color(1f, 1f, 1f, num2);
			}
			float num3 = radSizePx();
			float num4 = num3 * (1f / 3f);
			float num5 = num3 + num4;
			int count = radImages.Count;
			float num6 = (float)count * num5;
			for (int i = 0; i < count; i++)
			{
				if (!((Object)(object)radImages[i] != (Object)null))
				{
					continue;
				}
				RectTransform component = ((Component)radImages[i]).GetComponent<RectTransform>();
				if ((Object)(object)component != (Object)null)
				{
					Vector2 anchoredPosition = component.anchoredPosition;
					anchoredPosition.x += 0.2f * (float)Screen.width * Time.deltaTime;
					if (anchoredPosition.x > (float)Screen.width + num3)
					{
						anchoredPosition.x -= num6;
					}
					component.anchoredPosition = anchoredPosition;
				}
			}
		}
		catch (Exception)
		{
		}
	}

	private float radSizePx()
	{
		return (float)Screen.height * (5f / 18f);
	}

	private void CleanupOverlay()
	{
		if ((Object)(object)overlayRoot != (Object)null)
		{
			Object.Destroy((Object)(object)overlayRoot);
			overlayRoot = null;
		}
		overlayActive = false;
		overlayPhase = 0;
		canvasGroupRef = null;
		topImg = null;
		bottomImg = null;
		radImages.Clear();
	}

	private void FindDockedApparatus()
	{
		if (lungPropType == null)
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				lungPropType = assembly.GetType("LungProp");
				if (lungPropType != null)
				{
					break;
				}
			}
		}
		if (lungPropType == null)
		{
			return;
		}
		Object[] array = Object.FindObjectsOfType(lungPropType);
		if (array == null)
		{
			return;
		}
		Object[] array2 = array;
		foreach (Object obj in array2)
		{
			if (GetBool(obj, "isLungDocked"))
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Found docked Apparatus, watching for removal.");
				dockedApparatus = obj;
				break;
			}
		}
	}

	private static AudioClip LoadEmbeddedWav(string resourceName)
	{
		Assembly executingAssembly = Assembly.GetExecutingAssembly();
		using Stream stream = executingAssembly.GetManifestResourceStream(resourceName);
		if (stream == null)
		{
			return null;
		}
		byte[] array = new byte[stream.Length];
		stream.Read(array, 0, array.Length);
		if (array.Length < 44)
		{
			return null;
		}
		int num = array[22] | (array[23] << 8);
		int num2 = array[24] | (array[25] << 8) | (array[26] << 16) | (array[27] << 24);
		int num3 = array[34] | (array[35] << 8);
		int i = 12;
		int num4 = 0;
		int num5;
		for (; i < array.Length - 8; i += 8 + num5)
		{
			char c = (char)array[i];
			string text = c.ToString();
			c = (char)array[i + 1];
			string text2 = c.ToString();
			c = (char)array[i + 2];
			string text3 = c.ToString();
			c = (char)array[i + 3];
			string text4 = text + text2 + text3 + c;
			num5 = array[i + 4] | (array[i + 5] << 8) | (array[i + 6] << 16) | (array[i + 7] << 24);
			if (text4 == "data")
			{
				i += 8;
				num4 = num5;
				break;
			}
		}
		if (num4 == 0)
		{
			return null;
		}
		int num6 = num4 / (num3 / 8) / num;
		float[] array2 = new float[num6 * num];
		int num7 = i;
		for (int j = 0; j < array2.Length; j++)
		{
			if (num7 >= array.Length - 1)
			{
				break;
			}
			short num8 = (short)(array[num7] | (array[num7 + 1] << 8));
			array2[j] = (float)num8 / 32768f;
			num7 += 2;
		}
		AudioClip val = AudioClip.Create("alert", num6, num, num2, false);
		val.SetData(array2, 0);
		return val;
	}

	private void SuppressVanillaRadiationWarning()
	{
		try
		{
			if (hudManagerType == null)
			{
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				foreach (Assembly assembly in assemblies)
				{
					hudManagerType = assembly.GetType("HUDManager");
					if (hudManagerType != null)
					{
						break;
					}
				}
			}
			if (hudManagerType == null)
			{
				return;
			}
			PropertyInfo property = hudManagerType.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public);
			object obj = ((property != null) ? property.GetValue(null) : null);
			if (obj == null)
			{
				FieldInfo field = hudManagerType.GetField("Instance", BindingFlags.Static | BindingFlags.Public);
				obj = ((field != null) ? field.GetValue(null) : null);
			}
			if (obj == null)
			{
				return;
			}
			FieldInfo field2 = hudManagerType.GetField("RadiationWarningHUD", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field2 != null)
			{
				object value = field2.GetValue(obj);
				GameObject val = (GameObject)((value is GameObject) ? value : null);
				if (val != null && val.activeSelf)
				{
					val.SetActive(false);
				}
				else
				{
					Behaviour val2 = (Behaviour)((value is Behaviour) ? value : null);
					if (val2 != null && val2.enabled)
					{
						val2.enabled = false;
					}
				}
			}
			FieldInfo field3 = hudManagerType.GetField("radiationWarningAudio", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field3 != null)
			{
				object value2 = field3.GetValue(obj);
				if (value2 != null)
				{
					PropertyInfo property2 = value2.GetType().GetProperty("mute", BindingFlags.Instance | BindingFlags.Public);
					if (property2 != null)
					{
						property2.SetValue(value2, true);
					}
					MethodInfo method = value2.GetType().GetMethod("Stop", Type.EmptyTypes);
					if (method != null)
					{
						method.Invoke(value2, null);
					}
				}
			}
			FieldInfo field4 = hudManagerType.GetField("radiationGraphicAnimator", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field4 != null)
			{
				object value3 = field4.GetValue(obj);
				Behaviour val3 = (Behaviour)((value3 is Behaviour) ? value3 : null);
				if (val3 != null && val3.enabled)
				{
					val3.enabled = false;
				}
			}
		}
		catch (Exception)
		{
		}
	}

	private void PlayAlertSound()
	{
		if (audioLoaded && !((Object)(object)alertClip == (Object)null))
		{
			if ((Object)(object)alertSource == (Object)null)
			{
				alertSource = ((Component)this).gameObject.AddComponent<AudioSource>();
				alertSource.spatialBlend = 0f;
				alertSource.loop = false;
				alertSource.playOnAwake = false;
				alertSource.priority = 0;
			}
			alertSource.clip = alertClip;
			alertSource.volume = 1f;
			alertSource.Play();
		}
	}

	private void TriggerOverlay()
	{
		if (overlayActive)
		{
			return;
		}
		overlayActive = true;
		overlayPhase = 1;
		phaseTimer = 0f;
		scrollOffset = 0f;
		try
		{
			CreateOverlay();
			PlayAlertSound();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Overlay created successfully!");
		}
		catch (Exception arg)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)$"Failed to create overlay: {arg}");
			CleanupOverlay();
		}
	}

	private void CreateOverlay()
	{
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_002c: Expected O, but got Unknown
		//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
		//IL_0153: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f2: Unknown result type (might be due to invalid IL or missing references)
		//IL_0286: Unknown result type (might be due to invalid IL or missing references)
		//IL_028d: Expected O, but got Unknown
		//IL_02b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_02d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_02eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0302: Unknown result type (might be due to invalid IL or missing references)
		//IL_0318: Unknown result type (might be due to invalid IL or missing references)
		//IL_034f: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)overlayRoot != (Object)null)
		{
			Object.Destroy((Object)(object)overlayRoot);
		}
		overlayRoot = new GameObject("TouhouNuclearOverlay");
		Object.DontDestroyOnLoad((Object)(object)overlayRoot);
		Canvas val = overlayRoot.AddComponent<Canvas>();
		val.renderMode = (RenderMode)0;
		val.sortingOrder = 10000;
		canvasGroupRef = overlayRoot.AddComponent<CanvasGroup>();
		canvasGroupRef.alpha = 0f;
		canvasGroupRef.blocksRaycasts = false;
		canvasGroupRef.interactable = false;
		float num = Screen.width;
		float num2 = Screen.height;
		GameObject val2 = CreateChild(overlayRoot, "Bg");
		RawImage val3 = val2.AddComponent<RawImage>();
		((Graphic)val3).color = new Color(0f, 0f, 0f, 0f);
		StretchFill(val2);
		float minY = 0.79999995f;
		float maxY = 0.95f;
		GameObject val4 = CreateChild(overlayRoot, "TopBar");
		SetAnchors(val4, 0f, minY, 1f, maxY);
		GameObject val5 = CreateChild(val4, "Img");
		topImg = val5.AddComponent<RawImage>();
		topImg.texture = (Texture)(object)cautionTex;
		topImg.uvRect = new Rect(0f, 0f, 2f, 1f);
		SetAnchors(val5, -0.5f, 0f, 1.5f, 1f);
		float minY2 = 0.05f;
		float maxY2 = 0.2f;
		GameObject val6 = CreateChild(overlayRoot, "BotBar");
		SetAnchors(val6, 0f, minY2, 1f, maxY2);
		GameObject val7 = CreateChild(val6, "Img");
		bottomImg = val7.AddComponent<RawImage>();
		bottomImg.texture = (Texture)(object)cautionTex;
		bottomImg.uvRect = new Rect(0f, 0f, 2f, 1f);
		SetAnchors(val7, -0.5f, 0f, 1.5f, 1f);
		float num3 = radSizePx();
		float num4 = num3 * (1f / 3f);
		float num5 = num3 + num4;
		int num6 = Mathf.CeilToInt(num / num5) + 3;
		float num7 = num2 * 0.5f;
		GameObject val8 = CreateChild(overlayRoot, "RadContainer");
		StretchFill(val8);
		radImages.Clear();
		for (int i = 0; i < num6; i++)
		{
			GameObject val9 = new GameObject("Rad" + i);
			val9.transform.SetParent(val8.transform, false);
			RectTransform val10 = val9.AddComponent<RectTransform>();
			val10.sizeDelta = new Vector2(num3, num3);
			float num8 = 0f - num3 + (float)i * num5;
			val10.anchorMin = new Vector2(0f, 0.5f);
			val10.anchorMax = new Vector2(0f, 0.5f);
			val10.pivot = new Vector2(0f, 0.5f);
			val10.anchoredPosition = new Vector2(num8, 0f * num2);
			RawImage val11 = val9.AddComponent<RawImage>();
			val11.texture = (Texture)(object)radTex;
			((Graphic)val11).color = new Color(1f, 1f, 1f, 0.5f);
			radImages.Add(val11);
		}
	}

	private static GameObject CreateChild(GameObject parent, string name)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0008: Expected O, but got Unknown
		GameObject val = new GameObject(name);
		val.transform.SetParent(parent.transform, false);
		val.AddComponent<RectTransform>();
		return val;
	}

	private static void SetAnchors(GameObject go, float minX, float minY, float maxX, float maxY)
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		//IL_003f: Unknown result type (might be due to invalid IL or missing references)
		RectTransform component = go.GetComponent<RectTransform>();
		if (!((Object)(object)component == (Object)null))
		{
			component.anchorMin = new Vector2(minX, minY);
			component.anchorMax = new Vector2(maxX, maxY);
			component.offsetMin = Vector2.zero;
			component.offsetMax = Vector2.zero;
		}
	}

	private static void StretchFill(GameObject go)
	{
		SetAnchors(go, 0f, 0f, 1f, 1f);
	}
}