using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using UnityEngine; using UnityEngine.Audio; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.Networking; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyVersion("0.0.0.0")] namespace ParanoidCompany; public class DarknessSensor { private Light[] _cached = (Light[])(object)new Light[0]; private float _nextScan; private bool _wasInsideFactory; private bool _dirty = true; private readonly HashSet _playerRoots = new HashSet(); private readonly List _keep = new List(256); private readonly HashSet _ownCarried = new HashSet(); public bool OwnLightActive { get; private set; } public void Invalidate() { _dirty = true; } private void Rescan() { _dirty = false; _nextScan = Time.time + Mathf.Max(2f, ModConfig.LightScanInterval.Value); _playerRoots.Clear(); StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance != (Object)null && instance.allPlayerScripts != null) { for (int i = 0; i < instance.allPlayerScripts.Length; i++) { PlayerControllerB val = instance.allPlayerScripts[i]; if ((Object)(object)val != (Object)null) { _playerRoots.Add(((Component)val).transform); } } } RescanCarriedLights(((Object)(object)instance != (Object)null) ? instance.localPlayerController : null); Light[] array = Object.FindObjectsOfType(); _keep.Clear(); foreach (Light val2 in array) { if (!((Object)(object)val2 == (Object)null) && !IsUnderPlayer(((Component)val2).transform, _playerRoots) && !_ownCarried.Contains(val2)) { _keep.Add(val2); } } _cached = _keep.ToArray(); if (ModConfig.DebugLogging.Value) { Plugin.Log.LogInfo((object)("Light scan: " + _cached.Length + " world light(s) tracked.")); } } private static bool IsUnderPlayer(Transform t, HashSet playerRoots) { if (playerRoots.Count == 0) { return false; } int num = 0; while ((Object)(object)t != (Object)null && num < 12) { if (playerRoots.Contains(t)) { return true; } t = t.parent; num++; } return false; } private void RescanCarriedLights(PlayerControllerB me) { _ownCarried.Clear(); if ((Object)(object)me == (Object)null) { return; } FlashlightItem[] array = Object.FindObjectsOfType(); if (array == null) { return; } foreach (FlashlightItem val in array) { if (!((Object)(object)val == (Object)null) && ((GrabbableObject)val).isHeld && !((Object)(object)((GrabbableObject)val).playerHeldBy != (Object)(object)me)) { if ((Object)(object)val.flashlightBulb != (Object)null) { _ownCarried.Add(val.flashlightBulb); } if ((Object)(object)val.flashlightBulbGlow != (Object)null) { _ownCarried.Add(val.flashlightBulbGlow); } } } } public float Evaluate(PlayerControllerB local, Vector3 eyePos) { //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Invalid comparison between Unknown and I4 //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)local == (Object)null) { return 1f; } if (local.isInsideFactory != _wasInsideFactory) { _wasInsideFactory = local.isInsideFactory; _dirty = true; } if (_dirty || Time.time >= _nextScan) { Rescan(); } OwnLightActive = IsOwnLightOn(local); float value = ModConfig.LightMaxConsiderDistance.Value; float maxConsiderSqr = value * value; bool value2 = ModConfig.RequireLineOfSight.Value; int num = 0; if (value2 && (Object)(object)StartOfRound.Instance != (Object)null) { num = StartOfRound.Instance.collidersAndRoomMask; } float num2 = 0f; for (int i = 0; i < _cached.Length; i++) { Light val = _cached[i]; if ((Object)(object)val == (Object)null || !((Behaviour)val).isActiveAndEnabled || val.intensity <= 0f) { continue; } if ((int)val.type == 1) { if (ModConfig.DirectionalLightCountsAsLit.Value && !local.isInsideFactory && !local.isPlayerDead && val.intensity > 0.05f) { return 1f; } continue; } float num3 = ScoreLight(val, eyePos, maxConsiderSqr); if (!(num3 <= num2) && (!value2 || num == 0 || !Physics.Linecast(eyePos, ((Component)val).transform.position, num, (QueryTriggerInteraction)1))) { num2 = num3; if (num2 >= 0.999f) { break; } } } return Mathf.Max(num2, ScoreOtherPlayerLights(local, eyePos, maxConsiderSqr)); } private static float ScoreLight(Light l, Vector3 eyePos, float maxConsiderSqr) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000d: 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) //IL_0013: 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_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: 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) Vector3 position = ((Component)l).transform.position; Vector3 val = eyePos - position; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; if (sqrMagnitude > maxConsiderSqr) { return 0f; } float range = l.range; if (range <= 0.01f) { return 0f; } if (sqrMagnitude > range * range) { return 0f; } float num = Mathf.Sqrt(sqrMagnitude); if ((int)l.type == 0 && num > 0.01f) { float num2 = Vector3.Angle(((Component)l).transform.forward, val / num); float num3 = Mathf.Max(1f, l.spotAngle * 0.5f); if (num2 > num3) { return 0f; } } return 1f - num / range; } private static float ScoreOtherPlayerLights(PlayerControllerB local, Vector3 eyePos, float maxConsiderSqr) { //IL_007d: Unknown result type (might be due to invalid IL or missing references) StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null || instance.allPlayerScripts == null) { return 0f; } float num = 0f; for (int i = 0; i < instance.allPlayerScripts.Length; i++) { PlayerControllerB val = instance.allPlayerScripts[i]; if ((Object)(object)val == (Object)null || (Object)(object)val == (Object)(object)local || !val.isPlayerControlled || val.isPlayerDead) { continue; } Light helmetLight = val.helmetLight; if (!((Object)(object)helmetLight == (Object)null) && ((Behaviour)helmetLight).isActiveAndEnabled && !(helmetLight.intensity <= 0f)) { float num2 = ScoreLight(helmetLight, eyePos, maxConsiderSqr); if (num2 > num) { num = num2; } } } return num; } private static bool IsOwnLightOn(PlayerControllerB local) { if ((Object)(object)local == (Object)null) { return false; } if ((Object)(object)local.helmetLight != (Object)null && ((Behaviour)local.helmetLight).isActiveAndEnabled && local.helmetLight.intensity > 0f) { return true; } if (local.allHelmetLights != null) { for (int i = 0; i < local.allHelmetLights.Length; i++) { Light val = local.allHelmetLights[i]; if ((Object)(object)val != (Object)null && ((Behaviour)val).isActiveAndEnabled && val.intensity > 0f) { return true; } } } GrabbableObject currentlyHeldObject = local.currentlyHeldObject; FlashlightItem val2 = (FlashlightItem)(object)((currentlyHeldObject is FlashlightItem) ? currentlyHeldObject : null); if ((Object)(object)val2 == (Object)null) { GrabbableObject currentlyHeldObjectServer = local.currentlyHeldObjectServer; val2 = (FlashlightItem)(object)((currentlyHeldObjectServer is FlashlightItem) ? currentlyHeldObjectServer : null); } if ((Object)(object)val2 != (Object)null && ((GrabbableObject)val2).isBeingUsed) { return true; } return false; } } public static class DoorSensor { private static DoorLock[] _doors; private static float _nextScan; private static readonly List _candidates = new List(); private static readonly List _behind = new List(); private const float RescanSeconds = 20f; private const float EmptyRescanSeconds = 4f; public static void Invalidate() { _doors = null; _nextScan = 0f; _candidates.Clear(); _behind.Clear(); } private static void EnsureScan() { if (_doors == null || !(Time.time < _nextScan)) { _nextScan = Time.time + 20f; try { _doors = Object.FindObjectsOfType(); } catch { _doors = (DoorLock[])(object)new DoorLock[0]; } if (_doors == null) { _doors = (DoorLock[])(object)new DoorLock[0]; } if (_doors.Length == 0) { _nextScan = Time.time + 4f; } } } public static bool TryPickDoor(Vector3 eye, Vector3 lookForward, 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_009c: 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_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0159: 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_00cf: Unknown result type (might be due to invalid IL or missing references) position = Vector3.zero; EnsureScan(); if (_doors == null || _doors.Length == 0) { return false; } float value = ModConfig.DoorMinDistance.Value; float num = ModConfig.DoorMaxDistance.Value; if (num < value + 0.5f) { num = value + 0.5f; } _candidates.Clear(); _behind.Clear(); for (int i = 0; i < _doors.Length; i++) { DoorLock val = _doors[i]; if ((Object)(object)val == (Object)null) { continue; } Transform transform = ((Component)val).transform; if ((Object)(object)transform == (Object)null || !((Component)transform).gameObject.activeInHierarchy) { continue; } Vector3 val2 = transform.position - eye; float magnitude = ((Vector3)(ref val2)).magnitude; if (!(magnitude < value) && !(magnitude > num)) { _candidates.Add(transform); if (Vector3.Dot(((Vector3)(ref val2)).normalized, lookForward) < 0.35f) { _behind.Add(transform); } } } List list = ((_behind.Count > 0) ? _behind : _candidates); if (list.Count == 0) { return false; } Transform val3 = list[Random.Range(0, list.Count)]; if ((Object)(object)val3 == (Object)null) { return false; } position = val3.position + Vector3.up * 1.1f; return true; } } public class HallucinationPlayer : MonoBehaviour { private class Voice { public AudioSource Source; public AudioLowPassFilter LowPass; public AudioHighPassFilter HighPass; public AudioDistortionFilter Distortion; public bool Busy; public int Token; } private Voice[] _voices; private const float MaxHoldSeconds = 12f; public const int DefaultPriority = 110; public const int CriticalPriority = 0; public AudioMixerGroup MixerGroup { get; set; } public bool AnythingPlaying { get { if (_voices == null) { return false; } for (int i = 0; i < _voices.Length; i++) { Voice voice = _voices[i]; if (voice != null && !((Object)(object)voice.Source == (Object)null) && (voice.Busy || voice.Source.isPlaying)) { return true; } } return false; } } public void Build(int count) { //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) count = Mathf.Clamp(count, 1, 16); if (_voices != null && _voices.Length == count) { return; } if (_voices != null) { for (int i = 0; i < _voices.Length; i++) { Voice voice = _voices[i]; if (voice != null && (Object)(object)voice.Source != (Object)null && (voice.Busy || voice.Source.isPlaying)) { return; } } ((MonoBehaviour)this).StopAllCoroutines(); for (int j = 0; j < _voices.Length; j++) { if (_voices[j] != null && (Object)(object)_voices[j].Source != (Object)null) { Object.Destroy((Object)(object)((Component)_voices[j].Source).gameObject); } } } _voices = new Voice[count]; for (int k = 0; k < count; k++) { GameObject val = new GameObject("SchizoVoice_" + k); val.transform.SetParent(((Component)this).transform, false); AudioSource val2 = val.AddComponent(); val2.playOnAwake = false; val2.loop = false; val2.spatialBlend = 1f; val2.rolloffMode = (AudioRolloffMode)1; val2.dopplerLevel = 0f; val2.priority = 110; AudioLowPassFilter val3 = val.AddComponent(); ((Behaviour)val3).enabled = false; AudioHighPassFilter val4 = val.AddComponent(); ((Behaviour)val4).enabled = false; AudioDistortionFilter val5 = val.AddComponent(); ((Behaviour)val5).enabled = false; _voices[k] = new Voice { Source = val2, LowPass = val3, HighPass = val4, Distortion = val5 }; } } private Voice FindFreeVoice() { if (_voices == null) { return null; } for (int i = 0; i < _voices.Length; i++) { Voice voice = _voices[i]; if (voice != null && !((Object)(object)voice.Source == (Object)null) && !voice.Busy && !voice.Source.isPlaying) { return voice; } } return null; } private void Configure(Voice v, Vector3 pos, bool insideHead, float volume, float pitch, bool radioEffect, float falloffDistance, float lowPassCutoff, int priority = 110, bool dry = false) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) v.Token++; AudioSource source = v.Source; ((Component)source).transform.position = pos; source.spatialBlend = (insideHead ? 0f : 1f); source.minDistance = 2f; source.maxDistance = Mathf.Max(6f, falloffDistance); source.volume = Mathf.Clamp01(volume); source.pitch = Mathf.Clamp(pitch, 0.25f, 3f); source.outputAudioMixerGroup = (dry ? null : MixerGroup); source.priority = Mathf.Clamp(priority, 0, 256); if ((Object)(object)v.LowPass != (Object)null) { float num = (radioEffect ? 2600f : lowPassCutoff); ((Behaviour)v.LowPass).enabled = num < 19000f; v.LowPass.cutoffFrequency = Mathf.Clamp(num, 180f, 22000f); } if ((Object)(object)v.HighPass != (Object)null) { ((Behaviour)v.HighPass).enabled = radioEffect; if (radioEffect) { v.HighPass.cutoffFrequency = 520f; } } if ((Object)(object)v.Distortion != (Object)null) { ((Behaviour)v.Distortion).enabled = radioEffect; if (radioEffect) { v.Distortion.distortionLevel = 0.28f; } } } public bool Play(AudioClip clip, Vector3 worldPos, bool insideHead, float volume, float pitch, bool radioEffect, float falloffDistance, float lowPassCutoff, float startTime, float maxPlaySeconds = 0f, int priority = 110, bool dry = false, float minDistance = 2f) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)clip == (Object)null) { return false; } if (!EnsureLoaded(clip)) { return false; } Voice voice = FindFreeVoice(); if (voice == null) { return false; } Configure(voice, worldPos, insideHead, volume, pitch, radioEffect, falloffDistance, lowPassCutoff, priority, dry); voice.Source.minDistance = Mathf.Max(0.5f, minDistance); voice.Source.clip = clip; if (startTime > 0.01f && startTime < clip.length - 0.35f) { try { voice.Source.time = startTime; } catch { } } voice.Source.Play(); float num = (clip.length - startTime) / Mathf.Max(0.05f, Mathf.Abs(voice.Source.pitch)); float num2 = ((maxPlaySeconds > 0.1f) ? maxPlaySeconds : 12f); if (num > num2) { ((MonoBehaviour)this).StartCoroutine(FadeOutAfter(voice, voice.Token, num2, volume)); } return true; } private static bool EnsureLoaded(AudioClip clip) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Invalid comparison between Unknown and I4 try { if ((int)clip.loadState == 2) { return true; } clip.LoadAudioData(); if ((int)clip.loadState == 2) { return true; } if (ModConfig.DebugLogging.Value) { Plugin.Log.LogInfo((object)("Skipped '" + ((Object)clip).name + "' - its audio data is still loading.")); } return false; } catch { return true; } } private IEnumerator FadeOutAfter(Voice v, int token, float seconds, float volume) { yield return (object)new WaitForSeconds(seconds); if (v != null && !((Object)(object)v.Source == (Object)null) && v.Token == token) { float t = 0f; while (t < 0.7f && (Object)(object)v.Source != (Object)null && v.Source.isPlaying && v.Token == token) { t += Time.deltaTime; v.Source.volume = Mathf.Lerp(volume, 0f, t / 0.7f); yield return null; } if ((Object)(object)v.Source != (Object)null && v.Token == token) { v.Source.Stop(); v.Source.volume = volume; } } } public bool PlayFootsteps(AudioClip[] clips, Vector3 from, Vector3 to, int steps, float interval, float volume, float pitch, float falloffDistance, float cutoffFrom, float cutoffTo, bool varyPitchPerStep = true) { //IL_0048: 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_0073: Unknown result type (might be due to invalid IL or missing references) if (clips == null || clips.Length == 0 || steps <= 0) { return false; } bool flag = false; for (int i = 0; i < clips.Length; i++) { if ((Object)(object)clips[i] != (Object)null && EnsureLoaded(clips[i])) { flag = true; } } if (!flag) { return false; } Voice voice = FindFreeVoice(); if (voice == null) { return false; } Configure(voice, from, insideHead: false, volume, pitch, radioEffect: false, falloffDistance, cutoffFrom); voice.Source.clip = null; voice.Busy = true; try { if (((MonoBehaviour)this).StartCoroutine(StepRoutine(voice, clips, from, to, steps, interval, pitch, cutoffFrom, cutoffTo, varyPitchPerStep)) == null) { voice.Busy = false; return false; } } catch { voice.Busy = false; return false; } return true; } private IEnumerator StepRoutine(Voice v, AudioClip[] clips, Vector3 from, Vector3 to, int steps, float interval, float pitch, float cutoffFrom, float cutoffTo, bool varyPitchPerStep) { //IL_0015: 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_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) try { for (int i = 0; i < steps; i++) { if (v == null) { break; } if ((Object)(object)v.Source == (Object)null) { break; } float num = ((steps <= 1) ? 1f : ((float)i / (float)(steps - 1))); ((Component)v.Source).transform.position = Vector3.Lerp(from, to, num); if ((Object)(object)v.LowPass != (Object)null) { float num2 = Mathf.Lerp(cutoffFrom, cutoffTo, num); ((Behaviour)v.LowPass).enabled = num2 < 19000f; v.LowPass.cutoffFrequency = Mathf.Clamp(num2, 180f, 22000f); } AudioClip val = clips[Random.Range(0, clips.Length)]; if ((Object)(object)val != (Object)null) { if (varyPitchPerStep) { v.Source.pitch = Mathf.Clamp(pitch * Random.Range(0.94f, 1.06f), 0.25f, 3f); } v.Source.PlayOneShot(val, Random.Range(0.82f, 1f)); } yield return (object)new WaitForSeconds(interval * Random.Range(0.9f, 1.1f)); } float num3 = 0.6f; for (int j = 0; j < clips.Length; j++) { if ((Object)(object)clips[j] != (Object)null && clips[j].length > num3) { num3 = clips[j].length; } } yield return (object)new WaitForSeconds(Mathf.Min(num3, 3f)); } finally { if (v != null) { v.Busy = false; } } } public void StopAll() { ((MonoBehaviour)this).StopAllCoroutines(); if (_voices == null) { return; } for (int i = 0; i < _voices.Length; i++) { Voice voice = _voices[i]; if (voice != null) { voice.Busy = false; if ((Object)(object)voice.Source != (Object)null && voice.Source.isPlaying) { voice.Source.Stop(); } } } } } public static class ModConfig { public static ConfigEntry Enabled; public static ConfigEntry CheckInterval; public static ConfigEntry DarknessThreshold; public static ConfigEntry OnlyInsideFacility; public static ConfigEntry AllowInShip; public static ConfigEntry FlashlightClockRate; public static ConfigEntry RequireLineOfSight; public static ConfigEntry DirectionalLightCountsAsLit; public static ConfigEntry LightScanInterval; public static ConfigEntry LightMaxConsiderDistance; public static ConfigEntry FirstSoundAfterSeconds; public static ConfigEntry Stage2AfterSeconds; public static ConfigEntry Stage3AfterSeconds; public static ConfigEntry LightResetSeconds; public static ConfigEntry Stage1MinGap; public static ConfigEntry Stage1MaxGap; public static ConfigEntry Stage2MinGap; public static ConfigEntry Stage2MaxGap; public static ConfigEntry Stage3MinGap; public static ConfigEntry Stage3MaxGap; public static ConfigEntry MixInLowerStages; public static ConfigEntry ShorterGapsWhenAlone; public static ConfigEntry PlayWhileDead; public static ConfigEntry Stage1MinDistance; public static ConfigEntry Stage1MaxDistance; public static ConfigEntry Stage1Volume; public static ConfigEntry Stage2MinDistance; public static ConfigEntry Stage2MaxDistance; public static ConfigEntry Stage2Volume; public static ConfigEntry Stage3MinDistance; public static ConfigEntry Stage3MaxDistance; public static ConfigEntry Stage3Volume; public static ConfigEntry InsideHeadChance; public static ConfigEntry RadioEffectChance; public static ConfigEntry PitchVariance; public static ConfigEntry VolumeVariance; public static ConfigEntry MuffleAmount; public static ConfigEntry RandomStartChance; public static ConfigEntry LayerChance; public static ConfigEntry ThroughWallChance; public static ConfigEntry MaxSoundSeconds; public static ConfigEntry MaxSimultaneousSounds; public static ConfigEntry RouteThroughGameAudioMixer; public static ConfigEntry UseVanillaSounds; public static ConfigEntry VanillaShare; public static ConfigEntry ExtraEnemyVoices; public static ConfigEntry FootstepsEnabled; public static ConfigEntry UseGameFootsteps; public static ConfigEntry FootstepsAfterSeconds; public static ConfigEntry FootstepChance; public static ConfigEntry FootstepMinGap; public static ConfigEntry FootstepMaxGap; public static ConfigEntry FootstepStartMin; public static ConfigEntry FootstepStartMax; public static ConfigEntry FootstepEndDistance; public static ConfigEntry FootstepAroundCornerChance; public static ConfigEntry FootstepVolume; public static ConfigEntry WalkStepInterval; public static ConfigEntry RunStepInterval; public static ConfigEntry OneSoundAtATime; public static ConfigEntry SignatureChance; public static ConfigEntry CoilheadBurstChance; public static ConfigEntry CoilheadBurstDistance; public static ConfigEntry CoilheadLoudChance; public static ConfigEntry CoilheadVolume; public static ConfigEntry CoilheadClipIndex; public static ConfigEntry CoilheadRunShare; public static ConfigEntry CoilheadBurstShare; public static ConfigEntry CoilheadRunDistance; public static ConfigEntry CoilheadRunStepInterval; public static ConfigEntry DoorSoundChance; public static ConfigEntry DoorMinDistance; public static ConfigEntry DoorMaxDistance; public static ConfigEntry DoorVolume; public static ConfigEntry DoorOpenShare; public static ConfigEntry DoorPairChance; public static ConfigEntry RunChanceAtStage3; public static ConfigEntry WhisperChance; public static ConfigEntry WhisperMinDistance; public static ConfigEntry WhisperMaxDistance; public static ConfigEntry WhisperVolume; public static ConfigEntry VoiceChance; public static ConfigEntry MatchVoicesToPlayerNames; public static ConfigEntry NeverImpersonateYourself; public static ConfigEntry DebugLogging; public static ConfigEntry EnableTestKey; public static ConfigEntry CoilheadKey; public static ConfigEntry TestKey; public static ConfigEntry StageUpKey; public static ConfigEntry FootstepKey; public static void Init(ConfigFile cfg) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Expected O, but got Unknown //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Expected O, but got Unknown //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Expected O, but got Unknown //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Expected O, but got Unknown //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Expected O, but got Unknown //IL_0200: Unknown result type (might be due to invalid IL or missing references) //IL_020a: Expected O, but got Unknown //IL_0238: Unknown result type (might be due to invalid IL or missing references) //IL_0242: Expected O, but got Unknown //IL_0270: Unknown result type (might be due to invalid IL or missing references) //IL_027a: Expected O, but got Unknown //IL_02a8: Unknown result type (might be due to invalid IL or missing references) //IL_02b2: Expected O, but got Unknown //IL_02e0: Unknown result type (might be due to invalid IL or missing references) //IL_02ea: Expected O, but got Unknown //IL_0318: Unknown result type (might be due to invalid IL or missing references) //IL_0322: Expected O, but got Unknown //IL_0350: Unknown result type (might be due to invalid IL or missing references) //IL_035a: Expected O, but got Unknown //IL_0388: Unknown result type (might be due to invalid IL or missing references) //IL_0392: Expected O, but got Unknown //IL_03c0: Unknown result type (might be due to invalid IL or missing references) //IL_03ca: Expected O, but got Unknown //IL_0449: Unknown result type (might be due to invalid IL or missing references) //IL_0453: Expected O, but got Unknown //IL_0481: Unknown result type (might be due to invalid IL or missing references) //IL_048b: Expected O, but got Unknown //IL_04b9: Unknown result type (might be due to invalid IL or missing references) //IL_04c3: Expected O, but got Unknown //IL_04f1: Unknown result type (might be due to invalid IL or missing references) //IL_04fb: Expected O, but got Unknown //IL_0529: Unknown result type (might be due to invalid IL or missing references) //IL_0533: Expected O, but got Unknown //IL_0561: Unknown result type (might be due to invalid IL or missing references) //IL_056b: Expected O, but got Unknown //IL_0599: Unknown result type (might be due to invalid IL or missing references) //IL_05a3: Expected O, but got Unknown //IL_05d1: Unknown result type (might be due to invalid IL or missing references) //IL_05db: Expected O, but got Unknown //IL_0609: Unknown result type (might be due to invalid IL or missing references) //IL_0613: Expected O, but got Unknown //IL_0641: Unknown result type (might be due to invalid IL or missing references) //IL_064b: Expected O, but got Unknown //IL_0679: Unknown result type (might be due to invalid IL or missing references) //IL_0683: Expected O, but got Unknown //IL_06b1: Unknown result type (might be due to invalid IL or missing references) //IL_06bb: Expected O, but got Unknown //IL_06e9: Unknown result type (might be due to invalid IL or missing references) //IL_06f3: Expected O, but got Unknown //IL_0721: Unknown result type (might be due to invalid IL or missing references) //IL_072b: Expected O, but got Unknown //IL_0759: Unknown result type (might be due to invalid IL or missing references) //IL_0763: Expected O, but got Unknown //IL_0791: Unknown result type (might be due to invalid IL or missing references) //IL_079b: Expected O, but got Unknown //IL_07c9: Unknown result type (might be due to invalid IL or missing references) //IL_07d3: Expected O, but got Unknown //IL_0801: Unknown result type (might be due to invalid IL or missing references) //IL_080b: Expected O, but got Unknown //IL_082e: Unknown result type (might be due to invalid IL or missing references) //IL_0838: Expected O, but got Unknown //IL_089c: Unknown result type (might be due to invalid IL or missing references) //IL_08a6: Expected O, but got Unknown //IL_0925: Unknown result type (might be due to invalid IL or missing references) //IL_092f: Expected O, but got Unknown //IL_095d: Unknown result type (might be due to invalid IL or missing references) //IL_0967: Expected O, but got Unknown //IL_0995: Unknown result type (might be due to invalid IL or missing references) //IL_099f: Expected O, but got Unknown //IL_09cd: Unknown result type (might be due to invalid IL or missing references) //IL_09d7: Expected O, but got Unknown //IL_0a05: Unknown result type (might be due to invalid IL or missing references) //IL_0a0f: Expected O, but got Unknown //IL_0a3d: Unknown result type (might be due to invalid IL or missing references) //IL_0a47: Expected O, but got Unknown //IL_0a75: Unknown result type (might be due to invalid IL or missing references) //IL_0a7f: Expected O, but got Unknown //IL_0aad: Unknown result type (might be due to invalid IL or missing references) //IL_0ab7: Expected O, but got Unknown //IL_0ae5: Unknown result type (might be due to invalid IL or missing references) //IL_0aef: Expected O, but got Unknown //IL_0b1d: Unknown result type (might be due to invalid IL or missing references) //IL_0b27: Expected O, but got Unknown //IL_0b55: Unknown result type (might be due to invalid IL or missing references) //IL_0b5f: Expected O, but got Unknown //IL_0b8d: Unknown result type (might be due to invalid IL or missing references) //IL_0b97: Expected O, but got Unknown //IL_0be0: Unknown result type (might be due to invalid IL or missing references) //IL_0bea: Expected O, but got Unknown //IL_0c18: Unknown result type (might be due to invalid IL or missing references) //IL_0c22: Expected O, but got Unknown //IL_0c50: Unknown result type (might be due to invalid IL or missing references) //IL_0c5a: Expected O, but got Unknown //IL_0c88: Unknown result type (might be due to invalid IL or missing references) //IL_0c92: Expected O, but got Unknown //IL_0cb5: Unknown result type (might be due to invalid IL or missing references) //IL_0cbf: Expected O, but got Unknown //IL_0ced: Unknown result type (might be due to invalid IL or missing references) //IL_0cf7: Expected O, but got Unknown //IL_0d25: Unknown result type (might be due to invalid IL or missing references) //IL_0d2f: Expected O, but got Unknown //IL_0d5d: Unknown result type (might be due to invalid IL or missing references) //IL_0d67: Expected O, but got Unknown //IL_0d95: Unknown result type (might be due to invalid IL or missing references) //IL_0d9f: Expected O, but got Unknown //IL_0dcd: Unknown result type (might be due to invalid IL or missing references) //IL_0dd7: Expected O, but got Unknown //IL_0e05: Unknown result type (might be due to invalid IL or missing references) //IL_0e0f: Expected O, but got Unknown //IL_0e3d: Unknown result type (might be due to invalid IL or missing references) //IL_0e47: Expected O, but got Unknown //IL_0e75: Unknown result type (might be due to invalid IL or missing references) //IL_0e7f: Expected O, but got Unknown //IL_0ead: Unknown result type (might be due to invalid IL or missing references) //IL_0eb7: Expected O, but got Unknown //IL_0ee5: Unknown result type (might be due to invalid IL or missing references) //IL_0eef: Expected O, but got Unknown //IL_0f1d: Unknown result type (might be due to invalid IL or missing references) //IL_0f27: Expected O, but got Unknown //IL_0f55: Unknown result type (might be due to invalid IL or missing references) //IL_0f5f: Expected O, but got Unknown //IL_0f8d: Unknown result type (might be due to invalid IL or missing references) //IL_0f97: Expected O, but got Unknown //IL_0fc5: Unknown result type (might be due to invalid IL or missing references) //IL_0fcf: Expected O, but got Unknown //IL_0ffd: Unknown result type (might be due to invalid IL or missing references) //IL_1007: Expected O, but got Unknown //IL_1035: Unknown result type (might be due to invalid IL or missing references) //IL_103f: Expected O, but got Unknown Enabled = cfg.Bind("1 - General", "Enabled", true, "Master switch. Turn off to disable all hallucinations without uninstalling."); CheckInterval = cfg.Bind("1 - General", "CheckInterval", 0.5f, new ConfigDescription("How often (seconds) the mod re-evaluates whether you are in the dark.", (AcceptableValueBase)(object)new AcceptableValueRange(0.1f, 5f), Array.Empty())); DarknessThreshold = cfg.Bind("2 - Darkness", "DarknessThreshold", 0.3f, new ConfigDescription("0 = pitch black only, 1 = anywhere. A spot counts as dark when its light score is below this. 0.3 is roughly 'no lamp within 70% of its range', which is what an unlit corridor in a powered facility actually measures. Raise it if the mod stays silent, lower it if it triggers in lit rooms.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); OnlyInsideFacility = cfg.Bind("2 - Darkness", "OnlyInsideFacility", true, "Only hallucinate inside the facility/mine/mansion. Set false to also allow the outdoor map at night."); AllowInShip = cfg.Bind("2 - Darkness", "AllowInShip", false, "Allow hallucinations while you are standing inside the ship."); FlashlightClockRate = cfg.Bind("2 - Darkness", "FlashlightClockRate", 0.5f, new ConfigDescription("How fast the dark timer runs while YOUR OWN flashlight or helmet lamp is on, in an otherwise unlit room. 0 = your light stops it completely, 0.5 = it still creeps up at half speed, 1 = your light doesn't help at all. A lit room always stops the timer regardless of this.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); RequireLineOfSight = cfg.Bind("2 - Darkness", "RequireLineOfSight", true, "Lights behind a wall do not count as lighting you. More accurate, slightly more CPU."); DirectionalLightCountsAsLit = cfg.Bind("2 - Darkness", "DirectionalLightCountsAsLit", true, "Treat the sun / moon (directional light) as full lighting when you are outdoors."); LightScanInterval = cfg.Bind("2 - Darkness", "LightScanInterval", 8f, new ConfigDescription("How often (seconds) the mod rebuilds its list of lights in the level. Raise it if you see stutter on huge maps.", (AcceptableValueBase)(object)new AcceptableValueRange(2f, 60f), Array.Empty())); LightMaxConsiderDistance = cfg.Bind("2 - Darkness", "LightMaxConsiderDistance", 60f, new ConfigDescription("Lights farther away than this are skipped entirely.", (AcceptableValueBase)(object)new AcceptableValueRange(10f, 300f), Array.Empty())); FirstSoundAfterSeconds = cfg.Bind("3 - Pacing", "FirstSoundAfterSeconds", 20f, new ConfigDescription("Seconds of darkness before you hear anything at all. The timer only runs while you are actually in the dark. A Lethal Company day is only about 11 real minutes, so the defaults are sized to fit inside one round.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 900f), Array.Empty())); Stage2AfterSeconds = cfg.Bind("3 - Pacing", "Stage2AfterSeconds", 90f, new ConfigDescription("Total darkness needed before Stage 2 unlocks: closer whispers, walking, breathing. A minute and a half of accumulated darkness.", (AcceptableValueBase)(object)new AcceptableValueRange(10f, 3600f), Array.Empty())); Stage3AfterSeconds = cfg.Bind("3 - Pacing", "Stage3AfterSeconds", 180f, new ConfigDescription("Total darkness needed before Stage 3 unlocks: running up behind you, coilhead snapping. Three minutes of accumulated darkness - reachable within a single day even if you carry a light.", (AcceptableValueBase)(object)new AcceptableValueRange(10f, 3600f), Array.Empty())); LightResetSeconds = cfg.Bind("3 - Pacing", "LightResetSeconds", 45f, new ConfigDescription("Stand in light for this long and everything resets to zero - back to Stage 1 and a fresh minute before the next sound. Shorter trips through a lit room only pause the timer, they do not reset it.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 900f), Array.Empty())); Stage1MinGap = cfg.Bind("3 - Pacing", "Stage1MinGap", 12f, new ConfigDescription("Shortest gap (seconds of darkness) between Stage 1 sounds. Values below 5 are treated as 5.", (AcceptableValueBase)(object)new AcceptableValueRange(5f, 900f), Array.Empty())); Stage1MaxGap = cfg.Bind("3 - Pacing", "Stage1MaxGap", 26f, new ConfigDescription("Longest gap between Stage 1 sounds.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 900f), Array.Empty())); Stage2MinGap = cfg.Bind("3 - Pacing", "Stage2MinGap", 10f, new ConfigDescription("Shortest gap between Stage 2 sounds.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 900f), Array.Empty())); Stage2MaxGap = cfg.Bind("3 - Pacing", "Stage2MaxGap", 22f, new ConfigDescription("Longest gap between Stage 2 sounds.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 900f), Array.Empty())); Stage3MinGap = cfg.Bind("3 - Pacing", "Stage3MinGap", 9f, new ConfigDescription("Shortest gap between Stage 3 sounds.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 900f), Array.Empty())); Stage3MaxGap = cfg.Bind("3 - Pacing", "Stage3MaxGap", 19f, new ConfigDescription("Longest gap between Stage 3 sounds.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 900f), Array.Empty())); MixInLowerStages = cfg.Bind("3 - Pacing", "MixInLowerStages", true, "At Stage 2 and 3, still occasionally play the quieter earlier sounds so it doesn't turn into constant chaos."); ShorterGapsWhenAlone = cfg.Bind("3 - Pacing", "ShorterGapsWhenAlone", true, "Gaps are ~30% shorter when no teammate is near you."); PlayWhileDead = cfg.Bind("3 - Pacing", "PlayWhileDead", false, "Keep hallucinating while you are dead and spectating."); Stage1MinDistance = cfg.Bind("4 - Distance", "Stage1MinDistance", 7f, new ConfigDescription("Stage 1 sounds spawn no closer than this, in metres. Far away is the point.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 100f), Array.Empty())); Stage1MaxDistance = cfg.Bind("4 - Distance", "Stage1MaxDistance", 17f, new ConfigDescription("Stage 1 sounds spawn no further than this.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 150f), Array.Empty())); Stage1Volume = cfg.Bind("4 - Distance", "Stage1Volume", 0.75f, new ConfigDescription("Stage 1 loudness.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); Stage2MinDistance = cfg.Bind("4 - Distance", "Stage2MinDistance", 4f, new ConfigDescription("Stage 2 minimum distance.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 100f), Array.Empty())); Stage2MaxDistance = cfg.Bind("4 - Distance", "Stage2MaxDistance", 11f, new ConfigDescription("Stage 2 maximum distance.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 150f), Array.Empty())); Stage2Volume = cfg.Bind("4 - Distance", "Stage2Volume", 0.9f, new ConfigDescription("Stage 2 loudness.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); Stage3MinDistance = cfg.Bind("4 - Distance", "Stage3MinDistance", 2.5f, new ConfigDescription("Stage 3 minimum distance.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 100f), Array.Empty())); Stage3MaxDistance = cfg.Bind("4 - Distance", "Stage3MaxDistance", 8f, new ConfigDescription("Stage 3 maximum distance.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 150f), Array.Empty())); Stage3Volume = cfg.Bind("4 - Distance", "Stage3Volume", 1f, new ConfigDescription("Stage 3 loudness.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); InsideHeadChance = cfg.Bind("5 - Playback", "InsideHeadChance", 0.12f, new ConfigDescription("Chance a Stage 2 or 3 sound plays flat in both ears instead of from a direction - 'inside your head'. Stage 1 is always positional so it stays distant.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); RadioEffectChance = cfg.Bind("5 - Playback", "RadioEffectChance", 0.3f, new ConfigDescription("Chance a voice clip is filtered to sound like it came over the walkie-talkie.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); PitchVariance = cfg.Bind("5 - Playback", "PitchVariance", 0.09f, new ConfigDescription("Random pitch shift per play, so the same clip is never quite the same sound. 0 disables it.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 0.5f), Array.Empty())); VolumeVariance = cfg.Bind("5 - Playback", "VolumeVariance", 0.2f, new ConfigDescription("Random loudness shift per play. Some you barely catch, some you definitely hear.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 0.8f), Array.Empty())); MuffleAmount = cfg.Bind("5 - Playback", "MuffleAmount", 0.45f, new ConfigDescription("How much the top end is rolled off, so sounds arrive through walls and distance rather than sitting on top of the mix. 0 = crisp and obvious, 1 = very muffled and easy to dismiss. The amount is randomised around this per play, and doubles when there is real geometry between you and the sound.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); RandomStartChance = cfg.Bind("5 - Playback", "RandomStartChance", 0.3f, new ConfigDescription("Chance a longer clip starts partway in, as if you only caught the end of it.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); LayerChance = cfg.Bind("5 - Playback", "LayerChance", 0f, new ConfigDescription("Chance a second, quieter sound plays alongside the first from a different direction. Off by default - two sounds at once tends to read as a bug rather than as company. Needs MaxSimultaneousSounds of at least 2 and OneSoundAtATime off.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); ThroughWallChance = cfg.Bind("5 - Playback", "ThroughWallChance", 0.2f, new ConfigDescription("Chance a sound is placed in the next room rather than in yours. Those get a much heavier roll-off, so they arrive as 'something is through there' instead of 'something is here'.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); MaxSoundSeconds = cfg.Bind("5 - Playback", "MaxSoundSeconds", 8f, new ConfigDescription("Longest any single sound is allowed to run before it fades out. Stops one long file in a Sounds folder from holding the channel - and, with OneSoundAtATime, blocking everything else.", (AcceptableValueBase)(object)new AcceptableValueRange(2f, 60f), Array.Empty())); MaxSimultaneousSounds = cfg.Bind("5 - Playback", "MaxSimultaneousSounds", 3, new ConfigDescription("Size of the playback channel pool. OneSoundAtATime still stops two hallucinations from being started together, but a couple of sounds finish each other off - a running coilhead ends on the coil, a door that creaks open shuts again - and those need a spare channel to land on. Never runs with fewer than two.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 16), Array.Empty())); RouteThroughGameAudioMixer = cfg.Bind("5 - Playback", "RouteThroughGameAudioMixer", true, "Play through the game's own audio mixer so hallucinations obey the in-game sound slider and pick up room reverb. Turn off if the sounds come out muffled or silent."); UseVanillaSounds = cfg.Bind("6 - Game sounds", "UseVanillaSounds", true, "Borrow the game's own audio at runtime - real coilhead springs, the ghost girl's breathing, real footsteps, door and hull creaks, the insanity ambience. Nothing is copied or redistributed; the mod reads the clips already loaded in your game, so they always match your version."); VanillaShare = cfg.Bind("6 - Game sounds", "VanillaShare", 0.35f, new ConfigDescription("How often a sound comes from the game rather than from the mod's own Sounds folder. 1 = only game audio, 0 = only the bundled clips. Kept low because the borrowed set is deliberately small - only the sounds you can't put a name to.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); ExtraEnemyVoices = cfg.Bind("6 - Game sounds", "ExtraEnemyVoices", false, "Also borrow voice clips from other monsters, so you occasionally hear something that isn't there at all. Off by default: a recognisable monster noise makes people think the mod is buggy rather than that the building is wrong. Vanilla enemies only, even when on."); FootstepsEnabled = cfg.Bind("6b - Footsteps", "FootstepsEnabled", true, "Phantom footsteps: a line of steps that walks across the room behind you, or straight at you, and stops. They have their own schedule, independent of every other sound."); UseGameFootsteps = cfg.Bind("6b - Footsteps", "UseGameFootsteps", true, "Use the game's real footstep audio, matched to the floor surface you are standing on. Turn off to use only your own clips from Sounds/Footsteps/."); FootstepsAfterSeconds = cfg.Bind("6b - Footsteps", "FootstepsAfterSeconds", 12f, new ConfigDescription("Seconds of darkness before footsteps can start. Lower than the first voice on purpose - footsteps are the friendliest way in.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 600f), Array.Empty())); FootstepChance = cfg.Bind("6b - Footsteps", "FootstepChance", 0.72f, new ConfigDescription("When the footstep timer comes round, the odds it actually happens. Below 1 the rhythm stops being a rhythm - two skips in a row is a long, pointed silence, and you can't learn when to expect them. Lower for rarer and more irregular.", (AcceptableValueBase)(object)new AcceptableValueRange(0.05f, 1f), Array.Empty())); FootstepMinGap = cfg.Bind("6b - Footsteps", "FootstepMinGap", 36f, new ConfigDescription("Shortest gap between footstep events, in seconds of darkness. Values below 4 are treated as 4.", (AcceptableValueBase)(object)new AcceptableValueRange(4f, 600f), Array.Empty())); FootstepMaxGap = cfg.Bind("6b - Footsteps", "FootstepMaxGap", 72f, new ConfigDescription("Longest gap between footstep events.", (AcceptableValueBase)(object)new AcceptableValueRange(2f, 600f), Array.Empty())); FootstepStartMin = cfg.Bind("6b - Footsteps", "FootstepStartMin", 9f, new ConfigDescription("Closest the steps can begin, in metres. Lethal Company interiors are cramped, so keep this modest - too large and every walk gets blocked by a wall and never plays.", (AcceptableValueBase)(object)new AcceptableValueRange(1.5f, 40f), Array.Empty())); FootstepStartMax = cfg.Bind("6b - Footsteps", "FootstepStartMax", 20f, new ConfigDescription("Furthest the steps can begin, in metres.", (AcceptableValueBase)(object)new AcceptableValueRange(2f, 60f), Array.Empty())); FootstepEndDistance = cfg.Bind("6b - Footsteps", "FootstepEndDistance", 5f, new ConfigDescription("How close they get before stopping, in metres. Kept at arm's length rather than on top of you - they should sound like someone in the next stretch of corridor, not someone touching you.", (AcceptableValueBase)(object)new AcceptableValueRange(0.5f, 20f), Array.Empty())); RunChanceAtStage3 = cfg.Bind("6b - Footsteps", "RunChanceAtStage3", 0.9f, new ConfigDescription("At Stage 3, the odds a footstep event is somebody RUNNING at you rather than walking. The first Stage 3 footsteps of a run always sprint regardless, so you never finish a round without hearing it.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); FootstepAroundCornerChance = cfg.Bind("6b - Footsteps", "FootstepAroundCornerChance", 0.5f, new ConfigDescription("Chance the steps begin out of sight - through a wall, around a corner - and walk into view. Those start heavily muffled and open up as they come, which is the difference between 'a sound played' and 'somebody is coming'.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); FootstepVolume = cfg.Bind("6b - Footsteps", "FootstepVolume", 0.8f, new ConfigDescription("Footstep loudness.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); WalkStepInterval = cfg.Bind("6b - Footsteps", "WalkStepInterval", 0.54f, new ConfigDescription("Seconds between steps when it's walking. The number of steps follows the distance walked, so this sets the pace, not the length.", (AcceptableValueBase)(object)new AcceptableValueRange(0.2f, 1.2f), Array.Empty())); RunStepInterval = cfg.Bind("6b - Footsteps", "RunStepInterval", 0.28f, new ConfigDescription("Seconds between steps when it's running (Stage 3).", (AcceptableValueBase)(object)new AcceptableValueRange(0.12f, 0.8f), Array.Empty())); OneSoundAtATime = cfg.Bind("5 - Playback", "OneSoundAtATime", true, "Never start a sound while another one is still audible. Two hallucinations at once reads as a glitch rather than as something being in the room with you."); CoilheadBurstChance = cfg.Bind("6 - Game sounds", "CoilheadBurstChance", 0.8f, new ConfigDescription("When a signature sound comes up, the odds it's the coilhead rather than the ghost girl. The coilhead arrives as a rapid burst of spring noises right behind you - several in a row, closing - the way a real one sounds when it lunges. Not one polite click.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); CoilheadBurstDistance = cfg.Bind("6 - Game sounds", "CoilheadBurstDistance", 1.5f, new ConfigDescription("How close the coilhead burst gets, in metres. This is meant to be right behind your head. Values above about 3 leave no room for it to close the distance.", (AcceptableValueBase)(object)new AcceptableValueRange(0.8f, 4f), Array.Empty())); CoilheadLoudChance = cfg.Bind("6 - Game sounds", "CoilheadLoudChance", 0.4f, new ConfigDescription("Relative share of coilhead moments that are the single loud coil, dead behind you. Weighed against CoilheadRunShare and CoilheadBurstShare rather than being a straight probability - the three are normalised, so 0.4 / 0.45 / 0.25 means roughly 36% / 41% / 23%.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 2f), Array.Empty())); CoilheadVolume = cfg.Bind("6 - Game sounds", "CoilheadVolume", 1f, new ConfigDescription("How loud the coil is. This one is deliberately played dry and at full volume - unlike everything else in the mod, it is meant to be recognised the instant it happens.", (AcceptableValueBase)(object)new AcceptableValueRange(0.2f, 1f), Array.Empty())); CoilheadClipIndex = cfg.Bind("6 - Game sounds", "CoilheadClipIndex", -1, new ConfigDescription("-1 picks the loudest spring clip automatically. The log lists every coilhead clip it found, numbered and with lengths, on every level load - set this to one of those numbers to always use that exact clip.", (AcceptableValueBase)(object)new AcceptableValueRange(-1, 31), Array.Empty())); CoilheadRunShare = cfg.Bind("6 - Game sounds", "CoilheadRunShare", 0.45f, new ConfigDescription("Relative share of coilhead moments that are one running at you - its own movement audio closing from several metres out, ending on the coil as it stops dead. Needs room to run; falls back to the other forms in a tight corridor. Weighed against CoilheadLoudChance and CoilheadBurstShare.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 2f), Array.Empty())); CoilheadBurstShare = cfg.Bind("6 - Game sounds", "CoilheadBurstShare", 0.25f, new ConfigDescription("Relative share of coilhead moments that are a rapid burst of short snaps closing in behind you.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 2f), Array.Empty())); CoilheadRunDistance = cfg.Bind("6 - Game sounds", "CoilheadRunDistance", 11f, new ConfigDescription("How far away a running coilhead starts, when there is that much clear space behind you.", (AcceptableValueBase)(object)new AcceptableValueRange(5f, 25f), Array.Empty())); CoilheadRunStepInterval = cfg.Bind("6 - Game sounds", "CoilheadRunStepInterval", 0.19f, new ConfigDescription("Seconds between the steps of a running coilhead. Lower is faster and more frantic.", (AcceptableValueBase)(object)new AcceptableValueRange(0.08f, 0.5f), Array.Empty())); DoorSoundChance = cfg.Bind("6 - Game sounds", "DoorSoundChance", 0.2f, new ConfigDescription("How often a sound is a door creaking open or slamming. It is played from the position of a door that is actually there - if nothing near you is a door, no door sound happens at all.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); DoorMinDistance = cfg.Bind("6 - Game sounds", "DoorMinDistance", 3f, new ConfigDescription("Doors closer than this are skipped - a door slamming in your face is a jumpscare, not a doubt.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 15f), Array.Empty())); DoorMaxDistance = cfg.Bind("6 - Game sounds", "DoorMaxDistance", 16f, new ConfigDescription("How far away a real door can be and still be used.", (AcceptableValueBase)(object)new AcceptableValueRange(4f, 40f), Array.Empty())); DoorVolume = cfg.Bind("6 - Game sounds", "DoorVolume", 0.8f, new ConfigDescription("Volume of door sounds before distance and walls are applied.", (AcceptableValueBase)(object)new AcceptableValueRange(0.1f, 1f), Array.Empty())); DoorOpenShare = cfg.Bind("6 - Game sounds", "DoorOpenShare", 0.6f, new ConfigDescription("Share of door sounds that are a slow creak open rather than a slam.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); DoorPairChance = cfg.Bind("6 - Game sounds", "DoorPairChance", 0.45f, new ConfigDescription("After a creak, the odds the same door shuts again a second or two later. Someone came through.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); SignatureChance = cfg.Bind("6 - Game sounds", "SignatureChance", 0.22f, new ConfigDescription("At Stage 3 only: chance the sound is one of the game's unmistakable ones - a coilhead's neck, the ghost girl's sting. Guaranteed a slice of the late game rather than being one clip among dozens.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); WhisperChance = cfg.Bind("7 - Voices", "WhisperChance", 0.45f, new ConfigDescription("How often a sound is a close human one - whispering, breathing, humming, someone swallowing - from the Whispers folder. These are the point of the mod, so they are the most common thing you hear.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); WhisperMinDistance = cfg.Bind("7 - Voices", "WhisperMinDistance", 1.5f, new ConfigDescription("Closest a whisper can be, in metres. A whisper is only unsettling at conversational range - from across a room it is simply inaudible, which is why these ignore the stage distances.", (AcceptableValueBase)(object)new AcceptableValueRange(0.5f, 20f), Array.Empty())); WhisperMaxDistance = cfg.Bind("7 - Voices", "WhisperMaxDistance", 6f, new ConfigDescription("Furthest a whisper can be, in metres.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 40f), Array.Empty())); WhisperVolume = cfg.Bind("7 - Voices", "WhisperVolume", 0.85f, new ConfigDescription("Loudness of close human sounds. Quiet on purpose - you should have to stop moving to be sure you heard it.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); VoiceChance = cfg.Bind("7 - Voices", "VoiceChance", 0.2f, new ConfigDescription("Share of Stage 2 and 3 sounds that come from the Voices folder instead. Ignored if that folder is empty. Voices never appear at Stage 1.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); MatchVoicesToPlayerNames = cfg.Bind("7 - Voices", "MatchVoicesToPlayerNames", false, "Only play a voice clip if a player with that name is in the lobby. Put the clips in a subfolder named after the player, e.g. Sounds/Voices/Sam/help.ogg. Files directly in Voices/ are generic and always allowed."); NeverImpersonateYourself = cfg.Bind("7 - Voices", "NeverImpersonateYourself", true, "With name matching on, never play clips named after your own username."); DebugLogging = cfg.Bind("8 - Debug", "DebugLogging", false, "Spam the BepInEx console with light scores, stage changes and countdowns. Use this to tune DarknessThreshold."); EnableTestKey = cfg.Bind("8 - Debug", "EnableTestKey", false, "Enable the testing keys below. Off by default so a normal game has no hotkeys bound - turn it on while you are tuning the mod, then off again. The keys are ignored while you are typing in chat or using the terminal."); TestKey = cfg.Bind("8 - Debug", "TestKey", "F8", "Plays a sound from your current stage immediately, anywhere. Uses Unity's new Input System key names, e.g. F8, F9, Backquote, Numpad0."); FootstepKey = cfg.Bind("8 - Debug", "FootstepKey", "F10", "Forces a phantom footstep sequence immediately, so you can check they work without waiting."); CoilheadKey = cfg.Bind("8 - Debug", "CoilheadKey", "F7", "Plays the loud coil immediately, wherever you are. The fastest way to confirm the coilhead audio was found on your install."); StageUpKey = cfg.Bind("8 - Debug", "StageUpKey", "F9", "Jumps straight to the next stage so you can hear Stage 2 and 3 without waiting eight minutes. Press again past Stage 3 to reset to zero."); } } [BepInPlugin("com.paranoidcompany", "ParanoidCompany", "2.0.1")] public class Plugin : BaseUnityPlugin { private GameObject _host; public static ManualLogSource Log { get; private set; } private void Awake() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; ModConfig.Init(((BaseUnityPlugin)this).Config); SoundLibrary.ResolveRoot(ResolvePluginDirectory()); _host = new GameObject("ParanoidCompanyHost"); Object.DontDestroyOnLoad((Object)(object)_host); ((Object)_host).hideFlags = (HideFlags)61; _host.AddComponent(); Log.LogInfo((object)("ParanoidCompany v2.0.1 loaded. Sounds folder: " + SoundLibrary.SoundsRoot)); } private string ResolvePluginDirectory() { try { if (((BaseUnityPlugin)this).Info != null && ((BaseUnityPlugin)this).Info.Location != null && ((BaseUnityPlugin)this).Info.Location.Length > 0) { string directoryName = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location); if (!string.IsNullOrEmpty(directoryName)) { return directoryName; } } } catch { } try { string location = Assembly.GetExecutingAssembly().Location; if (!string.IsNullOrEmpty(location)) { string directoryName2 = Path.GetDirectoryName(location); if (!string.IsNullOrEmpty(directoryName2)) { return directoryName2; } } } catch { } Log.LogWarning((object)"Could not determine the plugin folder; falling back to BepInEx/plugins."); return Path.Combine(Paths.PluginPath, "ParanoidCompany"); } } public static class PluginInfo { public const string GUID = "com.paranoidcompany"; public const string NAME = "ParanoidCompany"; public const string VERSION = "2.0.1"; } public class SchizoManager : MonoBehaviour { private readonly DarknessSensor _sensor = new DarknessSensor(); private HallucinationPlayer _player; private float _nextCheck; private float _lastCheckTime; private float _darkSeconds; private float _litSeconds; private int _litSamples; private float _nextEventAtDark; private float _nextFootstepAtDark; private int _lastStage; private bool _wasDead; private bool _ranAtStage3; private bool _signalledStage3; private Key _testKey = (Key)101; private Key _stageKey = (Key)102; private Key _footKey = (Key)103; private Key _coilKey = (Key)100; private int _sceneToken; private int _cutToken; private int _lastCoilForm; private int _coilKeyForm; private bool _coilKeyValid = true; private bool _testKeyValid; private bool _stageKeyValid; private bool _footKeyValid; private static bool _warnedNoEligibleVoices; private static bool _warnedNoFootsteps; private void Awake() { _player = ((Component)this).gameObject.AddComponent(); _player.Build(Mathf.Max(2, ModConfig.MaxSimultaneousSounds.Value)); _testKeyValid = ParseKey(ModConfig.TestKey.Value, out _testKey); _stageKeyValid = ParseKey(ModConfig.StageUpKey.Value, out _stageKey); _footKeyValid = ParseKey(ModConfig.FootstepKey.Value, out _footKey); _coilKeyValid = ParseKey(ModConfig.CoilheadKey.Value, out _coilKey); if (ModConfig.EnableTestKey.Value) { if (!_testKeyValid) { Plugin.Log.LogWarning((object)("TestKey '" + ModConfig.TestKey.Value + "' is not a usable key name.")); } if (!_stageKeyValid) { Plugin.Log.LogWarning((object)("StageUpKey '" + ModConfig.StageUpKey.Value + "' is not a usable key name.")); } if (!_footKeyValid) { Plugin.Log.LogWarning((object)("FootstepKey '" + ModConfig.FootstepKey.Value + "' is not a usable key name.")); } if (!_coilKeyValid) { Plugin.Log.LogWarning((object)("CoilheadKey '" + ModConfig.CoilheadKey.Value + "' is not a usable key name.")); } } ResetProgress(log: false); ValidateConfig(); } private static void ValidateConfig() { float value = ModConfig.FirstSoundAfterSeconds.Value; float value2 = ModConfig.Stage2AfterSeconds.Value; float value3 = ModConfig.Stage3AfterSeconds.Value; if (value2 <= value) { Plugin.Log.LogWarning((object)("Config: Stage2AfterSeconds (" + value2 + ") is not greater than FirstSoundAfterSeconds (" + value + "), so Stage 1 will never play. Raise Stage2AfterSeconds.")); } if (value3 <= value2) { Plugin.Log.LogWarning((object)("Config: Stage3AfterSeconds (" + value3 + ") is not greater than Stage2AfterSeconds (" + value2 + "), so Stage 2 will never play. Raise Stage3AfterSeconds.")); } if (ModConfig.FootstepEndDistance.Value >= ModConfig.FootstepStartMin.Value) { Plugin.Log.LogWarning((object)("Config: FootstepEndDistance (" + ModConfig.FootstepEndDistance.Value + ") should be smaller than FootstepStartMin (" + ModConfig.FootstepStartMin.Value + ") or the steps have nowhere to walk from; it will be clamped.")); } if (ModConfig.FootstepStartMax.Value > 25f) { Plugin.Log.LogWarning((object)"Config: FootstepStartMax is large. Lethal Company interiors are cramped - walks that start far away usually get blocked by a wall and never play."); } if (ModConfig.LightResetSeconds.Value < ModConfig.CheckInterval.Value * 3f) { Plugin.Log.LogWarning((object)"Config: LightResetSeconds is very small compared to CheckInterval; progress may reset the instant you see any light."); } if (!ModConfig.OnlyInsideFacility.Value && ModConfig.DirectionalLightCountsAsLit.Value) { Plugin.Log.LogWarning((object)"Config: OnlyInsideFacility is off but DirectionalLightCountsAsLit is on, so anywhere outdoors counts as fully lit while the sun is up and you will hear nothing out there. Turn DirectionalLightCountsAsLit off as well."); } if (ModConfig.PlayWhileDead.Value && ModConfig.OnlyInsideFacility.Value) { Plugin.Log.LogInfo((object)"Config: PlayWhileDead is on. The game reports a dead player as being outside the facility, so the OnlyInsideFacility rule is skipped while you are dead."); } } private static bool ParseKey(string name, out Key key) { if (Enum.TryParse(name, ignoreCase: true, out key) && key) { return Enum.IsDefined(typeof(Key), key); } return false; } private void OnEnable() { SceneManager.sceneLoaded += OnSceneLoaded; } private void OnDisable() { SceneManager.sceneLoaded -= OnSceneLoaded; } private IEnumerator Start() { _lastCheckTime = Time.time; yield return SoundLibrary.LoadAll(); } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { try { HandleSceneLoaded(); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Scene load handling failed: " + ex.Message)); } } private void HandleSceneLoaded() { _sceneToken++; _warnedNoEligibleVoices = false; _warnedNoFootsteps = false; _sensor.Invalidate(); VanillaSounds.Invalidate(); DoorSensor.Invalidate(); _lastCheckTime = Time.time; ResetProgress(log: false); if ((Object)(object)_player != (Object)null) { CutAudio(); } } private void ResetProgress(bool log) { _darkSeconds = 0f; _litSeconds = 0f; _litSamples = 0; _nextEventAtDark = Mathf.Max(0f, ModConfig.FirstSoundAfterSeconds.Value); _nextFootstepAtDark = Mathf.Max(0f, ModConfig.FootstepsAfterSeconds.Value); _lastStage = 0; _ranAtStage3 = false; _signalledStage3 = false; _lastCoilForm = 0; if (log) { Plugin.Log.LogInfo((object)"Back in the light long enough - everything reset."); } } private void Update() { if (!ModConfig.Enabled.Value || !SoundLibrary.FinishedLoading) { _lastCheckTime = Time.time; return; } try { HandleKeys(); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Key handling failed: " + ex.Message)); } if (Time.time < _nextCheck) { return; } try { Tick(); } catch (Exception ex2) { Plugin.Log.LogWarning((object)("Tick failed: " + ex2.Message)); } } private void Tick() { //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_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_02fa: Unknown result type (might be due to invalid IL or missing references) //IL_0355: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Max(0.1f, ModConfig.CheckInterval.Value); float num2 = Mathf.Clamp(Time.time - _lastCheckTime, 0f, 5f); _lastCheckTime = Time.time; _nextCheck = Time.time + num; PlayerControllerB localPlayer = GetLocalPlayer(); if ((Object)(object)localPlayer == (Object)null) { AccumulateLight(num2); return; } if (VanillaSounds.NeedsHarvest) { VanillaSounds.Harvest(); } bool isPlayerDead = localPlayer.isPlayerDead; if (isPlayerDead != _wasDead) { _wasDead = isPlayerDead; _sensor.Invalidate(); if (isPlayerDead && !ModConfig.PlayWhileDead.Value && (Object)(object)_player != (Object)null) { CutAudio(); } } if (!IsEligible(localPlayer)) { AccumulateLight(num2); return; } Vector3 eyePosition = GetEyePosition(localPlayer); float num3 = _sensor.Evaluate(localPlayer, eyePosition); if (!(num3 <= ModConfig.DarknessThreshold.Value)) { AccumulateLight(num2); if (ModConfig.DebugLogging.Value) { Plugin.Log.LogInfo((object)$"lit (score {num3:0.00}) - darkClock held at {_darkSeconds:0}s, reset in {Mathf.Max(0f, ModConfig.LightResetSeconds.Value - _litSeconds):0}s"); } return; } float num4 = (_sensor.OwnLightActive ? Mathf.Clamp01(ModConfig.FlashlightClockRate.Value) : 1f); if (num4 <= 0f) { _litSeconds = 0f; _litSamples = 0; if (ModConfig.DebugLogging.Value) { Plugin.Log.LogInfo((object)("dark, but your own light is on and FlashlightClockRate is 0 - clock frozen at " + Mathf.RoundToInt(_darkSeconds) + "s")); } return; } _litSeconds = 0f; _litSamples = 0; _darkSeconds += num2 * num4; int num5 = StageFor(_darkSeconds); if (num5 != _lastStage) { bool flag = num5 > _lastStage; _lastStage = num5; if (num5 > 0) { Plugin.Log.LogInfo((object)("Stage " + num5 + " reached (" + Mathf.RoundToInt(_darkSeconds) + "s in the dark).")); } if (flag && num5 >= 1) { float num6 = _darkSeconds + RandomGap(num5, localPlayer); if (num6 < _nextEventAtDark) { _nextEventAtDark = num6; } } } if (ModConfig.DebugLogging.Value) { Plugin.Log.LogInfo((object)$"dark (score {num3:0.00}) darkClock={_darkSeconds:0}s stage={num5} next in {Mathf.Max(0f, _nextEventAtDark - _darkSeconds):0}s"); } UpdateFootsteps(localPlayer, eyePosition, num5); if (num5 < 1 || _darkSeconds < _nextEventAtDark) { return; } if (ModConfig.OneSoundAtATime.Value && (Object)(object)_player != (Object)null && _player.AnythingPlaying) { _nextEventAtDark = _darkSeconds + 2f; return; } try { TriggerHallucination(localPlayer, eyePosition, num5, forced: false); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Skipped a hallucination: " + ex.GetType().Name + " - " + ex.Message)); } finally { _nextEventAtDark = _darkSeconds + RandomGap(num5, localPlayer); } } private void UpdateFootsteps(PlayerControllerB local, Vector3 eye, int stage) { //IL_00c1: Unknown result type (might be due to invalid IL or missing references) if (!ModConfig.FootstepsEnabled.Value || (Object)(object)_player == (Object)null || _darkSeconds < ModConfig.FootstepsAfterSeconds.Value || _darkSeconds < _nextFootstepAtDark) { return; } if (ModConfig.OneSoundAtATime.Value && (Object)(object)_player != (Object)null && _player.AnythingPlaying) { _nextFootstepAtDark = _darkSeconds + 2f; return; } if (Random.value >= ModConfig.FootstepChance.Value) { _nextFootstepAtDark = _darkSeconds + FootstepGap(local); if (ModConfig.DebugLogging.Value) { Plugin.Log.LogInfo((object)"Footsteps: skipped this one on purpose."); } return; } ApplyMixer(local); bool flag; try { flag = TryPhantomFootsteps(local, eye, Mathf.Max(1, stage), forced: false); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Skipped a footstep event: " + ex.GetType().Name + " - " + ex.Message)); flag = false; } if (flag) { _nextFootstepAtDark = _darkSeconds + FootstepGap(local); return; } _nextFootstepAtDark = _darkSeconds + 4f; if (ModConfig.DebugLogging.Value) { Plugin.Log.LogInfo((object)"Footsteps: no usable path from here, retrying in 4s."); } } private static float FootstepGap(PlayerControllerB local) { float value = ModConfig.FootstepMinGap.Value; float num = Mathf.Max(value, ModConfig.FootstepMaxGap.Value); float num2 = Random.Range(value, num); if (ModConfig.ShorterGapsWhenAlone.Value && (Object)(object)local != (Object)null && local.isPlayerAlone) { num2 *= 0.75f; } return Mathf.Max(4f, num2); } private void ApplyMixer(PlayerControllerB local) { if (!((Object)(object)_player == (Object)null)) { _player.MixerGroup = ((ModConfig.RouteThroughGameAudioMixer.Value && (Object)(object)local != (Object)null && (Object)(object)local.movementAudio != (Object)null) ? local.movementAudio.outputAudioMixerGroup : null); } } private void AccumulateLight(float elapsed) { _litSeconds += elapsed; _litSamples++; if (_darkSeconds > 0f && _litSamples >= 2 && _litSeconds >= ModConfig.LightResetSeconds.Value) { ResetProgress(log: true); } } private static float RandomGap(int stage, PlayerControllerB local) { float value; float value2; if (stage >= 3) { value = ModConfig.Stage3MinGap.Value; value2 = ModConfig.Stage3MaxGap.Value; } else if (stage == 2) { value = ModConfig.Stage2MinGap.Value; value2 = ModConfig.Stage2MaxGap.Value; } else { value = ModConfig.Stage1MinGap.Value; value2 = ModConfig.Stage1MaxGap.Value; } float num = Random.Range(value, Mathf.Max(value, value2)); if (ModConfig.ShorterGapsWhenAlone.Value && (Object)(object)local != (Object)null && local.isPlayerAlone) { num *= 0.7f; } return Mathf.Max(5f, num); } private static int StageFor(float darkSeconds) { float num = Mathf.Max(0f, ModConfig.FirstSoundAfterSeconds.Value); float num2 = Mathf.Max(num, ModConfig.Stage2AfterSeconds.Value); float num3 = Mathf.Max(num2, ModConfig.Stage3AfterSeconds.Value); if (darkSeconds < num) { return 0; } if (darkSeconds < num2) { return 1; } if (darkSeconds < num3) { return 2; } return 3; } private void QuietenSchedule(float seconds) { _nextEventAtDark = Mathf.Max(_nextEventAtDark, _darkSeconds + seconds); _nextFootstepAtDark = Mathf.Max(_nextFootstepAtDark, _darkSeconds + seconds); } private void HandleKeys() { //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_02ff: Unknown result type (might be due to invalid IL or missing references) //IL_03df: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_0393: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_029b: Unknown result type (might be due to invalid IL or missing references) //IL_0200: Unknown result type (might be due to invalid IL or missing references) //IL_0268: Unknown result type (might be due to invalid IL or missing references) //IL_02c8: Unknown result type (might be due to invalid IL or missing references) if (!ModConfig.EnableTestKey.Value) { return; } Keyboard current = Keyboard.current; if (current == null) { return; } PlayerControllerB localPlayer = GetLocalPlayer(); if ((Object)(object)localPlayer != (Object)null && (localPlayer.isTypingChat || localPlayer.inTerminalMenu || ((Object)(object)localPlayer.quickMenuManager != (Object)null && localPlayer.quickMenuManager.isMenuOpen))) { return; } if (_testKeyValid && Pressed(current, _testKey)) { PlayerControllerB localPlayer2 = GetLocalPlayer(); if ((Object)(object)localPlayer2 == (Object)null) { Plugin.Log.LogInfo((object)"Test key: no local player yet."); } else { Vector3 eyePosition = GetEyePosition(localPlayer2); int stage = Mathf.Max(1, StageFor(_darkSeconds)); QuietenSchedule(5f); Plugin.Log.LogInfo((object)("Test key - forcing a Stage " + stage + " sound. Light score here: " + _sensor.Evaluate(localPlayer2, eyePosition).ToString("0.00"))); if ((Object)(object)_player != (Object)null) { CutAudio(); } try { TriggerHallucination(localPlayer2, eyePosition, stage, forced: true); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Test sound failed: " + ex.Message)); } } } if (_coilKeyValid && Pressed(current, _coilKey)) { PlayerControllerB localPlayer3 = GetLocalPlayer(); if ((Object)(object)localPlayer3 == (Object)null) { Plugin.Log.LogInfo((object)"Coilhead key: no local player yet."); } else if ((Object)(object)VanillaSounds.PickLoudCoil() == (Object)null) { Plugin.Log.LogWarning((object)"Coilhead key: no spring clips were harvested on this level. Check the load line for the coilhead count."); } else { CutAudio(); QuietenSchedule(6f); Vector3 eyePosition2 = GetEyePosition(localPlayer3); _coilKeyForm = _coilKeyForm % 3 + 1; try { bool flag; if (_coilKeyForm == 2) { AudioClip[] coilheadMoveClips = VanillaSounds.CoilheadMoveClips; if (coilheadMoveClips == null || coilheadMoveClips.Length == 0) { Plugin.Log.LogWarning((object)"Coilhead key (2/3 running): no movement clips at all."); flag = false; } else { Plugin.Log.LogInfo((object)"Coilhead key (2/3): running at you."); flag = TryCoilheadRun(localPlayer3, eyePosition2, coilheadMoveClips, forced: true); if (!flag) { Plugin.Log.LogWarning((object)"Not enough room to run - it needs about 5 m of clear space behind you. Playing the coil instead."); } } } else if (_coilKeyForm == 3) { AudioClip[] coilheadSnapClips = VanillaSounds.CoilheadSnapClips; if (coilheadSnapClips == null || coilheadSnapClips.Length < 2) { Plugin.Log.LogWarning((object)"Coilhead key (3/3 burst): fewer than two short spring clips were found, so there is nothing to burst."); flag = false; } else { Plugin.Log.LogInfo((object)"Coilhead key (3/3): a burst of snaps closing in."); flag = TryCoilheadBurst(localPlayer3, eyePosition2, coilheadSnapClips, forced: true); if (!flag) { Plugin.Log.LogWarning((object)"Not enough room for the burst. Playing the coil instead."); } } } else { Plugin.Log.LogInfo((object)"Coilhead key (1/3): the single loud coil."); flag = TryCoilheadLoud(localPlayer3, eyePosition2, forced: true); if (!flag) { Plugin.Log.LogWarning((object)"The loud coil could not be placed."); } } if (!flag && _coilKeyForm != 1) { TryCoilheadLoud(localPlayer3, eyePosition2, forced: true); } } catch (Exception ex2) { Plugin.Log.LogWarning((object)("Coilhead key failed: " + ex2.Message)); } } } if (_footKeyValid && Pressed(current, _footKey)) { PlayerControllerB localPlayer4 = GetLocalPlayer(); if ((Object)(object)localPlayer4 == (Object)null) { Plugin.Log.LogInfo((object)"Footstep key: no local player yet."); } else { int stage2 = Mathf.Max(1, StageFor(_darkSeconds)); QuietenSchedule(6f); Plugin.Log.LogInfo((object)("Footstep key - forcing a Stage " + stage2 + " walk.")); if ((Object)(object)_player != (Object)null) { CutAudio(); ApplyMixer(localPlayer4); } try { if (!TryPhantomFootsteps(localPlayer4, GetEyePosition(localPlayer4), stage2, forced: true)) { Plugin.Log.LogWarning((object)"No room to walk anywhere near you right now - try a wider space."); } } catch (Exception ex3) { Plugin.Log.LogWarning((object)("Footstep key failed: " + ex3.Message)); } } } if (_stageKeyValid && Pressed(current, _stageKey)) { int num = StageFor(_darkSeconds); if (num >= 3) { ResetProgress(log: false); Plugin.Log.LogInfo((object)"Stage key - reset to silence."); return; } float num2 = ((num <= 0) ? ModConfig.FirstSoundAfterSeconds.Value : ((num == 1) ? ModConfig.Stage2AfterSeconds.Value : ModConfig.Stage3AfterSeconds.Value)); _darkSeconds = Mathf.Max(_darkSeconds, num2) + 0.5f; _litSeconds = 0f; _lastStage = StageFor(_darkSeconds); _nextEventAtDark = _darkSeconds + 2f; _nextFootstepAtDark = _darkSeconds + 8f; Plugin.Log.LogInfo((object)("Stage key - jumped to Stage " + _lastStage + ". Next sound in a couple of seconds (stay in the dark).")); } } private static bool Pressed(Keyboard kb, Key k) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) try { KeyControl val = kb[k]; return val != null && ((ButtonControl)val).wasPressedThisFrame; } catch { return false; } } private static Vector3 GetEyePosition(PlayerControllerB local) { //IL_0009: 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_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0086: 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_006b: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)local == (Object)null) { return Vector3.zero; } if (local.isPlayerDead) { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance != (Object)null && (Object)(object)instance.spectateCamera != (Object)null && ((Behaviour)instance.spectateCamera).enabled) { return ((Component)instance.spectateCamera).transform.position; } } if ((Object)(object)local.gameplayCamera != (Object)null) { return ((Component)local.gameplayCamera).transform.position; } return ((Component)local).transform.position + Vector3.up * 1.6f; } private static PlayerControllerB GetLocalPlayer() { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null) { return null; } PlayerControllerB localPlayerController = instance.localPlayerController; if ((Object)(object)localPlayerController == (Object)null) { return null; } if (!localPlayerController.isPlayerControlled && !localPlayerController.isPlayerDead) { return null; } return localPlayerController; } private bool IsEligible(PlayerControllerB local) { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null) { return false; } bool isPlayerDead = local.isPlayerDead; if (isPlayerDead && !ModConfig.PlayWhileDead.Value) { return false; } if (instance.inShipPhase && !ModConfig.AllowInShip.Value) { return false; } if (local.isInHangarShipRoom && !ModConfig.AllowInShip.Value) { return false; } if (ModConfig.OnlyInsideFacility.Value && !local.isInsideFactory && !isPlayerDead) { return false; } return true; } private void TriggerHallucination(PlayerControllerB local, Vector3 eye, int stage, bool forced) { //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_009c: 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_0112: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_player == (Object)null) { return; } _player.Build(Mathf.Max(2, ModConfig.MaxSimultaneousSounds.Value)); ApplyMixer(local); bool flag = !forced && !_signalledStage3 && (Object)(object)VanillaSounds.PickLoudCoil() != (Object)null; if ((stage >= 3 && ModConfig.UseVanillaSounds.Value && (flag || Random.value < ModConfig.SignatureChance.Value) && TrySignature(local, eye, forced)) || (ModConfig.UseVanillaSounds.Value && Random.value < ModConfig.DoorSoundChance.Value && TryDoorSound(local, eye, stage, forced))) { return; } SchizoClip schizoClip = PickClip(stage); if (schizoClip == null) { if (forced) { Plugin.Log.LogWarning((object)"Nothing to play - no clips were loaded from the Sounds folders."); } } else if (PlayPick(schizoClip, local, eye, stage, 1f, forced) && !ModConfig.OneSoundAtATime.Value && Random.value < ModConfig.LayerChance.Value) { SchizoClip schizoClip2 = PickClip(Mathf.Max(1, stage - 1)); if (schizoClip2 != null && schizoClip2 != schizoClip) { PlayPick(schizoClip2, local, eye, Mathf.Max(1, stage - 1), 0.5f, forced: false); } } } private bool TrySignature(PlayerControllerB local, Vector3 eye, bool forced) { //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) bool flag = (Object)(object)VanillaSounds.CoilheadLoudest != (Object)null; bool flag2 = VanillaSounds.Signature.Count > 0; if (flag && !forced && !_signalledStage3 && TryCoilheadLoud(local, eye, forced)) { _signalledStage3 = true; return true; } if (flag && (!flag2 || Random.value < ModConfig.CoilheadBurstChance.Value) && TryCoilhead(local, eye, forced)) { if (!forced) { _signalledStage3 = true; } return true; } if (!flag2) { return false; } SchizoClip sting = VanillaSounds.Signature[Random.Range(0, VanillaSounds.Signature.Count)]; if (!PlaySignatureSting(sting, local, eye, forced)) { return false; } if (!forced) { _signalledStage3 = true; } return true; } private bool PlaySignatureSting(SchizoClip sting, PlayerControllerB local, Vector3 eye, bool forced) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: 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_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0063: 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) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0083: 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_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: 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_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_player == (Object)null || sting == null || (Object)(object)sting.Clip == (Object)null) { return false; } int mask = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.collidersAndRoomMask : 0); Vector3 val = DirectionAt(local, Random.Range(70f, 290f)); Vector3 val2 = new Vector3(val.x, 0f, val.z); val = ((Vector3)(ref val2)).normalized; float num = Random.Range(2f, 4.5f); float num2 = Mathf.Clamp(ClearDistance(eye, val, num, mask) - 0.3f, 1f, num); Vector3 val3 = eye + val * num2; float lowPassCutoff = Mathf.Max(MuffleCutoff(3, eye, val3, insideHead: false), 6000f); if (!_player.Play(sting.Clip, val3, insideHead: false, Mathf.Clamp01(ModConfig.Stage3Volume.Value), 1f + Random.Range(-0.04f, 0.04f), radioEffect: false, 20f, lowPassCutoff, 0f, sting.MaxPlaySeconds, 0, dry: true, 4f)) { return false; } if (ModConfig.DebugLogging.Value || forced) { Plugin.Log.LogInfo((object)("Heard: the ghost girl - '" + sting.FileName + "' at " + num2.ToString("0.0") + "m")); } return true; } private bool TryCoilhead(PlayerControllerB local, Vector3 eye, bool forced) { //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) AudioClip[] coilheadSnapClips = VanillaSounds.CoilheadSnapClips; AudioClip[] coilheadMoveClips = VanillaSounds.CoilheadMoveClips; float num = Mathf.Max(0f, ModConfig.CoilheadLoudChance.Value); float num2 = ((coilheadMoveClips != null && coilheadMoveClips.Length != 0) ? Mathf.Max(0f, ModConfig.CoilheadRunShare.Value) : 0f); float num3 = ((coilheadSnapClips != null && coilheadSnapClips.Length > 1) ? Mathf.Max(0f, ModConfig.CoilheadBurstShare.Value) : 0f); if (_lastCoilForm == 1) { num *= 0.25f; } else if (_lastCoilForm == 2) { num2 *= 0.25f; } else if (_lastCoilForm == 3) { num3 *= 0.25f; } float num4 = num + num2 + num3; int num5 = 1; if (num4 > 0f) { float num6 = Random.value * num4; num5 = ((num6 < num) ? 1 : ((!(num6 < num + num2)) ? 3 : 2)); } for (int i = 0; i < 3; i++) { int num7 = (num5 - 1 + i) % 3 + 1; if (num7 == 1 && TryCoilheadLoud(local, eye, forced)) { Remember(1, forced); return true; } if (num7 == 2 && TryCoilheadRun(local, eye, coilheadMoveClips, forced)) { Remember(2, forced); return true; } if (num7 == 3 && TryCoilheadBurst(local, eye, coilheadSnapClips, forced)) { Remember(3, forced); return true; } } return false; } private void Remember(int form, bool forced) { if (!forced) { _lastCoilForm = form; } } private bool TryCoilheadRun(PlayerControllerB local, Vector3 eye, AudioClip[] move, bool forced) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004f: 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_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0086: 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_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: 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_00cc: 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) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_player == (Object)null || move == null || move.Length == 0) { return false; } int mask = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.collidersAndRoomMask : 0); Vector3 val = DirectionAt(local, 180f); Vector3 val2 = new Vector3(val.x, 0f, val.z); val = ((Vector3)(ref val2)).normalized; float num = ClearDistance(eye, val, ModConfig.CoilheadRunDistance.Value, mask); for (int i = 0; i < 6; i++) { Vector3 val3 = DirectionAt(local, Random.Range(110f, 250f)); val2 = new Vector3(val3.x, 0f, val3.z); Vector3 normalized = ((Vector3)(ref val2)).normalized; float num2 = ClearDistance(eye, normalized, ModConfig.CoilheadRunDistance.Value, mask); if (num2 > num) { val = normalized; num = num2; } if (num >= ModConfig.CoilheadRunDistance.Value * 0.8f) { break; } } float num3 = num - 0.4f; if (num3 < 4f) { return false; } float num4 = 1.4f; Vector3 val4 = eye + val * num3; Vector3 val5 = eye + val * num4; float num5 = Mathf.Clamp(ModConfig.CoilheadRunStepInterval.Value, 0.08f, 0.5f); int num6 = Mathf.Clamp(Mathf.RoundToInt((num3 - num4) / 0.9f), 4, 16); float cutoffFrom = Mathf.Max(MuffleCutoff(3, eye, val4, insideHead: false), 2600f); float volume = Mathf.Clamp01(ModConfig.CoilheadVolume.Value * 0.75f); if (!_player.PlayFootsteps(move, val4, val5, num6, num5, volume, 1f + Random.Range(-0.04f, 0.04f), 26f, cutoffFrom, 19000f, varyPitchPerStep: false)) { return false; } AudioClip val6 = VanillaSounds.PickLoudCoil(); if ((Object)(object)val6 != (Object)null) { float delay = (float)num6 * num5 + Random.Range(0.05f, 0.25f); try { ((MonoBehaviour)this).StartCoroutine(PlayCoilAfter(val6, val5, delay, _sceneToken, _cutToken)); } catch { } } if (ModConfig.DebugLogging.Value || forced) { Plugin.Log.LogInfo((object)("Heard: coilhead - running in from " + num3.ToString("0.0") + "m over " + num6 + " steps, then the coil")); } return true; } private IEnumerator PlayCoilAfter(AudioClip clip, Vector3 at, float delay, int token, int cut) { //IL_0015: 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) yield return (object)new WaitForSeconds(delay); if (!((Object)(object)_player == (Object)null) && !((Object)(object)clip == (Object)null) && token == _sceneToken && cut == _cutToken && ModConfig.Enabled.Value) { PlayerControllerB localPlayer = GetLocalPlayer(); if (!((Object)(object)localPlayer == (Object)null) && IsEligible(localPlayer)) { _player.Play(clip, at, insideHead: false, Mathf.Clamp01(ModConfig.CoilheadVolume.Value), 1f + Random.Range(-0.03f, 0.03f), radioEffect: false, 24f, 22000f, 0f, 0f, 0, dry: true, 6f); } } } private bool TryCoilheadBurst(PlayerControllerB local, Vector3 eye, AudioClip[] springs, bool forced) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006d: 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_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: 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_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_player == (Object)null || springs == null || springs.Length == 0) { return false; } int mask = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.collidersAndRoomMask : 0); Vector3 val = Vector3.forward; float num = 0f; for (int i = 0; i < 5; i++) { Vector3 val2 = DirectionAt(local, Random.Range(140f, 220f)); Vector3 val3 = new Vector3(val2.x, 0f, val2.z); Vector3 normalized = ((Vector3)(ref val3)).normalized; float want = Random.Range(3.5f, 6.5f); float num2 = UsableDistance(eye, normalized, want, mask); if (num2 > num) { val = normalized; num = num2; } if (num2 >= 2.4f) { break; } } if (num < 1.7f) { return false; } float num3 = Mathf.Clamp(ModConfig.CoilheadBurstDistance.Value, 0.8f, num - 0.7f); Vector3 val4 = eye + val * num; Vector3 val5 = eye + val * num3; float num4 = 0.25f; for (int j = 0; j < springs.Length; j++) { if ((Object)(object)springs[j] != (Object)null && springs[j].length > num4) { num4 = springs[j].length; } } float interval = Mathf.Clamp(num4 * 0.6f, 0.12f, 0.4f); int steps = Random.Range(3, 6); float pitch = 1f + Random.Range(-0.05f, 0.05f); float num5 = Mathf.Max(MuffleCutoff(3, eye, val5, insideHead: false), 7000f); float volume = Mathf.Clamp01(ModConfig.Stage3Volume.Value * 0.5f); if (!_player.PlayFootsteps(springs, val4, val5, steps, interval, volume, pitch, 14f, num5, num5, varyPitchPerStep: false)) { return false; } if (ModConfig.DebugLogging.Value || forced) { Plugin.Log.LogInfo((object)("Heard: coilhead - " + steps + " snaps closing from " + num.ToString("0.0") + "m to " + num3.ToString("0.0") + "m behind you")); } return true; } private bool TryCoilheadLoud(PlayerControllerB local, Vector3 eye, bool forced) { //IL_0040: 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) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //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) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0067: 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_008e: 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_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0102: 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) //IL_0138: 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) AudioClip val = VanillaSounds.PickLoudCoil(); if ((Object)(object)_player == (Object)null || (Object)(object)val == (Object)null) { return false; } int mask = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.collidersAndRoomMask : 0); Vector3 val2 = DirectionAt(local, 180f); Vector3 val3 = new Vector3(val2.x, 0f, val2.z); val2 = ((Vector3)(ref val3)).normalized; float num = ClearDistance(eye, val2, 3.2f, mask); for (int i = 0; i < 5; i++) { Vector3 val4 = DirectionAt(local, Random.Range(125f, 235f)); val3 = new Vector3(val4.x, 0f, val4.z); Vector3 normalized = ((Vector3)(ref val3)).normalized; float num2 = ClearDistance(eye, normalized, 3.2f, mask); if (num2 > num) { val2 = normalized; num = num2; } if (num >= 2.2f) { break; } } float num3 = Mathf.Clamp(num - 0.35f, 0.6f, 3f); Vector3 worldPos = eye + val2 * num3; float volume = Mathf.Clamp01(ModConfig.CoilheadVolume.Value); float pitch = 1f + Random.Range(-0.03f, 0.03f); if (!_player.Play(val, worldPos, insideHead: false, volume, pitch, radioEffect: false, 24f, 22000f, 0f, 0f, 0, dry: true, 6f)) { return false; } if (ModConfig.DebugLogging.Value || forced) { Plugin.Log.LogInfo((object)("Heard: coilhead - loud coil '" + ((Object)val).name + "' (" + val.length.ToString("0.00") + "s) at " + num3.ToString("0.0") + "m behind you, volume " + volume.ToString("0.00"))); } return true; } private void CutAudio() { _cutToken++; if ((Object)(object)_player != (Object)null) { _player.StopAll(); } } private static float ClearDistance(Vector3 eye, Vector3 dir, float want, int mask) { //IL_000d: 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) if (mask == 0 || want <= 0f) { return want; } RaycastHit val = default(RaycastHit); if (Physics.Raycast(eye, dir, ref val, want, mask, (QueryTriggerInteraction)1)) { return ((RaycastHit)(ref val)).distance; } return want; } private bool TryDoorSound(PlayerControllerB local, Vector3 eye, int stage, bool forced) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0053: 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_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: 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_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_player == (Object)null) { return false; } if (VanillaSounds.DoorOpenClips.Count == 0 && VanillaSounds.DoorShutClips.Count == 0) { return false; } Vector3 lookForward = (((Object)(object)local != (Object)null && (Object)(object)local.gameplayCamera != (Object)null) ? ((Component)local.gameplayCamera).transform.forward : Vector3.forward); if (!DoorSensor.TryPickDoor(eye, lookForward, out var position)) { return false; } List doorOpenClips = VanillaSounds.DoorOpenClips; List doorShutClips = VanillaSounds.DoorShutClips; bool flag = doorOpenClips.Count > 0 && (doorShutClips.Count == 0 || Random.value < ModConfig.DoorOpenShare.Value); List list = (flag ? doorOpenClips : doorShutClips); AudioClip val = list[Random.Range(0, list.Count)]; float num = Vector3.Distance(eye, position); float volume = Mathf.Clamp01(ModConfig.DoorVolume.Value); float pitch = 1f + Random.Range(-0.05f, 0.05f); float num2 = MuffleCutoff(stage, eye, position, insideHead: false); if (!_player.Play(val, position, insideHead: false, volume, pitch, radioEffect: false, Mathf.Max(18f, num * 1.8f), num2, 0f)) { return false; } if (flag && doorShutClips.Count > 0 && Random.value < ModConfig.DoorPairChance.Value) { AudioClip clip = doorShutClips[Random.Range(0, doorShutClips.Count)]; float delay = val.length * Random.Range(0.7f, 1f) + Random.Range(0.5f, 2f); try { ((MonoBehaviour)this).StartCoroutine(PlayDelayed(clip, position, volume, pitch, num2, num, delay, _sceneToken, _cutToken)); } catch { } } if (ModConfig.DebugLogging.Value || forced) { Plugin.Log.LogInfo((object)("Heard: a real door at " + num.ToString("0.0") + "m - " + (flag ? "creaking open" : "slamming"))); } return true; } private IEnumerator PlayDelayed(AudioClip clip, Vector3 at, float volume, float pitch, float cutoff, float dist, float delay, int token, int cut) { //IL_0015: 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) yield return (object)new WaitForSeconds(delay); if (!((Object)(object)_player == (Object)null) && !((Object)(object)clip == (Object)null) && token == _sceneToken && cut == _cutToken && ModConfig.Enabled.Value) { PlayerControllerB localPlayer = GetLocalPlayer(); if (!((Object)(object)localPlayer == (Object)null) && IsEligible(localPlayer)) { _player.Play(clip, at, insideHead: false, volume, pitch, radioEffect: false, Mathf.Max(18f, dist * 1.8f), cutoff, 0f); } } } private bool PlayPick(SchizoClip pick, PlayerControllerB local, Vector3 eye, int eventStage, float volumeScale, bool forced) { //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Unknown result type (might be due to invalid IL or missing references) if (pick == null || (Object)(object)pick.Clip == (Object)null || (Object)(object)_player == (Object)null) { return false; } int num = (pick.IsVoice ? Mathf.Clamp(Mathf.Max(2, eventStage), 1, 3) : Mathf.Clamp(Mathf.Min(pick.Stage, eventStage), 1, 3)); GetStageMix(num, out var minD, out var maxD, out var volume); if (pick.IsWhisper) { minD = ModConfig.WhisperMinDistance.Value; maxD = Mathf.Max(minD, ModConfig.WhisperMaxDistance.Value); volume = ModConfig.WhisperVolume.Value; } bool flag = (num >= 2 || pick.IsWhisper) && Random.value < ModConfig.InsideHeadChance.Value; bool flag2 = pick.IsVoice && !flag && Random.value < ModConfig.RadioEffectChance.Value; float value = ModConfig.PitchVariance.Value; float num2 = 1f + Random.Range(0f - value, value); num2 = Mathf.Clamp(num2, 0.25f, 3f); float value2 = ModConfig.VolumeVariance.Value; volume = Mathf.Clamp01(volume * volumeScale * (1f + Random.Range(0f - value2, value2))); float behindBias = ((num >= 3) ? 0.85f : ((num == 2) ? 0.78f : 0.7f)); bool allowThroughWall = !flag && !pick.IsWhisper && Random.value < ModConfig.ThroughWallChance.Value; Vector3 val = (flag ? eye : PickSpawnPoint(eye, local, minD, maxD, behindBias, allowThroughWall)); float falloffDistance = Mathf.Max(minD, maxD) * 1.7f; float num3 = MuffleCutoff(num, eye, val, flag); if (pick.IsWhisper) { num3 = Mathf.Max(num3, 5200f); } float num4 = RandomStartTime(pick.Clip); if (pick.MaxPlaySeconds > 0.1f && pick.Clip.length > pick.MaxPlaySeconds + 1f) { num4 = Random.Range(0f, pick.Clip.length - pick.MaxPlaySeconds - 0.5f); } if (!_player.Play(pick.Clip, val, flag, volume, num2, flag2, falloffDistance, num3, num4, pick.MaxPlaySeconds)) { if (forced) { Plugin.Log.LogInfo((object)"All sound channels busy - try again in a moment."); } return false; } if (ModConfig.DebugLogging.Value || forced) { Plugin.Log.LogInfo((object)("Heard: '" + pick.FileName + "' [stage " + num + "]" + (pick.IsWhisper ? " [close voice]" : string.Empty) + (pick.IsVanilla ? " [from the game]" : string.Empty) + (pick.IsVoice ? " [teammate]" : string.Empty) + (flag ? " [in your head]" : string.Empty) + (flag2 ? " [radio]" : string.Empty) + " muffle=" + Mathf.RoundToInt(num3) + "Hz" + ((num4 > 0.01f) ? " [mid-clip]" : string.Empty))); } return true; } private static float MuffleCutoff(int stage, Vector3 eye, Vector3 pos, bool insideHead) { //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) float num = ((stage >= 3) ? 3400f : ((stage == 2) ? 2100f : 1250f)); float num2 = Mathf.Clamp01(ModConfig.MuffleAmount.Value); float num3 = Mathf.Lerp(20000f, num, num2); num3 *= Random.Range(0.75f, 1.35f); if (insideHead) { return Mathf.Clamp(num3 * 1.6f, 220f, 22000f); } if ((Object)(object)StartOfRound.Instance != (Object)null) { int collidersAndRoomMask = StartOfRound.Instance.collidersAndRoomMask; if (collidersAndRoomMask != 0 && Physics.Linecast(eye, pos, collidersAndRoomMask, (QueryTriggerInteraction)1)) { num3 *= 0.5f; } } return Mathf.Clamp(num3, 220f, 22000f); } private static float RandomStartTime(AudioClip clip) { if ((Object)(object)clip == (Object)null || clip.length < 2.5f) { return 0f; } if (Random.value >= ModConfig.RandomStartChance.Value) { return 0f; } return clip.length * Random.Range(0.12f, 0.45f); } private bool TryPhantomFootsteps(PlayerControllerB local, Vector3 eye, int stage, bool forced) { //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_0277: Unknown result type (might be due to invalid IL or missing references) //IL_027c: Unknown result type (might be due to invalid IL or missing references) //IL_028a: Unknown result type (might be due to invalid IL or missing references) //IL_028f: Unknown result type (might be due to invalid IL or missing references) //IL_0291: Unknown result type (might be due to invalid IL or missing references) //IL_0292: Unknown result type (might be due to invalid IL or missing references) //IL_0294: Unknown result type (might be due to invalid IL or missing references) //IL_0295: Unknown result type (might be due to invalid IL or missing references) //IL_02a2: Unknown result type (might be due to invalid IL or missing references) //IL_02a7: Unknown result type (might be due to invalid IL or missing references) //IL_02a9: Unknown result type (might be due to invalid IL or missing references) //IL_02aa: Unknown result type (might be due to invalid IL or missing references) //IL_02ac: Unknown result type (might be due to invalid IL or missing references) //IL_02ad: Unknown result type (might be due to invalid IL or missing references) //IL_02ba: Unknown result type (might be due to invalid IL or missing references) //IL_02bf: Unknown result type (might be due to invalid IL or missing references) //IL_02c1: Unknown result type (might be due to invalid IL or missing references) //IL_02c3: Unknown result type (might be due to invalid IL or missing references) //IL_02c5: Unknown result type (might be due to invalid IL or missing references) //IL_02ca: Unknown result type (might be due to invalid IL or missing references) //IL_01e3: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Unknown result type (might be due to invalid IL or missing references) //IL_02ef: Unknown result type (might be due to invalid IL or missing references) //IL_02f1: Unknown result type (might be due to invalid IL or missing references) //IL_02f3: Unknown result type (might be due to invalid IL or missing references) //IL_02f8: Unknown result type (might be due to invalid IL or missing references) //IL_0211: Unknown result type (might be due to invalid IL or missing references) //IL_0212: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Unknown result type (might be due to invalid IL or missing references) //IL_021d: Unknown result type (might be due to invalid IL or missing references) //IL_021f: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Unknown result type (might be due to invalid IL or missing references) //IL_022b: Unknown result type (might be due to invalid IL or missing references) //IL_0391: Unknown result type (might be due to invalid IL or missing references) //IL_0392: Unknown result type (might be due to invalid IL or missing references) //IL_03c3: Unknown result type (might be due to invalid IL or missing references) //IL_03c5: Unknown result type (might be due to invalid IL or missing references) //IL_0489: Unknown result type (might be due to invalid IL or missing references) //IL_048a: Unknown result type (might be due to invalid IL or missing references) //IL_048c: Unknown result type (might be due to invalid IL or missing references) //IL_0491: Unknown result type (might be due to invalid IL or missing references) AudioClip[] array = FootstepClipsToUse(local); if (array == null || array.Length == 0) { if (forced || ModConfig.DebugLogging.Value) { Plugin.Log.LogWarning((object)"No footstep clips available (the game's surface clips came back empty)."); } return false; } float value = ModConfig.FootstepStartMin.Value; float num = Mathf.Max(value, ModConfig.FootstepStartMax.Value); float num2 = Mathf.Max(0.9f, Mathf.Min(ModConfig.FootstepEndDistance.Value, value)); float value2 = ModConfig.FootstepVolume.Value; bool flag = stage >= 3 && (!_ranAtStage3 || Random.value < ModConfig.RunChanceAtStage3.Value); float num3 = (flag ? ModConfig.RunStepInterval.Value : ModConfig.WalkStepInterval.Value); bool flag2 = Random.value < ((stage >= 3) ? 0.8f : 0.5f); float num4 = ((stage >= 3) ? 0.85f : ((stage == 2) ? 0.78f : 0.7f)); int mask = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.collidersAndRoomMask : 0); float y = ((Component)local).transform.position.y; bool flag3 = false; for (int i = 0; i < 6; i++) { float num5 = ((Random.value < num4) ? Random.Range(115f, 245f) : Random.Range(-115f, 115f)); Vector3 val; Vector3 val2; Vector3 val3; if (flag2) { Vector3 dir = DirectionAt(local, num5); Vector3 dir2 = DirectionAt(local, num5 + Random.Range(-15f, 15f)); float num6 = Random.Range(value, num); flag3 = Random.value < ModConfig.FootstepAroundCornerChance.Value; float num7 = UsableDistance(eye, dir, num6, mask); float num8 = (flag3 ? Mathf.Min(num6, num7 + Random.Range(1.5f, 3.5f)) : num7); float num9 = Mathf.Min(num2, num8 - 1.8f); if (num9 < 0.8f) { continue; } num9 = Mathf.Min(num9, UsableDistance(eye, dir2, num9, mask)); if (num9 < 0.8f || num8 - num9 < 1.8f) { continue; } val = GroundPoint(eye, dir, num8, y); val2 = GroundPoint(eye, dir2, num9, y); } else { float num10 = Random.Range(70f, 130f) * ((Random.value < 0.5f) ? (-1f) : 1f); float want = Random.Range(value, num) * 0.7f; Vector3 dir3 = DirectionAt(local, num5 - num10 * 0.5f); Vector3 dir4 = DirectionAt(local, num5 + num10 * 0.5f); val = GroundPoint(eye, dir3, UsableDistance(eye, dir3, want, mask), y); val2 = GroundPoint(eye, dir4, UsableDistance(eye, dir4, want, mask), y); val3 = val - val2; if (((Vector3)(ref val3)).sqrMagnitude < 3.2399998f) { continue; } } float num11 = (flag ? 1.7f : 0.85f); val3 = val - val2; int num12 = Mathf.Clamp(Mathf.RoundToInt(((Vector3)(ref val3)).magnitude / num11), 3, 16); int num13 = Mathf.Max(3, Mathf.FloorToInt(10f / Mathf.Max(0.05f, num3))); if (num12 > num13) { num12 = num13; } float pitch = 1f + Random.Range(0f - ModConfig.PitchVariance.Value, ModConfig.PitchVariance.Value); float value3 = ModConfig.VolumeVariance.Value; float volume = Mathf.Clamp01(value2 * (1f + Random.Range((0f - value3) * 0.5f, value3 * 0.5f))); float num14 = Mathf.Max(MuffleCutoff(3, eye, val2, insideHead: false), 4500f); float cutoffFrom = (flag3 ? Mathf.Min(num14, 1100f) : num14); if (!_player.PlayFootsteps(array, val, val2, num12, num3, volume, pitch, num * 2.2f, cutoffFrom, num14)) { if (ModConfig.DebugLogging.Value) { Plugin.Log.LogInfo((object)"Footsteps: found a path but no free sound channel."); } return false; } if (flag && !forced) { _ranAtStage3 = true; } if (ModConfig.DebugLogging.Value || forced) { ManualLogSource log = Plugin.Log; string[] obj = new string[9] { "Heard: ", num12.ToString(), flag ? " running" : " walking", " footsteps", flag2 ? " [coming towards you]" : " [crossing behind you]", flag3 ? " [from around a corner]" : string.Empty, " ", null, null }; val3 = eye - val; obj[7] = ((Vector3)(ref val3)).magnitude.ToString("0.0"); obj[8] = "m away"; log.LogInfo((object)string.Concat(obj)); } return true; } return false; } private static AudioClip[] FootstepClipsToUse(PlayerControllerB local) { AudioClip[] footstepClips = SoundLibrary.FootstepClips; bool flag = footstepClips != null && footstepClips.Length != 0; if (!ModConfig.UseGameFootsteps.Value) { if (!flag && !_warnedNoFootsteps) { _warnedNoFootsteps = true; Plugin.Log.LogWarning((object)"UseGameFootsteps is off but Sounds/Footsteps/ is empty, so there are no footsteps to play. Add clips or turn it back on."); } if (!flag) { return null; } return footstepClips; } AudioClip[] array = VanillaSounds.FootstepClipsFor(local); if (array != null && array.Length != 0) { if (flag && Random.value < 0.3f) { return footstepClips; } return array; } if (!flag) { return null; } return footstepClips; } private static void GetStageMix(int stage, out float minD, out float maxD, out float volume) { if (stage >= 3) { minD = ModConfig.Stage3MinDistance.Value; maxD = ModConfig.Stage3MaxDistance.Value; volume = ModConfig.Stage3Volume.Value; } else if (stage == 2) { minD = ModConfig.Stage2MinDistance.Value; maxD = ModConfig.Stage2MaxDistance.Value; volume = ModConfig.Stage2Volume.Value; } else { minD = ModConfig.Stage1MinDistance.Value; maxD = ModConfig.Stage1MaxDistance.Value; volume = ModConfig.Stage1Volume.Value; } maxD = Mathf.Max(minD, maxD); } private static Vector3 DirectionAt(PlayerControllerB local, float angle) { //IL_0025: 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_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) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006b: 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_0057: Unknown result type (might be due to invalid IL or missing references) Transform val = (((Object)(object)local.gameplayCamera != (Object)null) ? ((Component)local.gameplayCamera).transform : ((Component)local).transform); Vector3 forward = default(Vector3); ((Vector3)(ref forward))..ctor(val.forward.x, 0f, val.forward.z); if (((Vector3)(ref forward)).sqrMagnitude < 0.0001f) { forward = Vector3.forward; } ((Vector3)(ref forward)).Normalize(); return Quaternion.AngleAxis(angle, Vector3.up) * forward; } private static float UsableDistance(Vector3 eye, Vector3 dir, float want, int mask) { //IL_0017: 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_0028: 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_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_0037: Unknown result type (might be due to invalid IL or missing references) if (mask == 0 || want <= 0f) { return Mathf.Max(0.8f, want); } Vector3 val = new Vector3(dir.x, 0f, dir.z); Vector3 normalized = ((Vector3)(ref val)).normalized; RaycastHit val2 = default(RaycastHit); if (Physics.Raycast(eye, normalized, ref val2, want, mask, (QueryTriggerInteraction)1)) { return Mathf.Max(0.8f, ((RaycastHit)(ref val2)).distance - 0.6f); } return want; } private static Vector3 GroundPoint(Vector3 eye, Vector3 dir, float dist, float feetY) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: 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_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0020: 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_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) Vector3 val = new Vector3(dir.x, 0f, dir.z); Vector3 normalized = ((Vector3)(ref val)).normalized; Vector3 result = eye + normalized * dist; result.y = feetY + 0.15f; return result; } private SchizoClip PickClip(int stage) { if (stage >= 2 && SoundLibrary.Voices.Count > 0 && Random.value < ModConfig.VoiceChance.Value) { SchizoClip schizoClip = PickVoice(); if (schizoClip != null) { return schizoClip; } } if (Random.value < ModConfig.WhisperChance.Value) { SchizoClip schizoClip2 = PickWhisper(); if (schizoClip2 != null) { return schizoClip2; } } int stage2 = stage; if (ModConfig.MixInLowerStages.Value && stage > 1) { float value = Random.value; stage2 = ((stage != 2) ? ((value < 0.5f) ? 3 : ((!(value < 0.8f)) ? 1 : 2)) : ((!(value < 0.65f)) ? 1 : 2)); } bool flag = ModConfig.UseVanillaSounds.Value && VanillaSounds.Any; bool flag2 = flag && Random.value < ModConfig.VanillaShare.Value; SchizoClip schizoClip3 = PickAtStage(stage2, flag2); if (schizoClip3 == null && flag) { schizoClip3 = PickAtStage(stage2, !flag2); } if (schizoClip3 == null) { schizoClip3 = PickFromStage(stage2, flag2); } if (schizoClip3 == null) { bool flag3 = !flag2; if (!flag3 || flag) { schizoClip3 = PickFromStage(stage2, flag3); } } if (schizoClip3 == null) { schizoClip3 = PickWhisper(); } return schizoClip3; } private static SchizoClip PickAtStage(int stage, bool vanilla) { List list = (vanilla ? VanillaSounds.PoolForStage(stage) : SoundLibrary.PoolForStage(stage)); if (list.Count <= 0) { return null; } return list[Random.Range(0, list.Count)]; } private static SchizoClip PickFromStage(int stage, bool vanilla) { for (int num = stage; num >= 1; num--) { List list = (vanilla ? VanillaSounds.PoolForStage(num) : SoundLibrary.PoolForStage(num)); if (list.Count > 0) { return list[Random.Range(0, list.Count)]; } } for (int i = stage + 1; i <= 3; i++) { List list2 = (vanilla ? VanillaSounds.PoolForStage(i) : SoundLibrary.PoolForStage(i)); if (list2.Count > 0) { return list2[Random.Range(0, list2.Count)]; } } if (!vanilla && stage >= 2 && SoundLibrary.Voices.Count > 0) { return SoundLibrary.Voices[Random.Range(0, SoundLibrary.Voices.Count)]; } return null; } private static SchizoClip PickWhisper() { List whispers = SoundLibrary.Whispers; List list = (ModConfig.UseVanillaSounds.Value ? VanillaSounds.Whispers : null); bool flag = whispers.Count > 0; bool flag2 = list != null && list.Count > 0; if (!flag && !flag2) { return null; } if (flag2 && flag && Random.value < Mathf.Min(0.12f, ModConfig.VanillaShare.Value)) { return list[Random.Range(0, list.Count)]; } if (flag) { return whispers[Random.Range(0, whispers.Count)]; } if (Random.value < Mathf.Clamp01((float)list.Count * 0.25f)) { return list[Random.Range(0, list.Count)]; } return null; } private SchizoClip PickVoice() { List voices = SoundLibrary.Voices; if (voices.Count == 0) { return null; } if (!ModConfig.MatchVoicesToPlayerNames.Value) { return voices[Random.Range(0, voices.Count)]; } HashSet presentPlayerNames = GetPresentPlayerNames(); List list = new List(); for (int i = 0; i < voices.Count; i++) { SchizoClip schizoClip = voices[i]; if (schizoClip.OwnerName == null) { list.Add(schizoClip); } else if (presentPlayerNames.Contains(schizoClip.OwnerName.ToLowerInvariant())) { list.Add(schizoClip); } } if (list.Count == 0) { if (!_warnedNoEligibleVoices) { _warnedNoEligibleVoices = true; Plugin.Log.LogWarning((object)"MatchVoicesToPlayerNames is on, but none of your voice clips match a player currently in the lobby, so none of them can play. Put some clips directly in Sounds/Voices/ to make them generic, or turn the setting off."); } return null; } return list[Random.Range(0, list.Count)]; } private static HashSet GetPresentPlayerNames() { HashSet hashSet = new HashSet(); StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null || instance.allPlayerScripts == null) { return hashSet; } string text = (((Object)(object)instance.localPlayerController != (Object)null && instance.localPlayerController.playerUsername != null) ? instance.localPlayerController.playerUsername.ToLowerInvariant() : null); for (int i = 0; i < instance.allPlayerScripts.Length; i++) { PlayerControllerB val = instance.allPlayerScripts[i]; if (!((Object)(object)val == (Object)null) && val.isPlayerControlled && !string.IsNullOrEmpty(val.playerUsername)) { string text2 = val.playerUsername.ToLowerInvariant(); if (!ModConfig.NeverImpersonateYourself.Value || text == null || !(text2 == text)) { hashSet.Add(text2); } } } return hashSet; } private static Vector3 PickSpawnPoint(Vector3 eye, PlayerControllerB local, float minD, float maxD, float behindBias, bool allowThroughWall = false) { //IL_0025: 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_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0093: 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_0118: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) Transform val = (((Object)(object)local.gameplayCamera != (Object)null) ? ((Component)local.gameplayCamera).transform : ((Component)local).transform); Vector3 forward = default(Vector3); ((Vector3)(ref forward))..ctor(val.forward.x, 0f, val.forward.z); if (((Vector3)(ref forward)).sqrMagnitude < 0.0001f) { forward = Vector3.forward; } ((Vector3)(ref forward)).Normalize(); Vector3 val2 = Quaternion.AngleAxis((Random.value < behindBias) ? Random.Range(105f, 255f) : Random.Range(-105f, 105f), Vector3.up) * forward; val2.y = Random.Range(-0.1f, 0.1f); ((Vector3)(ref val2)).Normalize(); maxD = Mathf.Max(minD, maxD); float num = Random.Range(minD, maxD); int num2 = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.collidersAndRoomMask : 0); RaycastHit val3 = default(RaycastHit); if (num2 != 0 && !allowThroughWall && Physics.Raycast(eye, val2, ref val3, num, num2, (QueryTriggerInteraction)1)) { num = Mathf.Max(0.6f, Mathf.Min(num, ((RaycastHit)(ref val3)).distance - 0.6f)); } return eye + val2 * num; } } public class SchizoClip { public AudioClip Clip; public string FileName; public int Stage; public bool IsVoice; public bool IsVanilla; public bool IsWhisper; public float MaxPlaySeconds; public string OwnerName; } public static class SoundLibrary { public static readonly List Stage1 = new List(); public static readonly List Stage2 = new List(); public static readonly List Stage3 = new List(); public static readonly List Voices = new List(); public static readonly List Whispers = new List(); public static readonly List Footsteps = new List(); private static readonly string[] Extensions = new string[5] { ".wav", ".ogg", ".mp3", ".aiff", ".aif" }; public static AudioClip[] FootstepClips { get; private set; } public static bool FinishedLoading { get; private set; } public static string SoundsRoot { get; private set; } public static void ResolveRoot(string pluginDirectory) { SoundsRoot = Path.Combine(pluginDirectory, "Sounds"); } public static List PoolForStage(int stage) { if (stage <= 1) { return Stage1; } if (stage == 2) { return Stage2; } return Stage3; } public static IEnumerator LoadAll() { FinishedLoading = false; Stage1.Clear(); Stage2.Clear(); Stage3.Clear(); Voices.Clear(); Whispers.Clear(); Footsteps.Clear(); FootstepClips = null; try { if (string.IsNullOrEmpty(SoundsRoot) || !Directory.Exists(SoundsRoot)) { Plugin.Log.LogWarning((object)("No Sounds folder found at '" + SoundsRoot + "'.")); TryCreateFolders(); yield break; } string voicesDir = Path.Combine(SoundsRoot, "Voices"); foreach (string item in EnumerateAudio(Path.Combine(SoundsRoot, "Stage1"))) { yield return LoadOne(item, Stage1, 1, isVoice: false, null); } foreach (string item2 in EnumerateAudio(Path.Combine(SoundsRoot, "Ambient"))) { yield return LoadOne(item2, Stage1, 1, isVoice: false, null); } foreach (string item3 in EnumerateAudio(Path.Combine(SoundsRoot, "Stage2"))) { yield return LoadOne(item3, Stage2, 2, isVoice: false, null); } foreach (string item4 in EnumerateAudio(Path.Combine(SoundsRoot, "Stage3"))) { yield return LoadOne(item4, Stage3, 3, isVoice: false, null); } foreach (string item5 in EnumerateAudio(Path.Combine(SoundsRoot, "Whispers"))) { yield return LoadOne(item5, Whispers, 1, isVoice: false, null, isWhisper: true); } foreach (string item6 in EnumerateAudio(Path.Combine(SoundsRoot, "Footsteps"))) { yield return LoadOne(item6, Footsteps, 1, isVoice: false, null); } foreach (string item7 in EnumerateAudio(voicesDir)) { yield return LoadOne(item7, Voices, 2, isVoice: true, OwnerFromFolder(voicesDir, item7)); } foreach (string item8 in EnumerateAudio(SoundsRoot, recursive: false)) { yield return LoadOne(item8, Stage1, 1, isVoice: false, null); } if (Footsteps.Count > 0) { FootstepClips = (AudioClip[])(object)new AudioClip[Footsteps.Count]; for (int i = 0; i < Footsteps.Count; i++) { FootstepClips[i] = Footsteps[i].Clip; } } Plugin.Log.LogInfo((object)$"Loaded {Whispers.Count} whisper, {Stage1.Count} Stage1, {Stage2.Count} Stage2, {Stage3.Count} Stage3, {Footsteps.Count} footstep and {Voices.Count} voice clip(s)."); if (Whispers.Count == 0 && Stage1.Count == 0 && Stage2.Count == 0 && Stage3.Count == 0 && Voices.Count == 0 && Footsteps.Count == 0) { Plugin.Log.LogWarning((object)("No sounds were loaded - the mod will stay silent. Put .wav or .ogg files into: " + SoundsRoot)); } } finally { FinishedLoading = true; } } private static void TryCreateFolders() { try { Directory.CreateDirectory(Path.Combine(SoundsRoot, "Whispers")); Directory.CreateDirectory(Path.Combine(SoundsRoot, "Stage1")); Directory.CreateDirectory(Path.Combine(SoundsRoot, "Stage2")); Directory.CreateDirectory(Path.Combine(SoundsRoot, "Stage3")); Directory.CreateDirectory(Path.Combine(SoundsRoot, "Footsteps")); Directory.CreateDirectory(Path.Combine(SoundsRoot, "Voices")); Plugin.Log.LogInfo((object)("Created empty sound folders at " + SoundsRoot)); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not create the Sounds folders: " + ex.Message)); } } private static string OwnerFromFolder(string voicesDir, string file) { try { string directoryName = Path.GetDirectoryName(file); if (string.IsNullOrEmpty(directoryName)) { return null; } string text = Path.GetFullPath(directoryName).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); string text2 = Path.GetFullPath(voicesDir).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); if (string.Equals(text, text2, StringComparison.OrdinalIgnoreCase)) { return null; } if (!text.StartsWith(text2, StringComparison.OrdinalIgnoreCase)) { return null; } string[] array = text.Substring(text2.Length).TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar).Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); return (array.Length != 0 && array[0].Length > 0) ? array[0] : null; } catch { return null; } } private static IEnumerable EnumerateAudio(string dir, bool recursive = true) { if (!Directory.Exists(dir)) { yield break; } string[] files; try { files = Directory.GetFiles(dir, "*.*", recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not read '" + dir + "': " + ex.Message)); yield break; } Array.Sort(files, (IComparer?)StringComparer.OrdinalIgnoreCase); string[] array = files; foreach (string text in array) { string value; try { value = Path.GetExtension(text).ToLowerInvariant(); } catch { continue; } if (Array.IndexOf(Extensions, value) >= 0) { yield return text; } } } private static AudioType GuessType(string path) { switch (Path.GetExtension(path).ToLowerInvariant()) { case ".wav": return (AudioType)20; case ".ogg": return (AudioType)14; case ".mp3": return (AudioType)13; case ".aiff": case ".aif": return (AudioType)2; default: return (AudioType)0; } } private static IEnumerator LoadOne(string path, List target, int stage, bool isVoice, string owner, bool isWhisper = false) { string text = null; AudioType type = (AudioType)0; try { text = new Uri(path).AbsoluteUri.Replace("#", "%23"); type = GuessType(path); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Bad path '" + path + "': " + ex.Message)); } if (text == null) { yield break; } UnityWebRequest req = null; try { req = UnityWebRequestMultimedia.GetAudioClip(text, type); } catch (Exception ex2) { Plugin.Log.LogWarning((object)("Could not open '" + path + "': " + ex2.Message)); } if (req == null) { yield break; } UnityWebRequest val = req; try { DownloadHandler downloadHandler = req.downloadHandler; DownloadHandlerAudioClip val2 = (DownloadHandlerAudioClip)(object)((downloadHandler is DownloadHandlerAudioClip) ? downloadHandler : null); if (val2 != null) { val2.streamAudio = false; val2.compressed = false; } yield return req.SendWebRequest(); if ((int)req.result != 1) { string text2 = (((int)type == 13) ? " (MP3 is unreliable in Unity standalone builds - convert it to .ogg or .wav)" : string.Empty); Plugin.Log.LogWarning((object)("Failed to load '" + Path.GetFileName(path) + "': " + req.error + text2)); yield break; } AudioClip val3 = null; try { val3 = DownloadHandlerAudioClip.GetContent(req); } catch (Exception ex3) { Plugin.Log.LogWarning((object)("Decode error on '" + path + "': " + ex3.Message)); } if ((Object)(object)val3 == (Object)null || val3.length <= 0.01f) { Plugin.Log.LogWarning((object)("Skipped '" + Path.GetFileName(path) + "' - empty or unreadable.")); yield break; } string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path); ((Object)val3).name = "Schizo_" + fileNameWithoutExtension; ((Object)val3).hideFlags = (HideFlags)32; float value = ModConfig.MaxSoundSeconds.Value; float maxPlaySeconds = ((value > 0.5f && val3.length > value) ? value : 0f); target.Add(new SchizoClip { Clip = val3, FileName = fileNameWithoutExtension, Stage = stage, IsVoice = isVoice, IsWhisper = isWhisper, MaxPlaySeconds = maxPlaySeconds, OwnerName = owner }); if (ModConfig.DebugLogging.Value) { Plugin.Log.LogInfo((object)(" + [" + (isVoice ? "voice" : ("stage " + stage)) + "] " + fileNameWithoutExtension + " (" + val3.length.ToString("0.0") + "s)" + ((owner != null) ? (" as " + owner) : string.Empty))); } } finally { ((IDisposable)val)?.Dispose(); } } } public static class VanillaSounds { public static readonly List Stage1 = new List(); public static readonly List Stage2 = new List(); public static readonly List Stage3 = new List(); private static bool _harvestedEnabled; private static bool _ambienceFound; private static int _harvestRetries; private static float _nextRetryTime; private const int MaxRetries = 3; private const float RetrySpacing = 2.5f; public static readonly List Whispers = new List(); public static readonly List Signature = new List(); public static readonly List DoorOpenClips = new List(); public static readonly List DoorShutClips = new List(); private static readonly List _coilhead = new List(); private static readonly List _coilheadSnaps = new List(); private static readonly List _springsVanilla = new List(); private static readonly List _springsOther = new List(); private static readonly List _snapPick = new List(); private static readonly List _coilMove = new List(); private static bool _warnedNoCoilhead; private const float MinLength = 0.15f; private const float MaxLength = 6f; private const float MaxBurstLength = 1.5f; private static readonly HashSet _taken = new HashSet(); private static readonly HashSet _sweptPrefabs = new HashSet(); private static readonly HashSet VanillaEnemies = new HashSet(StringComparer.OrdinalIgnoreCase) { "Centipede", "Bunker Spider", "Hoarding bug", "Flowerman", "Crawler", "Blob", "Girl", "Puffer", "Nutcracker", "Masked", "Butler", "Butler Bees", "Jester", "Spring", "Baboon hawk", "Earth Leviathan", "Eyeless Dog", "Forest Giant", "MouthDog", "Manticoil", "Red Locust Bees", "Docile Locust Bees", "Tulip Snake", "Kidnapper Fox", "Maneater", "Barber", "Radmech", "Clay Surgeon", "Bush Wolf", "Cave Dweller", "Ghost Girl", "Coil-Head", "Bracken", "Thumper", "Hygrodere", "Snare Flea", "Circuit Bees", "Roaming Locusts", "Old Bird", "Spore Lizard" }; public static bool Harvested { get; private set; } public static bool NeedsHarvest { get { if (!Harvested) { return true; } if (_harvestedEnabled != ModConfig.UseVanillaSounds.Value) { return true; } if (_harvestRetries < 3 && ModConfig.UseVanillaSounds.Value && !_ambienceFound && Time.time >= _nextRetryTime && (Object)(object)RoundManager.Instance != (Object)null && (Object)(object)RoundManager.Instance.currentLevel != (Object)null) { return true; } return false; } } public static AudioClip[] CoilheadClips { get; private set; } public static AudioClip[] CoilheadSnapClips { get; private set; } public static AudioClip CoilheadLoudest { get; private set; } public static AudioClip[] CoilheadMoveClips { get; private set; } public static bool CoilheadMoveIsFallback { get; private set; } public static bool Any { get { if (Stage1.Count <= 0 && Stage2.Count <= 0 && Stage3.Count <= 0 && Whispers.Count <= 0 && Signature.Count <= 0 && (CoilheadClips == null || CoilheadClips.Length == 0) && DoorOpenClips.Count <= 0) { return DoorShutClips.Count > 0; } return true; } } public static List PoolForStage(int stage) { if (stage <= 1) { return Stage1; } if (stage == 2) { return Stage2; } return Stage3; } private static void ResetPools() { Stage1.Clear(); Stage2.Clear(); Stage3.Clear(); Whispers.Clear(); Signature.Clear(); _coilhead.Clear(); CoilheadClips = null; _coilheadSnaps.Clear(); _springsVanilla.Clear(); _springsOther.Clear(); _snapPick.Clear(); _coilMove.Clear(); CoilheadMoveClips = null; CoilheadMoveIsFallback = false; CoilheadSnapClips = null; CoilheadLoudest = null; DoorOpenClips.Clear(); DoorShutClips.Clear(); _taken.Clear(); } public static void Invalidate() { Harvested = false; _ambienceFound = false; _warnedNoCoilhead = false; _harvestRetries = 0; _nextRetryTime = 0f; ResetPools(); } public static void Harvest() { if (Harvested && _harvestedEnabled && ModConfig.UseVanillaSounds.Value) { _harvestRetries++; } _nextRetryTime = Time.time + 2.5f; Harvested = true; _harvestedEnabled = ModConfig.UseVanillaSounds.Value; ResetPools(); if (!ModConfig.UseVanillaSounds.Value) { Plugin.Log.LogInfo((object)"Vanilla sound borrowing is disabled in the config."); return; } TryRun(HarvestShipAndDoors, "ship and door sounds"); TryRun(HarvestPlayerSounds, "player sounds"); TryRun(HarvestCoilheadPrefabs, "the coilhead prefab"); TryRun(HarvestEnemies, "enemy sounds"); TryRun(FinishCoilheads, "the coilhead pool"); TryRun(HarvestInsanityAmbience, "the game's insanity ambience"); TryRun(HarvestLevelOneShots, "level ambience one-shots"); Plugin.Log.LogInfo((object)$"Borrowed {Stage1.Count} + {Stage2.Count} + {Stage3.Count} sound(s), {Whispers.Count} close human, {Signature.Count} signature, {((CoilheadClips != null) ? CoilheadClips.Length : 0)} coilhead spring and {DoorOpenClips.Count + DoorShutClips.Count} door clip(s) from the game itself."); if (CoilheadClips != null) { for (int i = 0; i < CoilheadClips.Length; i++) { Plugin.Log.LogInfo((object)string.Format(" [coilhead spring] #{0} {1} ({2:0.00}s){3}", i, ((Object)CoilheadClips[i]).name, CoilheadClips[i].length, ((Object)(object)CoilheadClips[i] == (Object)(object)PickLoudCoil()) ? " <-- this is the one Stage 3 plays" : "")); } } for (int j = 0; j < _coilMove.Count; j++) { Plugin.Log.LogInfo((object)$" [coilhead moving] {((Object)_coilMove[j]).name} ({_coilMove[j].length:0.00}s)"); } if (CoilheadMoveIsFallback) { Plugin.Log.LogInfo((object)" [coilhead moving] the prefab has no separate movement layer, which is correct for vanilla - a coilhead's walking sound IS its spring noises, fired once per lurch. A running one will use the full spring set."); } if (ModConfig.DebugLogging.Value) { LogPool("vanilla stage 1", Stage1); LogPool("vanilla stage 2", Stage2); LogPool("vanilla stage 3", Stage3); LogPool("vanilla close", Whispers); LogPool("vanilla signature", Signature); } } private static void TryRun(Action a, string label) { try { a(); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not borrow " + label + " (the game may have changed): " + ex.GetType().Name)); } } private static void LogPool(string label, List pool) { for (int i = 0; i < pool.Count; i++) { Plugin.Log.LogInfo((object)(" [" + label + "] " + pool[i].FileName)); } } private static bool Add(List target, AudioClip clip, int stage, string label, float maxPlay = 0f, bool whisper = false) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Invalid comparison between Unknown and I4 if ((Object)(object)clip == (Object)null) { return false; } float num = ((maxPlay > 0f) ? 300f : 6f); if (clip.length < 0.15f || clip.length > num) { return false; } if (!_taken.Add(clip)) { return false; } try { if (clip.length <= 30f && (int)clip.loadState != 2) { clip.LoadAudioData(); } } catch { } target.Add(new SchizoClip { Clip = clip, FileName = (string.IsNullOrEmpty(((Object)clip).name) ? label : ((Object)clip).name), Stage = stage, IsVanilla = true, IsWhisper = whisper, MaxPlaySeconds = maxPlay }); return true; } private static void AddAll(List target, AudioClip[] clips, int stage, string label, float maxPlay = 0f, bool whisper = false) { if (clips != null) { for (int i = 0; i < clips.Length; i++) { Add(target, clips[i], stage, label, maxPlay, whisper); } } } private static void HarvestInsanityAmbience() { RoundManager instance = RoundManager.Instance; if (!((Object)(object)instance == (Object)null) && !((Object)(object)instance.currentLevel == (Object)null)) { _ambienceFound = true; LevelAmbienceLibrary levelAmbienceClips = instance.currentLevel.levelAmbienceClips; if (!((Object)(object)levelAmbienceClips == (Object)null)) { AddRandomClipsSplit(levelAmbienceClips.insideAmbienceInsanity, 4.5f); AddRandomClips(Stage3, levelAmbienceClips.outsideAmbienceInsanity, 3, "insanity", 4.5f); } } } private static void AddRandomClipsSplit(RandomAudioClip[] clips, float maxPlay) { if (clips == null) { return; } bool flag = false; for (int i = 0; i < clips.Length; i++) { if (clips[i] != null) { List list = (flag ? Stage3 : Stage2); if (Add(list, clips[i].audioClip, StageOf(list), "insanity", maxPlay)) { flag = !flag; } } } } private static void AddRandomClips(List target, RandomAudioClip[] clips, int stage, string label, float maxPlay) { if (clips == null) { return; } for (int i = 0; i < clips.Length; i++) { if (clips[i] != null) { Add(target, clips[i].audioClip, stage, label, maxPlay); } } } private static void HarvestLevelOneShots() { RandomPeriodicAudioPlayer[] array = Resources.FindObjectsOfTypeAll(); if (array == null || array.Length == 0) { return; } for (int i = 0; i < array.Length; i++) { if (!((Object)(object)array[i] == (Object)null)) { AddAll(Stage1, array[i].randomClips, 1, "level ambience"); } } } private static void HarvestShipAndDoors() { StartOfRound instance = StartOfRound.Instance; if (!((Object)(object)instance == (Object)null)) { AddSplit(Stage1, Stage2, instance.shipCreakSFX, "creak"); TakeDoors(DoorOpenClips, instance.creakOpenDoorMetal); TakeDoors(DoorOpenClips, instance.creakOpenDoorWooden); TakeDoors(DoorShutClips, instance.shutDoorMetal); TakeDoors(DoorShutClips, instance.shutDoorWooden); } } private static void AddSplit(List a, List b, AudioClip[] clips, string label) { if (clips == null) { return; } bool flag = false; for (int i = 0; i < clips.Length; i++) { List list = (flag ? b : a); if (Add(list, clips[i], StageOf(list), label)) { flag = !flag; } } } private static int StageOf(List pool) { if (pool == Stage3) { return 3; } if (pool == Stage2) { return 2; } return 1; } private static void TakeDoors(List into, AudioClip[] clips) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Invalid comparison between Unknown and I4 if (into == null || clips == null) { return; } foreach (AudioClip val in clips) { if ((Object)(object)val == (Object)null || val.length < 0.15f || val.length > 6f || !_taken.Add(val)) { continue; } try { if ((int)val.loadState != 2) { val.LoadAudioData(); } } catch { } into.Add(val); } } private static void HarvestPlayerSounds() { StartOfRound instance = StartOfRound.Instance; if (!((Object)(object)instance == (Object)null)) { AddSplit(Stage1, Stage2, instance.playerDragFootSFX, "drag"); } } private static void HarvestEnemies() { EnemyAI[] all = Resources.FindObjectsOfTypeAll(); if (all == null || all.Length == 0) { return; } TryRun(delegate { HarvestCoilheads(all); }, "coilhead sounds"); TryRun(delegate { HarvestGhostGirls(all); }, "ghost girl sounds"); if (ModConfig.ExtraEnemyVoices.Value) { TryRun(delegate { HarvestCreatureVoices(all); }, "creature voices"); } } private static void HarvestCoilheads(EnemyAI[] all) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown for (int i = 0; i < all.Length; i++) { if (!(((object)all[i]).GetType() != typeof(SpringManAI))) { TakeSprings((SpringManAI)all[i], IsVanillaCoilhead(all[i])); } } } private static void FinishCoilheads() { CoilheadClips = ((_coilhead.Count > 0) ? _coilhead.ToArray() : null); _snapPick.Clear(); for (int i = 0; i < _coilheadSnaps.Count; i++) { if (_springsVanilla.Count == 0 || _springsVanilla.Contains(_coilheadSnaps[i])) { _snapPick.Add(_coilheadSnaps[i]); } } if (_snapPick.Count == 0) { _snapPick.AddRange(_coilheadSnaps); } CoilheadSnapClips = ((_snapPick.Count > 0) ? _snapPick.ToArray() : null); if (_coilMove.Count > 1) { CoilheadMoveClips = _coilMove.ToArray(); CoilheadMoveIsFallback = false; } else { CoilheadMoveClips = CoilheadClips; CoilheadMoveIsFallback = CoilheadClips != null; } List list = ((_springsVanilla.Count > 0) ? _springsVanilla : _springsOther); CoilheadLoudest = null; float num = -1f; for (int j = 0; j < list.Count; j++) { if (list[j].length > num) { num = list[j].length; CoilheadLoudest = list[j]; } } if ((Object)(object)CoilheadLoudest == (Object)null && _coilhead.Count > 0) { CoilheadLoudest = _coilhead[0]; } if (CoilheadClips == null && !_warnedNoCoilhead) { _warnedNoCoilhead = true; Plugin.Log.LogWarning((object)"No coilhead spring clips were found anywhere - not in the level and not in any moon's spawn table. Stage 3 will use the ghost girl instead."); } } public static AudioClip PickLoudCoil() { int value = ModConfig.CoilheadClipIndex.Value; if (value >= 0 && CoilheadClips != null && value < CoilheadClips.Length) { return CoilheadClips[value]; } return CoilheadLoudest; } private static bool IsVanillaCoilhead(EnemyAI ai) { if ((Object)(object)ai == (Object)null || (Object)(object)ai.enemyType == (Object)null) { return false; } return IsVanillaName(ai.enemyType.enemyName); } private static bool IsVanillaName(string n) { if (n == null) { return false; } n = n.Trim(); if (!n.Equals("Spring", StringComparison.OrdinalIgnoreCase) && !n.Equals("Coil-Head", StringComparison.OrdinalIgnoreCase)) { return n.Equals("Coilhead", StringComparison.OrdinalIgnoreCase); } return true; } private static void HarvestCoilheadPrefabs() { _sweptPrefabs.Clear(); StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null || instance.levels == null) { return; } for (int i = 0; i < instance.levels.Length; i++) { SelectableLevel val = instance.levels[i]; if (!((Object)(object)val == (Object)null)) { TakeFromSpawnList(val.Enemies); TakeFromSpawnList(val.OutsideEnemies); TakeFromSpawnList(val.DaytimeEnemies); } } } private static void TakeFromSpawnList(List list) { if (list == null) { return; } for (int i = 0; i < list.Count; i++) { if (list[i] == null || (Object)(object)list[i].enemyType == (Object)null) { continue; } GameObject enemyPrefab = list[i].enemyType.enemyPrefab; if (!((Object)(object)enemyPrefab == (Object)null) && _sweptPrefabs.Add(enemyPrefab)) { SpringManAI component = enemyPrefab.GetComponent(); if (!((Object)(object)component == (Object)null) && !(((object)component).GetType() != typeof(SpringManAI))) { bool vanilla = IsVanillaName(list[i].enemyType.enemyName); TakeSprings(component, vanilla); } } } } private static void TakeSprings(SpringManAI spring, bool vanilla) { if ((Object)(object)spring == (Object)null) { return; } AudioClip[] springNoises = spring.springNoises; if (springNoises != null) { for (int i = 0; i < springNoises.Length; i++) { TakeSpring(springNoises[i], isLunge: true, vanilla); } } TakeSpring(spring.enterCooldownSFX, isLunge: false, vanilla); if (!vanilla) { return; } try { TakeMovementAudio(spring); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not read the coilhead's movement audio (" + ex.GetType().Name + ") - it will run on its spring noises.")); } } private static void TakeMovementAudio(SpringManAI spring) { if ((Object)(object)((EnemyAI)spring).creatureSFX != (Object)null) { TakeMove(((EnemyAI)spring).creatureSFX.clip); } if ((Object)(object)((EnemyAI)spring).creatureVoice != (Object)null) { TakeMove(((EnemyAI)spring).creatureVoice.clip); } if ((Object)(object)((EnemyAI)spring).enemyType != (Object)null) { TakeMoveAll(((EnemyAI)spring).enemyType.audioClips); } PlayAudioAnimationEvent[] componentsInChildren = ((Component)spring).GetComponentsInChildren(true); if (componentsInChildren == null) { return; } foreach (PlayAudioAnimationEvent val in componentsInChildren) { if (!((Object)(object)val == (Object)null)) { TakeMove(val.audioClip); TakeMove(val.audioClip2); TakeMove(val.audioClip3); TakeMoveAll(val.randomClips); TakeMoveAll(val.randomClips2); if ((Object)(object)val.audioToPlay != (Object)null) { TakeMove(val.audioToPlay.clip); } if ((Object)(object)val.audioToPlayB != (Object)null) { TakeMove(val.audioToPlayB.clip); } } } } private static void TakeMoveAll(AudioClip[] clips) { if (clips != null) { for (int i = 0; i < clips.Length; i++) { TakeMove(clips[i]); } } } private static void TakeMove(AudioClip clip) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Invalid comparison between Unknown and I4 if ((Object)(object)clip == (Object)null || clip.length < 0.15f || clip.length > 2.5f || !_taken.Add(clip)) { return; } try { if ((int)clip.loadState != 2) { clip.LoadAudioData(); } } catch { } _coilMove.Add(clip); } private static void TakeSpring(AudioClip clip, bool isLunge, bool vanilla) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Invalid comparison between Unknown and I4 if ((Object)(object)clip == (Object)null || clip.length < 0.15f || clip.length > 6f || !_taken.Add(clip)) { return; } try { if ((int)clip.loadState != 2) { clip.LoadAudioData(); } } catch { } _coilhead.Add(clip); if (isLunge) { (vanilla ? _springsVanilla : _springsOther).Add(clip); } if (clip.length <= 1.5f) { _coilheadSnaps.Add(clip); } } private static void HarvestGhostGirls(EnemyAI[] all) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown for (int i = 0; i < all.Length; i++) { if (!(((object)all[i]).GetType() != typeof(DressGirlAI))) { DressGirlAI val = (DressGirlAI)all[i]; Add(Stage1, val.skipWalkSFX, 1, "skipping"); Add(Whispers, val.breathingSFX, 2, "breathing", 3.5f, whisper: true); AddAll(Signature, val.appearStaringSFX, 3, "staring", 4f); } } } private static void HarvestCreatureVoices(EnemyAI[] all) { HashSet hashSet = new HashSet(); int num = 0; for (int i = 0; i < all.Length; i++) { if (num >= 36) { break; } EnemyAI val = all[i]; if ((Object)(object)val == (Object)null) { continue; } EnemyType enemyType = val.enemyType; if ((Object)(object)enemyType == (Object)null || !hashSet.Add(enemyType) || string.IsNullOrEmpty(enemyType.enemyName) || !VanillaEnemies.Contains(enemyType.enemyName)) { continue; } if (enemyType.audioClips != null) { int num2 = 0; for (int j = 0; j < enemyType.audioClips.Length; j++) { if (num2 >= 3) { break; } int count = Stage3.Count; Add(Stage3, enemyType.audioClips[j], 3, "creature"); if (Stage3.Count > count) { num2++; num++; } } } Add(Stage2, enemyType.hitEnemyVoiceSFX, 2, "creature"); } } private static AudioClip[] Clean(AudioClip[] clips) { if (clips == null || clips.Length == 0) { return null; } int num = 0; for (int i = 0; i < clips.Length; i++) { if ((Object)(object)clips[i] != (Object)null) { num++; } } if (num == 0) { return null; } if (num == clips.Length) { return clips; } AudioClip[] array = (AudioClip[])(object)new AudioClip[num]; int num2 = 0; for (int j = 0; j < clips.Length; j++) { if ((Object)(object)clips[j] != (Object)null) { array[num2++] = clips[j]; } } return array; } public static AudioClip[] FootstepClipsFor(PlayerControllerB local) { try { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null || instance.footstepSurfaces == null || instance.footstepSurfaces.Length == 0) { return null; } int num = (((Object)(object)local != (Object)null) ? local.currentFootstepSurfaceIndex : 0); if (num < 0 || num >= instance.footstepSurfaces.Length) { num = 0; } AudioClip[] array = Clean(instance.footstepSurfaces[num].clips); if (array == null) { for (int i = 0; i < instance.footstepSurfaces.Length; i++) { AudioClip[] array2 = Clean(instance.footstepSurfaces[i].clips); if (array2 != null) { return array2; } } return null; } return array; } catch { return null; } } }