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