Decompiled source of CreepyHallways v1.1.0

CreepyHallways.dll

Decompiled a day ago
using 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);
	}
}