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 CreepyHallways v1.1.0
CreepyHallways.dll
Decompiled a day agousing System; using System.Collections.Generic; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using Unity.Collections; using Unity.Netcode; using UnityEngine; using UnityEngine.AI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: AssemblyVersion("0.0.0.0")] namespace CreepyHallways; internal static class Ambience { private class Schedule { internal float NextShriek; internal float NextFootstep; internal float LastEvent = -100f; internal float FollowUpTime = -1f; internal Vector3 FollowUpFrom; } private const float MinGapPerPlayer = 8f; private const float MinGapOverall = 3f; private const int MaxWalkers = 2; private static readonly Dictionary<PlayerControllerB, Schedule> schedules = new Dictionary<PlayerControllerB, Schedule>(); private static readonly List<PhantomWalker> walkers = new List<PhantomWalker>(); private static readonly List<PlayerControllerB> inside = new List<PlayerControllerB>(); private static float lastEventOverall = -100f; internal static void HostTick() { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null || instance.inShipPhase || !instance.shipHasLanded || instance.allPlayerScripts == null) { schedules.Clear(); return; } inside.Clear(); PlayerControllerB[] allPlayerScripts = instance.allPlayerScripts; foreach (PlayerControllerB val in allPlayerScripts) { if (IsInside(val)) { inside.Add(val); } } List<PlayerControllerB> list = new List<PlayerControllerB>(); foreach (PlayerControllerB key in schedules.Keys) { if (!inside.Contains(key)) { list.Add(key); } } foreach (PlayerControllerB item in list) { schedules.Remove(item); } walkers.RemoveAll((PhantomWalker w) => (Object)(object)w == (Object)null); foreach (PlayerControllerB item2 in inside) { if (!schedules.TryGetValue(item2, out var value)) { value = new Schedule(); value.NextShriek = Time.time + Interval(Plugin.ShriekIntervalMin.Value, Plugin.ShriekIntervalMax.Value); value.NextFootstep = Time.time + Interval(Plugin.FootstepIntervalMin.Value, Plugin.FootstepIntervalMax.Value); schedules[item2] = value; } else { TickPlayer(item2, value); } } } private static void TickPlayer(PlayerControllerB player, Schedule schedule) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) float time = Time.time; bool flag = time - schedule.LastEvent < 8f || time - lastEventOverall < 3f; if (schedule.FollowUpTime > 0f && time >= schedule.FollowUpTime) { schedule.FollowUpTime = -1f; if (StartFootsteps(player, schedule.FollowUpFrom, running: true, Random.value < 0.5f)) { MarkEvent(schedule); } } else if (Plugin.ShrieksEnabled.Value && time >= schedule.NextShriek) { if (flag || HasWalker(player)) { schedule.NextShriek = time + Random.Range(4f, 10f); } else if (TryShriek(player, schedule)) { MarkEvent(schedule); schedule.NextShriek = time + Interval(Plugin.ShriekIntervalMin.Value, Plugin.ShriekIntervalMax.Value); } else { schedule.NextShriek = time + 5f; } } else if (Plugin.FootstepsEnabled.Value && time >= schedule.NextFootstep) { if (flag || HasWalker(player) || walkers.Count >= 2 || schedule.FollowUpTime > 0f) { schedule.NextFootstep = time + Random.Range(4f, 10f); } else if (TryFootsteps(player)) { MarkEvent(schedule); schedule.NextFootstep = time + Interval(Plugin.FootstepIntervalMin.Value, Plugin.FootstepIntervalMax.Value); } else { schedule.NextFootstep = time + 5f; } } } internal static void StopAll() { foreach (PhantomWalker walker in walkers) { if ((Object)(object)walker != (Object)null) { Object.Destroy((Object)(object)((Component)walker).gameObject); } } walkers.Clear(); schedules.Clear(); Sounds.StopAll(); } internal static bool IsInside(PlayerControllerB player) { if ((Object)(object)player != (Object)null && player.isPlayerControlled && !player.isPlayerDead) { return player.isInsideFactory; } return false; } internal static Vector3 Eye(PlayerControllerB player) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)player.gameplayCamera != (Object)null)) { return ((Component)player).transform.position + Vector3.up * 1.6f; } return ((Component)player.gameplayCamera).transform.position; } internal static bool AnyoneCanSee(Vector3 point, float within) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0031: 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_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) int collidersAndRoomMaskAndDefault = StartOfRound.Instance.collidersAndRoomMaskAndDefault; PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; foreach (PlayerControllerB player in allPlayerScripts) { if (IsInside(player)) { Vector3 val = Eye(player); float num = Vector3.Distance(val, point); if (num < 6f || (num < within && !Physics.Linecast(val, point, collidersAndRoomMaskAndDefault, (QueryTriggerInteraction)1))) { return true; } } } return false; } private static void MarkEvent(Schedule schedule) { schedule.LastEvent = Time.time; lastEventOverall = Time.time; } private static bool HasWalker(PlayerControllerB player) { foreach (PhantomWalker walker in walkers) { if ((Object)(object)walker != (Object)null && (Object)(object)walker.Target == (Object)(object)player) { return true; } } return false; } private static float Interval(float min, float max) { min = Mathf.Max(5f, min); return Random.Range(min, Mathf.Max(min, max)); } private static bool TryShriek(PlayerControllerB player, Schedule schedule) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0036: 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) //IL_008a: Unknown result type (might be due to invalid IL or missing references) if (!FindHiddenNode(player, Plugin.ShriekDistanceMin.Value, Plugin.ShriekDistanceMax.Value, out var position)) { return false; } position += Vector3.up * 1.6f; Net.BroadcastShriek(position, SoundBank.RandomShriekIndex(), Random.Range(0.85f, 1.12f)); if (Plugin.FootstepsEnabled.Value && Random.value < Plugin.FootstepsAfterShriekChance.Value) { schedule.FollowUpTime = Time.time + Random.Range(1.5f, 4f); schedule.FollowUpFrom = position; } return true; } private static bool TryFootsteps(PlayerControllerB player) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) if (!FindHiddenNode(player, Plugin.FootstepDistanceMin.Value, Plugin.FootstepDistanceMax.Value, out var position)) { return false; } bool running = Random.value < 0.2f; bool approach = Random.value < Plugin.ApproachChance.Value; return StartFootsteps(player, position, running, approach); } private static bool StartFootsteps(PlayerControllerB player, Vector3 near, bool running, bool approach) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown //IL_003e: 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) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Expected O, but got Unknown //IL_0069: Unknown result type (might be due to invalid IL or missing references) NavMeshHit val = default(NavMeshHit); if (!NavMesh.SamplePosition(near, ref val, 4f, -1)) { return false; } Vector3 position = ((NavMeshHit)(ref val)).position; Vector3 target; if (approach) { target = ((Component)player).transform.position; } else if (!FindWanderTarget(position, player, out target)) { return false; } NavMeshPath val2 = new NavMeshPath(); if (!NavMesh.CalculatePath(position, target, -1, val2) || val2.corners.Length < 2) { return false; } GameObject val3 = new GameObject("CreepyHallways Walker"); val3.transform.position = position; PhantomWalker phantomWalker = val3.AddComponent<PhantomWalker>(); phantomWalker.Begin(player, val2.corners, running); walkers.Add(phantomWalker); return true; } private static bool FindHiddenNode(PlayerControllerB player, float minDistance, float maxDistance, out Vector3 position) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: 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) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_0098: 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_00af: Unknown result type (might be due to invalid IL or missing references) position = Vector3.zero; GameObject[] array = InsideNodes(); if (array == null || array.Length == 0) { return false; } List<Vector3> list = new List<Vector3>(); for (int i = 0; i < 2; i++) { if (list.Count != 0) { break; } float num = ((i == 0) ? maxDistance : (maxDistance * 1.6f)); float num2 = ((i == 0) ? minDistance : (minDistance * 0.6f)); GameObject[] array2 = array; foreach (GameObject val in array2) { if (!((Object)(object)val == (Object)null)) { Vector3 position2 = val.transform.position; float num3 = Vector3.Distance(position2, ((Component)player).transform.position); if (num3 >= num2 && num3 <= num && !TooCloseToAnyone(position2, num2) && !AnyoneCanSee(position2 + Vector3.up, 200f)) { list.Add(position2); } } } } if (list.Count == 0) { return false; } position = list[Random.Range(0, list.Count)]; return true; } private static bool TooCloseToAnyone(Vector3 point, float minDistance) { //IL_0015: 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) foreach (PlayerControllerB item in inside) { if (Vector3.Distance(point, ((Component)item).transform.position) < minDistance) { return true; } } return false; } private static bool FindWanderTarget(Vector3 start, PlayerControllerB player, out Vector3 target) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: 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_0076: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) target = Vector3.zero; GameObject[] array = InsideNodes(); if (array == null) { return false; } float num = Vector3.Distance(start, ((Component)player).transform.position); List<Vector3> list = new List<Vector3>(); GameObject[] array2 = array; foreach (GameObject val in array2) { if (!((Object)(object)val == (Object)null)) { Vector3 position = val.transform.position; float num2 = Vector3.Distance(position, start); if (num2 > 6f && num2 < 24f && Vector3.Distance(position, ((Component)player).transform.position) > num * 0.7f) { list.Add(position); } } } if (list.Count == 0) { return false; } target = list[Random.Range(0, list.Count)]; return true; } private static GameObject[] InsideNodes() { if ((Object)(object)RoundManager.Instance != (Object)null && RoundManager.Instance.insideAINodes != null && RoundManager.Instance.insideAINodes.Length > 0) { return RoundManager.Instance.insideAINodes; } return GameObject.FindGameObjectsWithTag("AINode"); } } internal class PhantomWalker : MonoBehaviour { internal PlayerControllerB Target; private Vector3[] corners; private int cornerIndex; private float speed; private float stride; private float sinceStep; private float endTime; internal void Begin(PlayerControllerB target, Vector3[] path, bool running) { Target = target; corners = path; cornerIndex = 1; speed = (running ? Random.Range(5.5f, 7f) : Random.Range(2.6f, 3.6f)); stride = (running ? 2.1f : 1.35f); sinceStep = stride; endTime = Time.time + Random.Range(6f, 14f); } private void Update() { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: 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) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) if (!Ambience.IsInside(Target) || Time.time > endTime || cornerIndex >= corners.Length || Ambience.AnyoneCanSee(((Component)this).transform.position + Vector3.up * 1.2f, 25f)) { Object.Destroy((Object)(object)((Component)this).gameObject); return; } Vector3 val = corners[cornerIndex]; float num = speed * Time.deltaTime; float num2 = Vector3.Distance(((Component)this).transform.position, val); if (num2 <= num) { ((Component)this).transform.position = val; cornerIndex++; } else { ((Component)this).transform.position = Vector3.MoveTowards(((Component)this).transform.position, val, num); } sinceStep += Mathf.Min(num, num2); if (sinceStep >= stride) { sinceStep = 0f; Net.BroadcastStep(((Component)this).transform.position); } } } internal static class Net { private const string ShriekMessage = "CreepyHallways.Shriek"; private const string StepMessage = "CreepyHallways.Step"; private static CustomMessagingManager registeredWith; internal static bool IsHost { get { if ((Object)(object)NetworkManager.Singleton != (Object)null) { return NetworkManager.Singleton.IsServer; } return false; } } internal static void OnLobbyStarted() { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Expected O, but got Unknown //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Expected O, but got Unknown NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null) && singleton.CustomMessagingManager != null && singleton.CustomMessagingManager != registeredWith) { registeredWith = singleton.CustomMessagingManager; registeredWith.RegisterNamedMessageHandler("CreepyHallways.Shriek", new HandleNamedMessageDelegate(OnShriekReceived)); registeredWith.RegisterNamedMessageHandler("CreepyHallways.Step", new HandleNamedMessageDelegate(OnStepReceived)); } } internal static void OnLobbyEnded() { if (registeredWith != null) { try { registeredWith.UnregisterNamedMessageHandler("CreepyHallways.Shriek"); registeredWith.UnregisterNamedMessageHandler("CreepyHallways.Step"); } catch (Exception) { } registeredWith = null; } } internal static void BroadcastShriek(Vector3 position, int clipIndex, float pitch) { //IL_0009: 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) byte[] array = new byte[20]; WritePosition(array, position); Buffer.BlockCopy(BitConverter.GetBytes(clipIndex), 0, array, 12, 4); Buffer.BlockCopy(BitConverter.GetBytes(pitch), 0, array, 16, 4); SendToOthers("CreepyHallways.Shriek", array); Sounds.PlayShriek(position, clipIndex, pitch); } internal static void BroadcastStep(Vector3 position) { //IL_0009: 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) byte[] payload = new byte[12]; WritePosition(payload, position); SendToOthers("CreepyHallways.Step", payload); Sounds.PlayStep(position); } private static void OnShriekReceived(ulong senderClientId, FastBufferReader reader) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) byte[] array = Read(senderClientId, reader, 20); if (array != null) { Sounds.PlayShriek(ReadPosition(array), BitConverter.ToInt32(array, 12), BitConverter.ToSingle(array, 16)); } } private static void OnStepReceived(ulong senderClientId, FastBufferReader reader) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) byte[] array = Read(senderClientId, reader, 12); if (array != null) { Sounds.PlayStep(ReadPosition(array)); } } private static void SendToOthers(string message, byte[] payload) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || singleton.CustomMessagingManager == null) { return; } FastBufferWriter val = default(FastBufferWriter); foreach (ulong connectedClientsId in singleton.ConnectedClientsIds) { if (connectedClientsId != singleton.LocalClientId) { ((FastBufferWriter)(ref val))..ctor(payload.Length, (Allocator)2, -1); try { ((FastBufferWriter)(ref val)).WriteBytesSafe(payload, payload.Length, 0); singleton.CustomMessagingManager.SendNamedMessage(message, connectedClientsId, val, (NetworkDelivery)3); } finally { ((FastBufferWriter)(ref val)).Dispose(); } } } } private static byte[] Read(ulong senderClientId, FastBufferReader reader, int size) { if (senderClientId != 0 || ((FastBufferReader)(ref reader)).Length - ((FastBufferReader)(ref reader)).Position < size) { return null; } byte[] result = new byte[size]; ((FastBufferReader)(ref reader)).ReadBytesSafe(ref result, size, 0); return result; } private static void WritePosition(byte[] payload, Vector3 position) { Buffer.BlockCopy(BitConverter.GetBytes(position.x), 0, payload, 0, 4); Buffer.BlockCopy(BitConverter.GetBytes(position.y), 0, payload, 4, 4); Buffer.BlockCopy(BitConverter.GetBytes(position.z), 0, payload, 8, 4); } private static Vector3 ReadPosition(byte[] payload) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) return new Vector3(BitConverter.ToSingle(payload, 0), BitConverter.ToSingle(payload, 4), BitConverter.ToSingle(payload, 8)); } } [HarmonyPatch(typeof(PlayerControllerB), "Update")] internal static class PlayerUpdatePatch { private static void Postfix(PlayerControllerB __instance) { if (Net.IsHost && (Object)(object)GameNetworkManager.Instance != (Object)null && (Object)(object)__instance == (Object)(object)GameNetworkManager.Instance.localPlayerController) { Ambience.HostTick(); } } } [HarmonyPatch(typeof(StartOfRound))] internal static class StartOfRoundPatches { [HarmonyPostfix] [HarmonyPatch("Start")] private static void LobbyStarted() { Net.OnLobbyStarted(); } [HarmonyPatch("ShipLeave")] [HarmonyPostfix] private static void ShipLeft() { Ambience.StopAll(); } [HarmonyPostfix] [HarmonyPatch("OnDestroy")] private static void LobbyEnded() { Ambience.StopAll(); Net.OnLobbyEnded(); } } [BepInPlugin("axelc.creepyhallways", "CreepyHallways", "1.1.0")] public class Plugin : BaseUnityPlugin { public const string Guid = "axelc.creepyhallways"; public const string PluginName = "CreepyHallways"; public const string Version = "1.1.0"; internal static ManualLogSource Log; internal static string PluginDir; internal static ConfigEntry<bool> ShrieksEnabled; internal static ConfigEntry<float> ShriekIntervalMin; internal static ConfigEntry<float> ShriekIntervalMax; internal static ConfigEntry<float> ShriekDistanceMin; internal static ConfigEntry<float> ShriekDistanceMax; internal static ConfigEntry<float> ShriekVolume; internal static ConfigEntry<bool> FootstepsEnabled; internal static ConfigEntry<float> FootstepIntervalMin; internal static ConfigEntry<float> FootstepIntervalMax; internal static ConfigEntry<float> FootstepDistanceMin; internal static ConfigEntry<float> FootstepDistanceMax; internal static ConfigEntry<float> FootstepVolume; internal static ConfigEntry<float> ApproachChance; internal static ConfigEntry<float> FootstepsAfterShriekChance; internal static ConfigEntry<bool> UseBuiltInShrieks; internal static ConfigEntry<bool> UseGameFootsteps; private void Awake() { //IL_025a: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; PluginDir = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location); ShrieksEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Shrieks", "Enabled", true, "Distant shrieks echo through the facility hallways. Only the host's settings for how often, how far and how likely things happen are used; Volume is per player."); ShriekIntervalMin = ((BaseUnityPlugin)this).Config.Bind<float>("Shrieks", "MinSecondsBetween", 40f, "Shortest wait between shrieks for each player inside."); ShriekIntervalMax = ((BaseUnityPlugin)this).Config.Bind<float>("Shrieks", "MaxSecondsBetween", 130f, "Longest wait between shrieks for each player inside."); ShriekDistanceMin = ((BaseUnityPlugin)this).Config.Bind<float>("Shrieks", "MinDistance", 22f, "Closest a shriek can come from, in meters."); ShriekDistanceMax = ((BaseUnityPlugin)this).Config.Bind<float>("Shrieks", "MaxDistance", 60f, "Farthest a shriek can come from, in meters."); ShriekVolume = ((BaseUnityPlugin)this).Config.Bind<float>("Shrieks", "Volume", 0.85f, "Shriek loudness on your machine, 0 to 1."); FootstepsEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Footsteps", "Enabled", true, "Footsteps walk around behind walls and around corners where nobody is."); FootstepIntervalMin = ((BaseUnityPlugin)this).Config.Bind<float>("Footsteps", "MinSecondsBetween", 30f, "Shortest wait between phantom footsteps for each player inside."); FootstepIntervalMax = ((BaseUnityPlugin)this).Config.Bind<float>("Footsteps", "MaxSecondsBetween", 90f, "Longest wait between phantom footsteps for each player inside."); FootstepDistanceMin = ((BaseUnityPlugin)this).Config.Bind<float>("Footsteps", "MinDistance", 10f, "Closest the footsteps can start, in meters."); FootstepDistanceMax = ((BaseUnityPlugin)this).Config.Bind<float>("Footsteps", "MaxDistance", 32f, "Farthest the footsteps can start, in meters."); FootstepVolume = ((BaseUnityPlugin)this).Config.Bind<float>("Footsteps", "Volume", 0.9f, "Footstep loudness on your machine, 0 to 1."); ApproachChance = ((BaseUnityPlugin)this).Config.Bind<float>("Footsteps", "ApproachChance", 0.35f, "Chance (0 to 1) the footsteps walk toward a player instead of wandering. They always stop before anyone could see them."); FootstepsAfterShriekChance = ((BaseUnityPlugin)this).Config.Bind<float>("Footsteps", "AfterShriekChance", 0.3f, "Chance (0 to 1) that something comes running from where a shriek came from."); UseBuiltInShrieks = ((BaseUnityPlugin)this).Config.Bind<bool>("Sounds", "UseBuiltInShrieks", true, "Use the mod's generated shrieks. Put your own .wav files in Sounds/Shrieks next to the DLL to add more; turn this off to hear only yours."); UseGameFootsteps = ((BaseUnityPlugin)this).Config.Bind<bool>("Sounds", "UseGameFootsteps", true, "Use the game's own player footstep sounds, matched to the floor they're on. Your own .wav files in Sounds/Footsteps replace them."); SoundBank.Load(); new Harmony("axelc.creepyhallways").PatchAll(); Log.LogInfo((object)"CreepyHallways 1.1.0 loaded."); } } internal static class SoundBank { private static readonly List<AudioClip> shrieks = new List<AudioClip>(); private static readonly List<AudioClip> customFootsteps = new List<AudioClip>(); private static readonly List<AudioClip> synthFootsteps = new List<AudioClip>(); private static int lastShriek = -1; internal static void Load() { string path = Path.Combine(Plugin.PluginDir, "Sounds"); LoadFolder(Path.Combine(path, "Shrieks"), shrieks); LoadFolder(Path.Combine(path, "Footsteps"), customFootsteps); int count = shrieks.Count; if (Plugin.UseBuiltInShrieks.Value || shrieks.Count == 0) { for (int i = 0; i < 10; i++) { shrieks.Add(ToClip("CreepyHallways_Shriek" + i, Synth.ShriekSamples(7919 * (i + 1)))); } } for (int j = 0; j < 6; j++) { synthFootsteps.Add(ToClip("CreepyHallways_Step" + j, Synth.FootstepSamples(104729 * (j + 1)))); } Plugin.Log.LogInfo((object)("Shrieks: " + shrieks.Count + " (" + count + " custom). Custom footsteps: " + customFootsteps.Count + ".")); } internal static int RandomShriekIndex() { if (shrieks.Count == 0) { return 0; } int num = Random.Range(0, shrieks.Count); if (shrieks.Count > 1 && num == lastShriek) { num = (num + 1 + Random.Range(0, shrieks.Count - 1)) % shrieks.Count; } lastShriek = num; return num; } internal static AudioClip Shriek(int index) { if (shrieks.Count == 0) { return null; } return shrieks[(index % shrieks.Count + shrieks.Count) % shrieks.Count]; } internal static AudioClip RandomFootstep(Vector3 position) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) if (customFootsteps.Count > 0) { return customFootsteps[Random.Range(0, customFootsteps.Count)]; } if (Plugin.UseGameFootsteps.Value) { AudioClip[] array = GameFootstepsAt(position); if (array != null && array.Length > 0) { return array[Random.Range(0, array.Length)]; } } if (synthFootsteps.Count <= 0) { return null; } return synthFootsteps[Random.Range(0, synthFootsteps.Count)]; } private static AudioClip[] GameFootstepsAt(Vector3 position) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: 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_0038: Unknown result type (might be due to invalid IL or missing references) StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null || instance.footstepSurfaces == null || instance.footstepSurfaces.Length == 0) { return null; } RaycastHit val = default(RaycastHit); if (Physics.Raycast(position + Vector3.up * 0.5f, Vector3.down, ref val, 3f, instance.collidersAndRoomMaskAndDefault, (QueryTriggerInteraction)1)) { FootstepSurface[] footstepSurfaces = instance.footstepSurfaces; foreach (FootstepSurface val2 in footstepSurfaces) { if (val2 != null && !string.IsNullOrEmpty(val2.surfaceTag) && ((Component)((RaycastHit)(ref val)).collider).CompareTag(val2.surfaceTag)) { return val2.clips; } } } FootstepSurface[] footstepSurfaces2 = instance.footstepSurfaces; foreach (FootstepSurface val3 in footstepSurfaces2) { if (val3 != null && val3.surfaceTag == "Concrete") { return val3.clips; } } if (instance.footstepSurfaces[0] == null) { return null; } return instance.footstepSurfaces[0].clips; } private static AudioClip ToClip(string name, float[] samples) { AudioClip val = AudioClip.Create(name, samples.Length, 1, 44100, false); val.SetData(samples, 0); return val; } private static void LoadFolder(string dir, List<AudioClip> into) { if (!Directory.Exists(dir)) { try { Directory.CreateDirectory(dir); return; } catch (Exception) { return; } } string[] files = Directory.GetFiles(dir, "*.wav"); foreach (string path in files) { try { into.Add(WavLoader.Load(path)); } catch (Exception ex2) { Plugin.Log.LogWarning((object)("Couldn't load " + Path.GetFileName(path) + ": " + ex2.Message)); } } } } internal static class Sounds { private static readonly List<GameObject> playing = new List<GameObject>(); internal static void PlayShriek(Vector3 position, int clipIndex, float pitch) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) AudioClip val = SoundBank.Shriek(clipIndex); if (!((Object)(object)val == (Object)null)) { GameObject val2 = Create("CreepyHallways Shriek", position, 8f, 110f, Plugin.ShriekVolume.Value, (AudioReverbPreset)14); AudioSource component = val2.GetComponent<AudioSource>(); component.pitch = pitch; component.clip = val; component.Play(); Object.Destroy((Object)(object)val2, val.length / pitch + 4f); } } internal static void PlayStep(Vector3 position) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) AudioClip val = SoundBank.RandomFootstep(position); if (!((Object)(object)val == (Object)null)) { GameObject val2 = Create("CreepyHallways Step", position + Vector3.up * 0.2f, 3f, 50f, Plugin.FootstepVolume.Value, (AudioReverbPreset)13); AudioSource component = val2.GetComponent<AudioSource>(); component.pitch = Random.Range(0.9f, 1.08f); component.PlayOneShot(val, Random.Range(0.75f, 1f)); Object.Destroy((Object)(object)val2, val.length + 3f); } } internal static void StopAll() { foreach (GameObject item in playing) { if ((Object)(object)item != (Object)null) { Object.Destroy((Object)(object)item); } } playing.Clear(); } private static GameObject Create(string name, Vector3 position, float minDistance, float maxDistance, float volume, AudioReverbPreset preset) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name); val.transform.position = position; AudioSource val2 = val.AddComponent<AudioSource>(); val2.playOnAwake = false; val2.spatialBlend = 1f; val2.rolloffMode = (AudioRolloffMode)0; val2.minDistance = minDistance; val2.maxDistance = maxDistance; val2.dopplerLevel = 0f; val2.spread = 40f; val2.volume = Mathf.Clamp01(volume); PlayerControllerB val3 = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : null); if ((Object)(object)val3 != (Object)null && (Object)(object)val3.movementAudio != (Object)null) { val2.outputAudioMixerGroup = val3.movementAudio.outputAudioMixerGroup; } val.AddComponent<AudioReverbFilter>().reverbPreset = preset; Muffle(val.AddComponent<AudioLowPassFilter>(), position); playing.RemoveAll((GameObject g) => (Object)(object)g == (Object)null); playing.Add(val); return val; } private static void Muffle(AudioLowPassFilter lowPass, Vector3 position) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0006: 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_002d: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) Vector3 val = ListenerPosition(); bool flag = Physics.Linecast(val, position, StartOfRound.Instance.collidersAndRoomMaskAndDefault, (QueryTriggerInteraction)1); float num = Mathf.Lerp(9000f, 3000f, Mathf.InverseLerp(10f, 70f, Vector3.Distance(val, position))); lowPass.cutoffFrequency = (flag ? (num * 0.45f) : num); } private static Vector3 ListenerPosition() { //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance != (Object)null && (Object)(object)instance.audioListener != (Object)null) { return ((Component)instance.audioListener).transform.position; } PlayerControllerB localPlayerController = GameNetworkManager.Instance.localPlayerController; if (!((Object)(object)localPlayerController != (Object)null)) { return Vector3.zero; } return ((Component)localPlayerController.gameplayCamera).transform.position; } } internal static class Synth { internal const int SampleRate = 44100; private const double TwoPi = Math.PI * 2.0; internal static float[] ShriekSamples(int seed) { Random random = new Random(seed); float num = Range(random, 1.3f, 2.6f); int num2 = (int)(num * 44100f); float[] array = new float[num2]; float num3 = Range(random, 650f, 1100f); float num4 = num3 * Range(random, 1.35f, 1.9f); float b = num3 * Range(random, 0.45f, 0.8f); float num5 = Range(random, 0.08f, 0.25f); float num6 = Range(random, 5f, 11f); float num7 = Range(random, 0.015f, 0.05f); float num8 = Range(random, 28f, 65f); float num9 = Range(random, 0.25f, 0.6f); float num10 = Range(random, 1.004f, 1.012f); float num11 = ((random.NextDouble() < 0.5) ? 1.414f : 1.488f); float num12 = Range(random, 0.15f, 0.35f); float num13 = ((random.NextDouble() < 0.5) ? Range(random, 0.4f, 0.7f) : 2f); double num14 = random.NextDouble() * (Math.PI * 2.0); double num15 = 0.0; double num16 = 0.0; double num17 = 0.0; double num18 = 0.0; float num19 = 0f; float num20 = 0f; float num21 = 0f; for (int i = 0; i < num2; i++) { float num22 = (float)i / 44100f; float num23 = num22 / num; float num24 = ((num23 < num5) ? Lerp(num3, num4, SmoothStep(num23 / num5)) : Lerp(num4, b, (float)Math.Pow((num23 - num5) / (1f - num5), 1.8))); if (num23 > num13) { num24 *= 1.25f; } double num25 = 1.0 + 0.02 * Math.Sin(8.168140899333462 * (double)num22 + num14) + 0.015 * Math.Sin(18.2212373908208 * (double)num22); double num26 = 1.0 + (double)num7 * (0.3 + 0.7 * (double)num23) * Math.Sin(Math.PI * 2.0 * (double)num6 * (double)num22); double num27 = (double)num24 * num25 * num26; num15 += num27 / 44100.0; num16 += num27 * (double)num10 / 44100.0; num17 += num27 * (double)num11 / 44100.0; num18 += (double)num8 / 44100.0; float num28 = Saw(num15) * 0.5f + Saw(num16) * 0.4f + (float)Math.Sin(Math.PI * 2.0 * num17) * 0.22f; num19 += (num28 - num19) * 0.35f; float num29 = 1f - num9 * 0.5f * (1f + (float)Math.Sin(Math.PI * 2.0 * num18)); float num30 = (float)(random.NextDouble() * 2.0 - 1.0); num20 += (num30 - num20) * 0.45f; num21 += (num30 - num21) * 0.08f; float num31 = (num20 - num21) * num12 * (0.6f + 0.8f * num23); float num32 = Math.Min(1f, num22 / 0.035f); float num33 = ((num23 > 0.6f) ? ((float)Math.Pow(1f - (num23 - 0.6f) / 0.4f, 2.0)) : 1f); float num34 = (num19 * num29 + num31) * num32 * num33; array[i] = (float)Math.Tanh(num34 * 2.2f); } Normalize(array, 0.9f); return array; } internal static float[] FootstepSamples(int seed) { Random random = new Random(seed); int num = 14111; float[] array = new float[num]; float num2 = Range(random, 95f, 135f); float num3 = Range(random, 0.035f, 0.06f); float num4 = Range(random, 0.12f, 0.3f); double num5 = 0.0; double num6 = 0.0; float num7 = 0f; float num8 = 0f; float num9 = 0f; for (int i = 0; i < num; i++) { float num10 = (float)i / 44100f; float num11 = (float)(random.NextDouble() * 2.0 - 1.0); double num12 = (double)num2 * (0.45 + 0.55 * Math.Exp((0f - num10) * 30f)); num5 += num12 / 44100.0; float num13 = (float)Math.Sin(Math.PI * 2.0 * num5) * (float)Math.Exp((0f - num10) * 38f); num7 += (num11 - num7) * 0.6f; num8 += (num11 - num8) * 0.12f; float num14 = (num7 - num8) * (float)Math.Exp((0f - num10) * 160f) * 0.8f; float num15 = 0f; if (num10 > num3) { float num16 = num10 - num3; num6 += (double)num2 * 1.3 / 44100.0; num15 = ((float)Math.Sin(Math.PI * 2.0 * num6) * 0.35f + (num7 - num8) * 0.4f) * (float)Math.Exp((0f - num16) * 70f); } num9 += (num11 - num9) * 0.05f; float num17 = ((num10 > 0.015f) ? (num9 * num4 * (float)Math.Exp((0f - (num10 - 0.015f)) * 20f)) : 0f); array[i] = num13 + num14 + num15 + num17; } Normalize(array, 0.85f); return array; } private static float Saw(double phase) { return (float)(2.0 * (phase - Math.Floor(phase)) - 1.0); } private static float Range(Random rng, float min, float max) { return min + (float)rng.NextDouble() * (max - min); } private static float Lerp(float a, float b, float t) { return a + (b - a) * t; } private static float SmoothStep(float t) { return t * t * (3f - 2f * t); } private static void Normalize(float[] samples, float peak) { float num = 0f; foreach (float value in samples) { num = Math.Max(num, Math.Abs(value)); } if (!(num <= 0f)) { float num2 = peak / num; for (int j = 0; j < samples.Length; j++) { samples[j] *= num2; } } } } internal static class WavLoader { internal static AudioClip Load(string path) { byte[] array = File.ReadAllBytes(path); if (array.Length < 12 || Tag(array, 0) != "RIFF" || Tag(array, 8) != "WAVE") { throw new InvalidDataException("not a WAV file"); } int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; int num5 = -1; int num6 = 0; int num7 = 12; while (num7 + 8 <= array.Length) { string text = Tag(array, num7); int num8 = BitConverter.ToInt32(array, num7 + 4); int num9 = num7 + 8; if (text == "fmt ") { num = BitConverter.ToUInt16(array, num9); num2 = BitConverter.ToUInt16(array, num9 + 2); num3 = BitConverter.ToInt32(array, num9 + 4); num4 = BitConverter.ToUInt16(array, num9 + 14); if (num == 65534 && num8 >= 26) { num = BitConverter.ToUInt16(array, num9 + 24); } } else if (text == "data") { num5 = num9; num6 = Math.Min(num8, array.Length - num9); break; } num7 = num9 + num8 + (num8 & 1); } if (num5 < 0 || num2 < 1 || num3 < 1) { throw new InvalidDataException("missing fmt or data chunk"); } if (num != 1 && num != 3) { throw new InvalidDataException("compressed WAV (format " + num + "); save it as PCM"); } int num10 = num4 / 8; int num11 = num6 / (num10 * num2); float[] array2 = new float[num11]; for (int i = 0; i < num11; i++) { float num12 = 0f; for (int j = 0; j < num2; j++) { num12 += ReadSample(array, num5 + (i * num2 + j) * num10, num4, num == 3); } array2[i] = num12 / (float)num2; } AudioClip val = AudioClip.Create(Path.GetFileNameWithoutExtension(path), num11, 1, num3, false); val.SetData(array2, 0); return val; } private static float ReadSample(byte[] b, int i, int bits, bool isFloat) { if (isFloat) { if (bits != 64) { return BitConverter.ToSingle(b, i); } return (float)BitConverter.ToDouble(b, i); } return bits switch { 8 => (float)(b[i] - 128) / 128f, 16 => (float)BitConverter.ToInt16(b, i) / 32768f, 24 => (float)(((b[i] << 8) | (b[i + 1] << 16) | (b[i + 2] << 24)) >> 8) / 8388608f, 32 => (float)BitConverter.ToInt32(b, i) / 2.1474836E+09f, _ => throw new InvalidDataException(bits + "-bit audio isn't supported"), }; } private static string Tag(byte[] b, int i) { return Encoding.ASCII.GetString(b, i, 4); } }