Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of FoxyJumpscare v1.0.1
FoxyJumpscare.dll
Decompiled 5 hours agousing 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}"); } } } }