Decompiled source of FoxyJumpscare v1.0.1

FoxyJumpscare.dll

Decompiled 5 hours ago
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("FoxyJumpscare")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1+0ab75f501a5ba60155b925ba520c0cea6e9ddc82")]
[assembly: AssemblyProduct("FoxyJumpscare")]
[assembly: AssemblyTitle("FoxyJumpscare")]
[assembly: AssemblyVersion("1.0.1.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 FoxyJumpscare
{
	internal static class AssetLoader
	{
		internal static Texture2D[] Frames;

		internal static AudioClip Scream;

		internal static bool Ready
		{
			get
			{
				if (Frames != null && Frames.Length != 0)
				{
					return (Object)(object)Scream != (Object)null;
				}
				return false;
			}
		}

		internal static void Load()
		{
			LoadFrames();
			LoadScream();
			if (Ready)
			{
				Plugin.Log.LogInfo((object)($"Loaded {Frames.Length} jumpscare frames ({((Texture)Frames[0]).width}x{((Texture)Frames[0]).height}) " + $"and scream audio ({Scream.length:0.00}s). Foxy is ready."));
			}
			else
			{
				Plugin.Log.LogError((object)"Assets failed to load - FoxyJumpscare will stay dormant.");
			}
		}

		private static void LoadFrames()
		{
			try
			{
				string[] array = Find("frame_*.png");
				if (array.Length == 0)
				{
					Plugin.Log.LogError((object)("No frame_*.png files found under " + Plugin.PluginDirectory));
					return;
				}
				Frames = (from texture in array.OrderBy<string, string>(Path.GetFileName, StringComparer.OrdinalIgnoreCase).Select(LoadTexture)
					where (Object)(object)texture != (Object)null
					select texture).ToArray();
				if (Frames.Length == 0)
				{
					Plugin.Log.LogError((object)"Found frame files but none of them could be decoded.");
				}
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Failed to load jumpscare frames: {arg}");
			}
		}

		private static string[] Find(string pattern)
		{
			try
			{
				return Directory.GetFiles(Plugin.PluginDirectory, pattern, SearchOption.AllDirectories);
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Could not search {Plugin.PluginDirectory} for {pattern}: {arg}");
				return new string[0];
			}
		}

		private static Texture2D LoadTexture(string path)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			try
			{
				byte[] array = File.ReadAllBytes(path);
				if (IsIndexedPng(array))
				{
					Plugin.Log.LogWarning((object)(Path.GetFileName(path) + " is an indexed-colour PNG, which Unity cannot decode. Re-save it as RGB/RGBA or it will show up blank."));
				}
				Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
				if (!ImageConversion.LoadImage(val, array))
				{
					Object.Destroy((Object)(object)val);
					Plugin.Log.LogError((object)("Could not decode image " + path));
					return null;
				}
				((Texture)val).wrapMode = (TextureWrapMode)1;
				return val;
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Failed to load {path}: {arg}");
				return null;
			}
		}

		private static bool IsIndexedPng(byte[] data)
		{
			if (data.Length > 25 && data[1] == 80 && data[2] == 78 && data[3] == 71)
			{
				return data[25] == 3;
			}
			return false;
		}

		private static void LoadScream()
		{
			string text = null;
			try
			{
				text = Find("scream.wav").FirstOrDefault();
				if (text == null)
				{
					Plugin.Log.LogError((object)("No scream.wav found under " + Plugin.PluginDirectory));
				}
				else
				{
					Scream = DecodeWav(File.ReadAllBytes(text), "FoxyScream");
				}
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Failed to load {text}: {arg}");
			}
		}

		private static AudioClip DecodeWav(byte[] wav, string name)
		{
			if (wav.Length < 12 || Encoding(wav, 0) != "RIFF" || Encoding(wav, 8) != "WAVE")
			{
				Plugin.Log.LogError((object)"Scream audio is not a RIFF/WAVE file.");
				return null;
			}
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			int num4 = 0;
			int num5 = -1;
			int num6 = 0;
			int num7 = 12;
			while (num7 + 8 <= wav.Length)
			{
				string text = Encoding(wav, num7);
				int num8 = BitConverter.ToInt32(wav, num7 + 4);
				int num9 = num7 + 8;
				if (num8 < 0 || num9 + num8 > wav.Length)
				{
					num8 = wav.Length - num9;
				}
				if (text == "fmt ")
				{
					num = BitConverter.ToInt16(wav, num9);
					num2 = BitConverter.ToInt16(wav, num9 + 2);
					num3 = BitConverter.ToInt32(wav, num9 + 4);
					num4 = BitConverter.ToInt16(wav, num9 + 14);
				}
				else if (text == "data")
				{
					num5 = num9;
					num6 = num8;
				}
				num7 = num9 + num8 + num8 % 2;
			}
			if (num5 < 0 || num2 <= 0 || num3 <= 0)
			{
				Plugin.Log.LogError((object)"Scream audio is missing a fmt or data chunk.");
				return null;
			}
			int num10 = num4 / 8;
			int num11 = num6 / num10;
			float[] array = new float[num11];
			for (int i = 0; i < num11; i++)
			{
				int num12 = num5 + i * num10;
				switch (num4)
				{
				case 8:
					array[i] = (float)(wav[num12] - 128) / 128f;
					break;
				case 16:
					array[i] = (float)BitConverter.ToInt16(wav, num12) / 32768f;
					break;
				case 24:
				{
					int num13 = wav[num12] | (wav[num12 + 1] << 8) | (wav[num12 + 2] << 16);
					if ((num13 & 0x800000) != 0)
					{
						num13 |= -16777216;
					}
					array[i] = (float)num13 / 8388608f;
					break;
				}
				case 32:
					array[i] = ((num == 3) ? BitConverter.ToSingle(wav, num12) : ((float)BitConverter.ToInt32(wav, num12) / 2.1474836E+09f));
					break;
				default:
					Plugin.Log.LogError((object)$"Unsupported WAV bit depth: {num4}");
					return null;
				}
			}
			AudioClip obj = AudioClip.Create(name, num11 / num2, num2, num3, false);
			obj.SetData(array, 0);
			return obj;
		}

		private static string Encoding(byte[] bytes, int offset)
		{
			return System.Text.Encoding.ASCII.GetString(bytes, offset, 4);
		}
	}
	internal static class Jumpscare
	{
		private static readonly Random Rng = new Random();

		private static float timer = -1f;

		private static int lastTickedFrame = -1;

		private static bool announcedTick;

		private static bool disabled;

		private static GameObject overlay;

		private static Canvas canvas;

		private static RawImage image;

		private static AudioSource audioSource;

		private static float elapsed;

		private static bool Playing => (Object)(object)overlay != (Object)null;

		internal static void Tick()
		{
			if (disabled || lastTickedFrame == Time.frameCount)
			{
				return;
			}
			lastTickedFrame = Time.frameCount;
			try
			{
				Run();
			}
			catch (Exception arg)
			{
				disabled = true;
				Plugin.Log.LogError((object)$"Unexpected error, disabling FoxyJumpscare for this session: {arg}");
				try
				{
					End();
				}
				catch
				{
				}
			}
		}

		private static void Run()
		{
			if (!announcedTick)
			{
				announcedTick = true;
				Plugin.Log.LogInfo((object)"Hooked into the game's update loop.");
			}
			float unscaledDeltaTime = Time.unscaledDeltaTime;
			if (Playing)
			{
				Advance(unscaledDeltaTime);
			}
			else if (Plugin.EnableTestHotkey.Value && Keyboard.current != null && ((ButtonControl)Keyboard.current.f8Key).wasPressedThisFrame)
			{
				if (!AssetLoader.Ready)
				{
					Plugin.Log.LogWarning((object)"F8 pressed, but the assets never loaded - see the errors above.");
					return;
				}
				Plugin.Log.LogInfo((object)"F8 pressed - forcing a jumpscare.");
				Begin();
			}
			else if (AssetLoader.Ready && IsEligible())
			{
				if (timer < 0f)
				{
					float value = Plugin.MinSeconds.Value;
					float num = Mathf.Max(value, Plugin.MaxSeconds.Value);
					timer = value + (float)Rng.NextDouble() * (num - value);
					Plugin.Log.LogInfo((object)$"Foxy armed: striking in {timer:0}s of eligible play time.");
				}
				timer -= unscaledDeltaTime;
				if (timer <= 0f)
				{
					Begin();
				}
			}
		}

		private static bool IsEligible()
		{
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return false;
			}
			PlayerControllerB localPlayerController = instance.localPlayerController;
			if ((Object)(object)localPlayerController == (Object)null)
			{
				return false;
			}
			if (!Plugin.AllowInOrbit.Value && (!instance.shipHasLanded || instance.inShipPhase))
			{
				return false;
			}
			if (!Plugin.ScareDeadPlayers.Value && localPlayerController.isPlayerDead)
			{
				return false;
			}
			return true;
		}

		private static void Begin()
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Expected O, but got Unknown
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			timer = -1f;
			elapsed = 0f;
			Plugin.Log.LogInfo((object)"Foxy jumpscare!");
			overlay = new GameObject("FoxyJumpscareOverlay");
			canvas = overlay.AddComponent<Canvas>();
			canvas.renderMode = (RenderMode)0;
			canvas.sortingOrder = 32767;
			GameObject val = new GameObject("FoxyImage");
			val.transform.SetParent(overlay.transform, false);
			image = val.AddComponent<RawImage>();
			image.texture = (Texture)(object)AssetLoader.Frames[0];
			Contain(((Graphic)image).rectTransform, AssetLoader.Frames[0]);
			audioSource = overlay.AddComponent<AudioSource>();
			audioSource.clip = AssetLoader.Scream;
			audioSource.spatialBlend = 0f;
			audioSource.volume = Mathf.Clamp01(Plugin.Volume.Value);
			audioSource.ignoreListenerPause = true;
			audioSource.bypassEffects = true;
			audioSource.bypassListenerEffects = true;
			audioSource.bypassReverbZones = true;
			audioSource.Play();
		}

		private static void Advance(float dt)
		{
			elapsed += dt;
			Texture2D[] frames = AssetLoader.Frames;
			float num = Mathf.Max(1f, Plugin.AnimationFps.Value);
			int num2 = Mathf.FloorToInt(elapsed * num);
			if (num2 < frames.Length)
			{
				image.texture = (Texture)(object)frames[num2];
				return;
			}
			if ((Object)(object)canvas != (Object)null)
			{
				((Behaviour)canvas).enabled = false;
			}
			if ((Object)(object)audioSource == (Object)null || !audioSource.isPlaying)
			{
				End();
			}
		}

		private static void End()
		{
			Object.Destroy((Object)(object)overlay);
			overlay = null;
			canvas = null;
			image = null;
			audioSource = null;
		}

		private static void Contain(RectTransform rect, Texture2D frame)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: 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_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: 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_0068: Unknown result type (might be due to invalid IL or missing references)
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(0.5f, 0.5f);
			rect.pivot = val;
			Vector2 anchorMin = (rect.anchorMax = val);
			rect.anchorMin = anchorMin;
			rect.anchoredPosition = Vector2.zero;
			float num = Mathf.Min((float)Screen.width / (float)((Texture)frame).width, (float)Screen.height / (float)((Texture)frame).height);
			rect.sizeDelta = new Vector2((float)((Texture)frame).width * num, (float)((Texture)frame).height * num);
		}
	}
	[HarmonyPatch(typeof(StartOfRound), "Update")]
	internal static class StartOfRoundUpdatePatch
	{
		private static void Postfix()
		{
			Jumpscare.Tick();
		}
	}
	[HarmonyPatch(typeof(MenuManager), "Update")]
	internal static class MenuManagerUpdatePatch
	{
		private static void Postfix()
		{
			Jumpscare.Tick();
		}
	}
	[BepInPlugin("kjelpw.FoxyJumpscare", "FoxyJumpscare", "1.0.1")]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "kjelpw.FoxyJumpscare";

		public const string Name = "FoxyJumpscare";

		public const string Version = "1.0.1";

		internal static ManualLogSource Log;

		internal static string PluginDirectory;

		internal static ConfigEntry<float> MinSeconds;

		internal static ConfigEntry<float> MaxSeconds;

		internal static ConfigEntry<float> Volume;

		internal static ConfigEntry<float> AnimationFps;

		internal static ConfigEntry<bool> AllowInOrbit;

		internal static ConfigEntry<bool> ScareDeadPlayers;

		internal static ConfigEntry<bool> EnableTestHotkey;

		private void Awake()
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Expected O, but got Unknown
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Expected O, but got Unknown
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Expected O, but got Unknown
			//IL_019f: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			MinSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("General", "MinSeconds", 900f, new ConfigDescription("Minimum seconds between jumpscares.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 86400f), Array.Empty<object>()));
			MaxSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("General", "MaxSeconds", 3300f, new ConfigDescription("Maximum seconds between jumpscares.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 86400f), Array.Empty<object>()));
			Volume = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Volume", 1f, new ConfigDescription("Volume of the scream.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			AnimationFps = ((BaseUnityPlugin)this).Config.Bind<float>("General", "AnimationFPS", 25f, new ConfigDescription("Playback speed of the jumpscare animation, in frames per second.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 120f), Array.Empty<object>()));
			AllowInOrbit = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "AllowInOrbit", false, "Also trigger jumpscares while the ship is in orbit. When false, Foxy only strikes while landed on a moon.");
			ScareDeadPlayers = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ScareDeadPlayers", false, "Also trigger jumpscares while you are dead / spectating.");
			EnableTestHotkey = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "EnableTestHotkey", false, "Press F8 in game to trigger the jumpscare on demand. For testing your setup.");
			PluginDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? string.Empty;
			Log.LogInfo((object)("Plugin directory: " + PluginDirectory));
			try
			{
				AssetLoader.Load();
				new Harmony("kjelpw.FoxyJumpscare").PatchAll(Assembly.GetExecutingAssembly());
				Log.LogInfo((object)"FoxyJumpscare 1.0.1 loaded. Foxy is waiting.");
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"FoxyJumpscare failed to start and will stay dormant: {arg}");
			}
		}
	}
}