Decompiled source of Ominous v1.0.0

Ominous.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using UnityEngine;
using UnityEngine.Networking;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Ominous")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+901b84ab23f2f10926b13865f9b40bb283490874")]
[assembly: AssemblyProduct("Ominous")]
[assembly: AssemblyTitle("Ominous")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Ominous
{
	public class ClipLibrary
	{
		private readonly Dictionary<string, AudioClip> m_clips = new Dictionary<string, AudioClip>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<string, string> m_errors = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

		public bool Loading { get; private set; }

		public int Loaded { get; private set; }

		public int Failed { get; private set; }

		public string CurrentFile { get; private set; }

		public string LastError { get; private set; }

		public int Pending { get; private set; }

		public AudioClip Get(string fileName)
		{
			if (string.IsNullOrEmpty(fileName))
			{
				return null;
			}
			if (!m_clips.TryGetValue(fileName, out var value))
			{
				return null;
			}
			return value;
		}

		public bool HasClip(string fileName)
		{
			if (!string.IsNullOrEmpty(fileName))
			{
				return m_clips.ContainsKey(fileName);
			}
			return false;
		}

		public string ErrorFor(string fileName)
		{
			if (!m_errors.TryGetValue(fileName ?? "", out var value))
			{
				return null;
			}
			return value;
		}

		public void Reset()
		{
			foreach (AudioClip value in m_clips.Values)
			{
				if ((Object)(object)value != (Object)null)
				{
					Object.Destroy((Object)(object)value);
				}
			}
			m_clips.Clear();
			m_errors.Clear();
			Loaded = 0;
			Failed = 0;
			Pending = 0;
			CurrentFile = null;
			LastError = null;
		}

		public IEnumerator LoadAll(IEnumerable<string> fileNames, string tracksDir, Action onComplete)
		{
			Reset();
			Loading = true;
			List<string> queue = new List<string>();
			foreach (string fileName in fileNames)
			{
				if (!string.IsNullOrWhiteSpace(fileName))
				{
					queue.Add(fileName.Trim());
				}
			}
			Pending = queue.Count;
			try
			{
				for (int i = 0; i < queue.Count; i++)
				{
					string name = (CurrentFile = queue[i]);
					if (m_clips.ContainsKey(name))
					{
						Loaded++;
						continue;
					}
					AudioClip val = null;
					string error = null;
					string path = Path.Combine(tracksDir, name);
					if (!File.Exists(path))
					{
						error = "File not found in tracks folder";
					}
					else
					{
						string text2 = Path.GetExtension(name).ToLowerInvariant();
						if (text2 == ".wav")
						{
							try
							{
								val = LoadWav(name, path);
								if ((Object)(object)val == (Object)null)
								{
									error = "Unsupported WAV format";
								}
							}
							catch (Exception ex)
							{
								error = ex.Message;
							}
						}
						else if (text2 == ".mp3")
						{
							AudioClip decoded = null;
							yield return LoadMp3(name, path, delegate(AudioClip result)
							{
								decoded = result;
							});
							val = decoded;
							if ((Object)(object)val == (Object)null)
							{
								error = "MP3 decode failed";
							}
						}
						else
						{
							error = "Unsupported audio type " + text2;
						}
					}
					if ((Object)(object)val != (Object)null)
					{
						Object.DontDestroyOnLoad((Object)(object)val);
						m_clips[name] = val;
						Loaded++;
					}
					else
					{
						m_errors[name] = error ?? "unknown error";
						LastError = name + ": " + m_errors[name];
						Failed++;
						Logger.LogWarning((object)("[Ominous] Failed to load track '" + name + "': " + m_errors[name]));
					}
				}
			}
			finally
			{
				ClipLibrary clipLibrary = this;
				clipLibrary.Loading = false;
				clipLibrary.CurrentFile = null;
				clipLibrary.Pending = 0;
			}
			onComplete?.Invoke();
		}

		private static AudioClip LoadWav(string name, string path)
		{
			byte[] array = File.ReadAllBytes(path);
			if (array.Length < 44 || !Tag(array, 0, "RIFF") || !Tag(array, 8, "WAVE"))
			{
				return null;
			}
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			int num4 = 0;
			byte[] array2 = null;
			int num5 = 12;
			while (num5 + 8 <= array.Length)
			{
				string text = Encoding.ASCII.GetString(array, num5, 4);
				int num6 = BitConverter.ToInt32(array, num5 + 4);
				int num7 = num5 + 8;
				if (num6 < 0 || num7 > array.Length)
				{
					break;
				}
				if (num7 + num6 > array.Length)
				{
					num6 = array.Length - num7;
				}
				if (text == "fmt " && num7 + 16 <= array.Length)
				{
					num = BitConverter.ToUInt16(array, num7);
					num2 = BitConverter.ToUInt16(array, num7 + 2);
					num3 = BitConverter.ToInt32(array, num7 + 4);
					num4 = BitConverter.ToUInt16(array, num7 + 14);
					if (num == 65534 && num7 + 26 <= array.Length)
					{
						num = BitConverter.ToUInt16(array, num7 + 24);
					}
				}
				else if (text == "data")
				{
					array2 = new byte[num6];
					Array.Copy(array, num7, array2, 0, num6);
				}
				num5 = num7 + num6 + (num6 & 1);
			}
			if (array2 == null || num2 < 1 || num2 > 8 || num3 < 1)
			{
				return null;
			}
			int num8 = num4 / 8;
			if (num8 < 1)
			{
				return null;
			}
			int num9 = num8 * num2;
			int num10 = array2.Length / num9;
			if (num10 < 1)
			{
				return null;
			}
			float[] array3 = new float[num10 * num2];
			for (int i = 0; i < array3.Length; i++)
			{
				int num11 = i * num8;
				if (num11 + num8 > array2.Length)
				{
					break;
				}
				array3[i] = DecodeSample(array2, num11, num, num4);
			}
			AudioClip obj = AudioClip.Create(name, num10, num2, num3, false);
			obj.SetData(array3, 0);
			return obj;
		}

		private static float DecodeSample(byte[] data, int offset, int format, int bits)
		{
			if (format == 3)
			{
				return BitConverter.ToSingle(data, offset);
			}
			switch (bits)
			{
			case 8:
				return (float)(data[offset] - 128) / 128f;
			case 16:
				return (float)BitConverter.ToInt16(data, offset) / 32768f;
			case 24:
			{
				int num = data[offset] | (data[offset + 1] << 8) | (data[offset + 2] << 16);
				if ((num & 0x800000) != 0)
				{
					num |= -16777216;
				}
				return (float)num / 8388608f;
			}
			case 32:
				return (float)BitConverter.ToInt32(data, offset) / 2.1474836E+09f;
			default:
				throw new NotSupportedException(bits + "-bit WAV");
			}
		}

		private static bool Tag(byte[] bytes, int offset, string tag)
		{
			if (offset + 4 > bytes.Length)
			{
				return false;
			}
			if (bytes[offset] == tag[0] && bytes[offset + 1] == tag[1] && bytes[offset + 2] == tag[2])
			{
				return bytes[offset + 3] == tag[3];
			}
			return false;
		}

		private static IEnumerator LoadMp3(string name, string path, Action<AudioClip> done)
		{
			string text = "file:///" + path.Replace('\\', '/').TrimStart('/');
			UnityWebRequest request = UnityWebRequestMultimedia.GetAudioClip(text, (AudioType)13);
			yield return request.SendWebRequest();
			if ((int)request.result != 1)
			{
				Logger.LogWarning((object)("[Ominous] MP3 '" + name + "' failed: " + request.error));
				request.Dispose();
				yield break;
			}
			AudioClip clip = DownloadHandlerAudioClip.GetContent(request);
			if ((Object)(object)clip == (Object)null)
			{
				request.Dispose();
				yield break;
			}
			((Object)clip).name = name;
			done(clip);
			yield return null;
			bool num = (Object)(object)clip != (Object)null;
			request.Dispose();
			if (!num)
			{
				Logger.LogWarning((object)("[Ominous] MP3 '" + name + "' did not survive loader disposal. Convert to WAV."));
			}
		}
	}
	public class OminousAudio : MonoBehaviour
	{
		private AudioSource m_sourceA;

		private AudioSource m_sourceB;

		private bool m_usingSourceA = true;

		private string m_currentTrackA = "";

		private string m_currentTrackB = "";

		private float m_targetVolA;

		private float m_targetVolB;

		private float m_targetPitchA = 1f;

		private float m_targetPitchB = 1f;

		public static OminousAudio Instance { get; private set; }

		public float CurrentDucking { get; private set; }

		public string ActiveTrack { get; private set; } = "";

		public float ActiveVolume { get; private set; }

		private void Awake()
		{
			if ((Object)(object)Instance != (Object)null && (Object)(object)Instance != (Object)(object)this)
			{
				Object.Destroy((Object)(object)((Component)this).gameObject);
				return;
			}
			Instance = this;
			Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
			m_sourceA = ((Component)this).gameObject.AddComponent<AudioSource>();
			ConfigureSource(m_sourceA);
			m_sourceB = ((Component)this).gameObject.AddComponent<AudioSource>();
			ConfigureSource(m_sourceB);
		}

		private void ConfigureSource(AudioSource src)
		{
			src.playOnAwake = false;
			src.loop = true;
			src.spatialBlend = 0f;
			src.volume = 0f;
			src.pitch = 1f;
			src.priority = 64;
			AssignMixer(src);
		}

		public void AssignMixer(AudioSource src)
		{
			try
			{
				if ((Object)(object)MusicMan.instance != (Object)null && (Object)(object)MusicMan.instance.m_musicMixer != (Object)null)
				{
					src.outputAudioMixerGroup = MusicMan.instance.m_musicMixer;
				}
			}
			catch
			{
			}
		}

		private void Update()
		{
			float deltaTime = Time.deltaTime;
			if (deltaTime <= 0f)
			{
				return;
			}
			if ((Object)(object)m_sourceA.outputAudioMixerGroup == (Object)null && (Object)(object)MusicMan.instance != (Object)null && (Object)(object)MusicMan.instance.m_musicMixer != (Object)null)
			{
				m_sourceA.outputAudioMixerGroup = MusicMan.instance.m_musicMixer;
				m_sourceB.outputAudioMixerGroup = MusicMan.instance.m_musicMixer;
			}
			float num = ((OminousConfig.Data != null) ? OminousConfig.Data.FadeInSpeed : 0.6f);
			float num2 = ((OminousConfig.Data != null) ? OminousConfig.Data.FadeOutSpeed : 0.35f);
			float num3 = ((m_targetVolA > m_sourceA.volume) ? num : num2);
			m_sourceA.volume = Mathf.MoveTowards(m_sourceA.volume, m_targetVolA, num3 * deltaTime);
			m_sourceA.pitch = Mathf.MoveTowards(m_sourceA.pitch, m_targetPitchA, 0.5f * deltaTime);
			if (m_sourceA.volume <= 0.001f && m_targetVolA <= 0f)
			{
				if (m_sourceA.isPlaying)
				{
					m_sourceA.Stop();
				}
				m_currentTrackA = "";
			}
			float num4 = ((m_targetVolB > m_sourceB.volume) ? num : num2);
			m_sourceB.volume = Mathf.MoveTowards(m_sourceB.volume, m_targetVolB, num4 * deltaTime);
			m_sourceB.pitch = Mathf.MoveTowards(m_sourceB.pitch, m_targetPitchB, 0.5f * deltaTime);
			if (m_sourceB.volume <= 0.001f && m_targetVolB <= 0f)
			{
				if (m_sourceB.isPlaying)
				{
					m_sourceB.Stop();
				}
				m_currentTrackB = "";
			}
			float num5 = (ActiveVolume = Mathf.Max(m_sourceA.volume, m_sourceB.volume));
			float num7 = ((OminousConfig.Data != null) ? OminousConfig.Data.DuckingStrength : 0.85f);
			CurrentDucking = Mathf.Clamp01(num5 * num7);
			if (m_usingSourceA && m_sourceA.isPlaying)
			{
				ActiveTrack = m_currentTrackA;
			}
			else if (!m_usingSourceA && m_sourceB.isPlaying)
			{
				ActiveTrack = m_currentTrackB;
			}
			else if (m_sourceA.isPlaying)
			{
				ActiveTrack = m_currentTrackA;
			}
			else if (m_sourceB.isPlaying)
			{
				ActiveTrack = m_currentTrackB;
			}
			else
			{
				ActiveTrack = "";
			}
		}

		public void SetThreat(string trackName, float targetVolume, float targetPitch)
		{
			if (string.IsNullOrEmpty(trackName) || targetVolume <= 0.001f)
			{
				m_targetVolA = 0f;
				m_targetVolB = 0f;
				return;
			}
			AudioSource val = (m_usingSourceA ? m_sourceA : m_sourceB);
			if (!m_usingSourceA)
			{
				_ = m_sourceA;
			}
			else
			{
				_ = m_sourceB;
			}
			if (string.Equals(m_usingSourceA ? m_currentTrackA : m_currentTrackB, trackName, StringComparison.OrdinalIgnoreCase) && val.isPlaying)
			{
				if (m_usingSourceA)
				{
					m_targetVolA = targetVolume;
					m_targetPitchA = targetPitch;
					m_targetVolB = 0f;
				}
				else
				{
					m_targetVolB = targetVolume;
					m_targetPitchB = targetPitch;
					m_targetVolA = 0f;
				}
				return;
			}
			AudioClip val2 = OminousPlugin.Library.Get(trackName);
			if ((Object)(object)val2 == (Object)null)
			{
				return;
			}
			m_usingSourceA = !m_usingSourceA;
			if (m_usingSourceA)
			{
				m_currentTrackA = trackName;
				m_sourceA.clip = val2;
				m_sourceA.pitch = targetPitch;
				m_targetPitchA = targetPitch;
				m_targetVolA = targetVolume;
				if (!m_sourceA.isPlaying)
				{
					m_sourceA.Play();
				}
				m_targetVolB = 0f;
			}
			else
			{
				m_currentTrackB = trackName;
				m_sourceB.clip = val2;
				m_sourceB.pitch = targetPitch;
				m_targetPitchB = targetPitch;
				m_targetVolB = targetVolume;
				if (!m_sourceB.isPlaying)
				{
					m_sourceB.Play();
				}
				m_targetVolA = 0f;
			}
		}

		public void StopAllImmediate()
		{
			m_targetVolA = 0f;
			m_targetVolB = 0f;
			m_sourceA.volume = 0f;
			m_sourceB.volume = 0f;
			m_sourceA.Stop();
			m_sourceB.Stop();
			CurrentDucking = 0f;
			ActiveVolume = 0f;
			ActiveTrack = "";
		}
	}
	public class OminousBeacon : MonoBehaviour
	{
		private GameObject m_beaconRoot;

		private Light m_light;

		private ParticleSystem[] m_particles;

		private Renderer m_orbRenderer;

		private AudioSource m_pingSource;

		private float m_currentIntensity;

		private Color m_currentColor = new Color(0.25f, 0.75f, 1f);

		public static OminousBeacon Instance { get; private set; }

		public bool IsActive { get; set; }

		public float TensionFactor { get; set; }

		private void Awake()
		{
			if ((Object)(object)Instance != (Object)null && (Object)(object)Instance != (Object)(object)this)
			{
				Object.Destroy((Object)(object)((Component)this).gameObject);
				return;
			}
			Instance = this;
			Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
		}

		private void Start()
		{
			CreateBeaconVisual();
		}

		private void CreateBeaconVisual()
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			//IL_0053: 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_00a4: Expected O, but got Unknown
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Expected O, but got Unknown
			//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0207: 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)
			if ((Object)(object)m_beaconRoot != (Object)null)
			{
				return;
			}
			m_beaconRoot = new GameObject("[Ominous_Beacon_Visual]");
			Object.DontDestroyOnLoad((Object)(object)m_beaconRoot);
			if ((Object)(object)OminousItem.WispBallPrefab != (Object)null)
			{
				GameObject val = Object.Instantiate<GameObject>(OminousItem.WispBallPrefab, m_beaconRoot.transform);
				val.transform.localPosition = Vector3.zero;
				Demister component = val.GetComponent<Demister>();
				if ((Object)(object)component != (Object)null)
				{
					Object.Destroy((Object)(object)component);
				}
				m_light = val.GetComponentInChildren<Light>();
				m_particles = val.GetComponentsInChildren<ParticleSystem>();
			}
			if ((Object)(object)m_light == (Object)null)
			{
				GameObject val2 = new GameObject("BeaconLight");
				val2.transform.SetParent(m_beaconRoot.transform, false);
				m_light = val2.AddComponent<Light>();
				m_light.type = (LightType)2;
				m_light.range = 14f;
				m_light.shadows = (LightShadows)0;
			}
			if (m_particles == null || m_particles.Length == 0)
			{
				GameObject val3 = GameObject.CreatePrimitive((PrimitiveType)0);
				((Object)val3).name = "BeaconSphere";
				val3.transform.SetParent(m_beaconRoot.transform, false);
				val3.transform.localScale = Vector3.one * 0.16f;
				Collider component2 = val3.GetComponent<Collider>();
				if ((Object)(object)component2 != (Object)null)
				{
					Object.Destroy((Object)(object)component2);
				}
				m_orbRenderer = val3.GetComponent<Renderer>();
				if ((Object)(object)m_orbRenderer != (Object)null)
				{
					Shader val4 = Shader.Find("Custom/Lit") ?? Shader.Find("Standard") ?? Shader.Find("Unlit/Color");
					if ((Object)(object)val4 != (Object)null)
					{
						Material val5 = new Material(val4);
						val5.color = new Color(0.3f, 0.8f, 1f, 0.9f);
						if (val5.HasProperty("_EmissionColor"))
						{
							val5.EnableKeyword("_EMISSION");
							val5.SetColor("_EmissionColor", new Color(0.3f, 0.8f, 1f) * 2f);
						}
						m_orbRenderer.material = val5;
					}
				}
			}
			m_pingSource = m_beaconRoot.AddComponent<AudioSource>();
			m_pingSource.playOnAwake = false;
			m_pingSource.loop = false;
			m_pingSource.spatialBlend = 0.5f;
			m_pingSource.minDistance = 2f;
			m_pingSource.maxDistance = 20f;
			m_pingSource.volume = 0.8f;
			m_beaconRoot.SetActive(false);
		}

		private void Update()
		{
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: 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_0123: 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)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: 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_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: 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)
			//IL_01f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0206: Unknown result type (might be due to invalid IL or missing references)
			//IL_020b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_020f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0214: Unknown result type (might be due to invalid IL or missing references)
			//IL_0221: 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_02c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ad: 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_03f6: 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)
			//IL_035a: Unknown result type (might be due to invalid IL or missing references)
			//IL_035f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0364: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead() || !IsActive || (OminousConfig.Data != null && !OminousConfig.Data.EnableBeaconLight))
			{
				if ((Object)(object)m_beaconRoot != (Object)null && m_beaconRoot.activeSelf)
				{
					m_currentIntensity = Mathf.MoveTowards(m_currentIntensity, 0f, 2f * Time.deltaTime);
					if ((Object)(object)m_light != (Object)null)
					{
						m_light.intensity = m_currentIntensity;
					}
					if (m_currentIntensity <= 0.01f)
					{
						m_beaconRoot.SetActive(false);
					}
				}
				return;
			}
			if ((Object)(object)m_beaconRoot == (Object)null)
			{
				CreateBeaconVisual();
			}
			if (!m_beaconRoot.activeSelf)
			{
				m_beaconRoot.SetActive(true);
			}
			float num = Mathf.Sin(Time.time * 2.2f) * 0.08f;
			Vector3 val = ((Component)localPlayer).transform.position + ((Component)localPlayer).transform.up * 1.75f + ((Component)localPlayer).transform.right * 0.45f - ((Component)localPlayer).transform.forward * 0.2f + Vector3.up * num;
			m_beaconRoot.transform.position = Vector3.Lerp(m_beaconRoot.transform.position, val, 7f * Time.deltaTime);
			Color val2 = default(Color);
			((Color)(ref val2))..ctor(0.25f, 0.75f, 1f);
			Color val3 = default(Color);
			((Color)(ref val3))..ctor(1f, 0.65f, 0.05f);
			Color val4 = default(Color);
			((Color)(ref val4))..ctor(1f, 0.12f, 0.12f);
			Color val5 = ((!(TensionFactor <= 0.5f)) ? Color.Lerp(val3, val4, (TensionFactor - 0.5f) * 2f) : Color.Lerp(val2, val3, TensionFactor * 2f));
			m_currentColor = Color.Lerp(m_currentColor, val5, 4f * Time.deltaTime);
			float num2 = Mathf.Lerp(1.5f, 7f, TensionFactor);
			float num3 = Mathf.Lerp(0.12f, 0.6f, TensionFactor);
			float num4 = Mathf.Sin(Time.time * num2 * MathF.PI * 2f) * num3;
			float num5 = ((OminousConfig.Data != null) ? OminousConfig.Data.BeaconBaseIntensity : 1.2f) + num4;
			m_currentIntensity = Mathf.MoveTowards(m_currentIntensity, num5, 3f * Time.deltaTime);
			if ((Object)(object)m_light != (Object)null)
			{
				m_light.color = m_currentColor;
				m_light.intensity = m_currentIntensity;
				int num6 = ((!((Object)(object)OminousTracker.Instance != (Object)null)) ? 1 : OminousTracker.Instance.ActiveQuality);
				float num7 = ((OminousConfig.Data != null) ? OminousConfig.Data.BeaconLightRange : 14f);
				m_light.range = num7 + (float)(num6 - 1) * 2f;
			}
			if (m_particles != null)
			{
				for (int i = 0; i < m_particles.Length; i++)
				{
					ParticleSystem val6 = m_particles[i];
					if ((Object)(object)val6 != (Object)null)
					{
						MainModule main = val6.main;
						((MainModule)(ref main)).startColor = MinMaxGradient.op_Implicit(m_currentColor);
					}
				}
			}
			if ((Object)(object)m_orbRenderer != (Object)null && (Object)(object)m_orbRenderer.material != (Object)null)
			{
				m_orbRenderer.material.color = m_currentColor;
				if (m_orbRenderer.material.HasProperty("_EmissionColor"))
				{
					m_orbRenderer.material.SetColor("_EmissionColor", m_currentColor * (1f + TensionFactor * 2f));
				}
			}
		}

		public void PlayPing()
		{
			if (!((Object)(object)m_pingSource == (Object)null))
			{
				AudioClip val = OminousPlugin.Library.Get("radar_ping.wav");
				if ((Object)(object)val != (Object)null)
				{
					m_pingSource.PlayOneShot(val, 0.85f);
				}
			}
		}
	}
	[Serializable]
	public class CreatureOminousEntry
	{
		public string Prefab = "";

		public string Track = "";

		public float TriggerDistance = 60f;

		public float MaxVolumeDistance = 18f;

		public float Volume = 0.85f;

		public int Priority = 10;

		public bool Enabled = true;

		public string Biome = "";

		public int RequiredTier = 1;

		public CreatureOminousEntry()
		{
		}

		public CreatureOminousEntry(string prefab, string track, float triggerDist, float maxVolDist, float volume, int priority, string biome = "", int requiredTier = 1)
		{
			Prefab = prefab;
			Track = track;
			TriggerDistance = triggerDist;
			MaxVolumeDistance = maxVolDist;
			Volume = volume;
			Priority = priority;
			Enabled = true;
			Biome = biome;
			RequiredTier = requiredTier;
		}
	}
	[Serializable]
	public class OminousConfigData
	{
		public bool Enabled = true;

		public float MasterVolume = 1f;

		public float DuckingStrength = 0.85f;

		public bool PitchScaling = true;

		public float FadeInSpeed = 0.6f;

		public float FadeOutSpeed = 0.35f;

		public float ScanInterval = 0.25f;

		public bool RequireEquippedItem = true;

		public bool EnableBeaconLight = true;

		public float BeaconLightRange = 14f;

		public float BeaconBaseIntensity = 1.2f;

		public bool PingOnPerimeterEnter = true;

		public int WebPort = 8788;

		public List<CreatureOminousEntry> Creatures = new List<CreatureOminousEntry>();
	}
	public static class OminousConfig
	{
		private static string s_pluginDir;

		private static string s_configPath;

		private static string s_tracksDir;

		public static OminousConfigData Data { get; private set; } = new OminousConfigData();

		public static string PluginDir
		{
			get
			{
				if (string.IsNullOrEmpty(s_pluginDir))
				{
					s_pluginDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? ".";
				}
				return s_pluginDir;
			}
		}

		public static string ConfigPath
		{
			get
			{
				if (string.IsNullOrEmpty(s_configPath))
				{
					s_configPath = Path.Combine(PluginDir, "ominous_config.json");
				}
				return s_configPath;
			}
		}

		public static string TracksDir
		{
			get
			{
				if (string.IsNullOrEmpty(s_tracksDir))
				{
					s_tracksDir = Path.Combine(PluginDir, "tracks");
				}
				return s_tracksDir;
			}
		}

		public static void Load()
		{
			try
			{
				if (!Directory.Exists(TracksDir))
				{
					Directory.CreateDirectory(TracksDir);
				}
				if (File.Exists(ConfigPath))
				{
					OminousConfigData ominousConfigData = JsonConvert.DeserializeObject<OminousConfigData>(File.ReadAllText(ConfigPath));
					if (ominousConfigData != null)
					{
						Data = ominousConfigData;
						Logger.LogInfo((object)$"[Ominous] Loaded config with {Data.Creatures.Count} creature entries.");
						return;
					}
				}
			}
			catch (Exception ex)
			{
				Logger.LogWarning((object)("[Ominous] Failed to read config file, generating defaults: " + ex.Message));
			}
			CreateDefaults();
			Save();
		}

		public static void Save()
		{
			try
			{
				string contents = JsonConvert.SerializeObject((object)Data, (Formatting)1);
				File.WriteAllText(ConfigPath, contents);
				Logger.LogInfo((object)"[Ominous] Config saved successfully.");
			}
			catch (Exception ex)
			{
				Logger.LogError((object)("[Ominous] Failed to save config: " + ex.Message));
			}
		}

		public static void CreateDefaults()
		{
			Data = new OminousConfigData
			{
				Enabled = true,
				RequireEquippedItem = true,
				EnableBeaconLight = true,
				BeaconLightRange = 14f,
				BeaconBaseIntensity = 1.2f,
				PingOnPerimeterEnter = true,
				MasterVolume = 1f,
				DuckingStrength = 0.85f,
				PitchScaling = true,
				FadeInSpeed = 0.6f,
				FadeOutSpeed = 0.35f,
				ScanInterval = 0.25f,
				WebPort = 8788,
				Creatures = new List<CreatureOminousEntry>
				{
					new CreatureOminousEntry("Troll", "troll_jaws.wav", 60f, 18f, 0.85f, 10, "BlackForest"),
					new CreatureOminousEntry("Greydwarf_Elite", "wolf_stalker.wav", 45f, 15f, 0.8f, 7, "BlackForest"),
					new CreatureOminousEntry("Skeleton", "wraith_spectral.wav", 40f, 14f, 0.75f, 6, "BlackForest"),
					new CreatureOminousEntry("Wraith", "wraith_spectral.wav", 55f, 20f, 0.85f, 9, "Swamp", 2),
					new CreatureOminousEntry("Abomination", "troll_jaws.wav", 55f, 18f, 0.85f, 9, "Swamp", 2),
					new CreatureOminousEntry("Serpent", "serpent_abyss.wav", 75f, 25f, 0.9f, 10, "Ocean", 2),
					new CreatureOminousEntry("Draugr_Elite", "troll_jaws.wav", 45f, 15f, 0.8f, 8, "Swamp", 2),
					new CreatureOminousEntry("Wolf", "wolf_stalker.wav", 50f, 15f, 0.8f, 8, "Mountain", 3),
					new CreatureOminousEntry("Fenring", "wolf_stalker.wav", 50f, 16f, 0.85f, 9, "Mountain", 3),
					new CreatureOminousEntry("StoneGolem", "troll_jaws.wav", 55f, 18f, 0.85f, 9, "Mountain", 3),
					new CreatureOminousEntry("Deathsquito", "wolf_stalker.wav", 45f, 12f, 0.85f, 9, "Plains", 4),
					new CreatureOminousEntry("Lox", "troll_jaws.wav", 55f, 18f, 0.8f, 7, "Plains", 4),
					new CreatureOminousEntry("GoblinBrute", "troll_jaws.wav", 50f, 16f, 0.85f, 9, "Plains", 4),
					new CreatureOminousEntry("Gjall", "wraith_spectral.wav", 70f, 25f, 0.9f, 10, "Mistlands", 5),
					new CreatureOminousEntry("Seeker", "wolf_stalker.wav", 45f, 15f, 0.8f, 8, "Mistlands", 5),
					new CreatureOminousEntry("SeekerSoldier", "troll_jaws.wav", 55f, 18f, 0.85f, 9, "Mistlands", 5),
					new CreatureOminousEntry("Morgen", "troll_jaws.wav", 60f, 20f, 0.9f, 10, "Ashlands", 6),
					new CreatureOminousEntry("FallenValkyrie", "wraith_spectral.wav", 65f, 22f, 0.9f, 9, "Ashlands", 6),
					new CreatureOminousEntry("Asksvin", "wolf_stalker.wav", 50f, 16f, 0.85f, 8, "Ashlands", 6)
				}
			};
		}

		public static int InferTierForCreature(string prefabName, string biome)
		{
			if (!string.IsNullOrEmpty(biome))
			{
				switch (biome.ToLowerInvariant())
				{
				case "meadows":
				case "blackforest":
					return 1;
				case "ocean":
				case "swamp":
					return 2;
				case "mountain":
				case "mountains":
					return 3;
				case "plains":
					return 4;
				case "mistlands":
					return 5;
				case "ashlands":
				case "deepnorth":
					return 6;
				}
			}
			string text = (prefabName ?? "").ToLowerInvariant();
			if (text.Contains("troll") || text.Contains("greydwarf") || text.Contains("skeleton") || text.Contains("ghost"))
			{
				return 1;
			}
			if (text.Contains("wraith") || text.Contains("abomination") || text.Contains("draugr") || text.Contains("leech") || text.Contains("serpent") || text.Contains("blob"))
			{
				return 2;
			}
			if (text.Contains("wolf") || text.Contains("drake") || text.Contains("fenring") || text.Contains("golem") || text.Contains("bat"))
			{
				return 3;
			}
			if (text.Contains("lox") || text.Contains("mosquito") || text.Contains("goblin") || text.Contains("growth"))
			{
				return 4;
			}
			if (text.Contains("gjall") || text.Contains("seeker") || text.Contains("tick") || text.Contains("dvergr"))
			{
				return 5;
			}
			if (text.Contains("morgen") || text.Contains("valkyrie") || text.Contains("asksvin") || text.Contains("bonemaw") || text.Contains("charred"))
			{
				return 6;
			}
			return 1;
		}

		public static CreatureOminousEntry FindEntry(string prefabName)
		{
			if (string.IsNullOrEmpty(prefabName) || Data == null || Data.Creatures == null)
			{
				return null;
			}
			for (int i = 0; i < Data.Creatures.Count; i++)
			{
				CreatureOminousEntry creatureOminousEntry = Data.Creatures[i];
				if (creatureOminousEntry.Enabled && string.Equals(creatureOminousEntry.Prefab, prefabName, StringComparison.OrdinalIgnoreCase))
				{
					return creatureOminousEntry;
				}
			}
			return null;
		}

		public static IEnumerable<string> GetAllReferencedTracks()
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			if (Data != null && Data.Creatures != null)
			{
				foreach (CreatureOminousEntry creature in Data.Creatures)
				{
					if (!string.IsNullOrEmpty(creature.Track))
					{
						hashSet.Add(creature.Track.Trim());
					}
				}
			}
			return hashSet;
		}
	}
	public static class OminousItem
	{
		public const string PrefabName = "OminousRadar";

		private static readonly FieldRef<Humanoid, ItemData> s_utilityItem = AccessTools.FieldRefAccess<Humanoid, ItemData>("m_utilityItem");

		public static GameObject ItemPrefab { get; private set; }

		public static GameObject WispBallPrefab { get; private set; }

		public static void Init()
		{
			PrefabManager.OnVanillaPrefabsAvailable += RegisterItem;
			LocalizationManager.Instance.AddToken("item_ominous_radar", "Ominous Sounder", false);
			LocalizationManager.Instance.AddToken("item_ominous_radar_desc", "An ancient runic proximity sounder and warning beacon. Floats beside you, illuminating the dark with a calm aura and sounding ominous alerts as predators close in.", false);
		}

		private static void RegisterItem()
		{
			//IL_017d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: Expected O, but got Unknown
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01da: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Expected O, but got Unknown
			//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0203: Expected O, but got Unknown
			//IL_0205: Unknown result type (might be due to invalid IL or missing references)
			//IL_020a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0215: Unknown result type (might be due to invalid IL or missing references)
			//IL_021c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0224: Expected O, but got Unknown
			//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_0236: Unknown result type (might be due to invalid IL or missing references)
			//IL_023d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0245: Expected O, but got Unknown
			//IL_0254: Unknown result type (might be due to invalid IL or missing references)
			//IL_025b: Expected O, but got Unknown
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			PrefabManager.OnVanillaPrefabsAvailable -= RegisterItem;
			try
			{
				GameObject prefab = PrefabManager.Instance.GetPrefab("Demister");
				if ((Object)(object)prefab != (Object)null)
				{
					ItemDrop component = prefab.GetComponent<ItemDrop>();
					if ((Object)(object)component != (Object)null)
					{
						StatusEffect equipStatusEffect = component.m_itemData.m_shared.m_equipStatusEffect;
						SE_Demister val = (SE_Demister)(object)((equipStatusEffect is SE_Demister) ? equipStatusEffect : null);
						if (val != null)
						{
							WispBallPrefab = val.m_ballPrefab;
						}
					}
				}
				GameObject prefab2 = PrefabManager.Instance.GetPrefab("Wishbone");
				if ((Object)(object)prefab2 == (Object)null)
				{
					prefab2 = PrefabManager.Instance.GetPrefab("BeltStrength");
				}
				if ((Object)(object)prefab2 == (Object)null)
				{
					prefab2 = PrefabManager.Instance.GetPrefab("Demister");
				}
				if ((Object)(object)prefab2 == (Object)null)
				{
					Logger.LogWarning((object)"[Ominous] Could not find base utility item to clone.");
					return;
				}
				ItemPrefab = PrefabManager.Instance.CreateClonedPrefab("OminousRadar", prefab2);
				ItemDrop component2 = ItemPrefab.GetComponent<ItemDrop>();
				if ((Object)(object)component2 != (Object)null)
				{
					component2.m_itemData.m_shared.m_name = "$item_ominous_radar";
					component2.m_itemData.m_shared.m_description = "$item_ominous_radar_desc";
					component2.m_itemData.m_shared.m_itemType = (ItemType)18;
					component2.m_itemData.m_shared.m_maxStackSize = 1;
					component2.m_itemData.m_shared.m_weight = 1f;
					component2.m_itemData.m_shared.m_maxQuality = 6;
					component2.m_itemData.m_shared.m_equipStatusEffect = null;
				}
				ItemConfig val2 = new ItemConfig();
				val2.Name = "Ominous Sounder";
				val2.Description = "An ancient runic proximity sounder and warning beacon. Floats beside you, illuminating the dark with a calm aura and sounding ominous alerts as predators close in.";
				val2.CraftingStation = CraftingStations.Workbench;
				val2.MinStationLevel = 1;
				val2.Amount = 1;
				val2.Requirements = (RequirementConfig[])(object)new RequirementConfig[4]
				{
					new RequirementConfig
					{
						Item = "Wood",
						Amount = 10,
						AmountPerLevel = 5
					},
					new RequirementConfig
					{
						Item = "Copper",
						Amount = 2,
						AmountPerLevel = 2
					},
					new RequirementConfig
					{
						Item = "Resin",
						Amount = 5,
						AmountPerLevel = 5
					},
					new RequirementConfig
					{
						Item = "LeatherScraps",
						Amount = 2,
						AmountPerLevel = 2
					}
				};
				ItemConfig val3 = val2;
				CustomItem val4 = new CustomItem(ItemPrefab, false, val3);
				ItemManager.Instance.AddItem(val4);
				Logger.LogInfo((object)"[Ominous] Registered 'Ominous Sounder' utility item and recipe.");
			}
			catch (Exception arg)
			{
				Logger.LogError((object)$"[Ominous] Failed to register Ominous Sounder item: {arg}");
			}
		}

		public static ItemData GetEquippedSounder(Player player)
		{
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Invalid comparison between Unknown and I4
			if ((Object)(object)player == (Object)null)
			{
				return null;
			}
			try
			{
				ItemData val = s_utilityItem.Invoke((Humanoid)(object)player);
				if (val != null)
				{
					if ((Object)(object)val.m_dropPrefab != (Object)null && string.Equals(((Object)val.m_dropPrefab).name, "OminousRadar", StringComparison.OrdinalIgnoreCase))
					{
						return val;
					}
					if (string.Equals(val.m_shared.m_name, "$item_ominous_radar", StringComparison.OrdinalIgnoreCase) || string.Equals(val.m_shared.m_name, "Ominous Sounder", StringComparison.OrdinalIgnoreCase))
					{
						return val;
					}
				}
			}
			catch
			{
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory != null)
			{
				foreach (ItemData equippedItem in inventory.GetEquippedItems())
				{
					if ((int)equippedItem.m_shared.m_itemType == 18 && (((Object)(object)equippedItem.m_dropPrefab != (Object)null && ((Object)equippedItem.m_dropPrefab).name == "OminousRadar") || equippedItem.m_shared.m_name == "$item_ominous_radar" || equippedItem.m_shared.m_name == "Ominous Sounder"))
					{
						return equippedItem;
					}
				}
			}
			return null;
		}

		public static ItemData FindSounderInInventory(Player player)
		{
			if ((Object)(object)player == (Object)null || ((Humanoid)player).GetInventory() == null)
			{
				return null;
			}
			foreach (ItemData allItem in ((Humanoid)player).GetInventory().GetAllItems())
			{
				if ((Object)(object)allItem.m_dropPrefab != (Object)null && string.Equals(((Object)allItem.m_dropPrefab).name, "OminousRadar", StringComparison.OrdinalIgnoreCase))
				{
					return allItem;
				}
				if (string.Equals(allItem.m_shared.m_name, "$item_ominous_radar", StringComparison.OrdinalIgnoreCase) || string.Equals(allItem.m_shared.m_name, "Ominous Sounder", StringComparison.OrdinalIgnoreCase))
				{
					return allItem;
				}
			}
			return null;
		}

		public static bool IsSounderEquipped(Player player)
		{
			return GetEquippedSounder(player) != null;
		}

		public static int GetSounderQuality(ItemData sounder)
		{
			if (sounder == null)
			{
				return 1;
			}
			return Mathf.Clamp(sounder.m_quality, 1, 6);
		}

		public static bool IsBiomeAttuned(ItemData sounder, string biome, int requiredTier = 1)
		{
			if (sounder == null)
			{
				return false;
			}
			if (Mathf.Clamp(sounder.m_quality, 1, 6) >= requiredTier)
			{
				return true;
			}
			if (sounder.m_customData != null)
			{
				if (!string.IsNullOrEmpty(biome) && sounder.m_customData.ContainsKey("Ominous_" + biome))
				{
					return true;
				}
				if (requiredTier <= 2 && (sounder.m_customData.ContainsKey("Ominous_Swamp") || sounder.m_customData.ContainsKey("Ominous_Ocean")))
				{
					return true;
				}
				if (requiredTier <= 3 && sounder.m_customData.ContainsKey("Ominous_Mountain"))
				{
					return true;
				}
				if (requiredTier <= 4 && sounder.m_customData.ContainsKey("Ominous_Plains"))
				{
					return true;
				}
				if (requiredTier <= 5 && sounder.m_customData.ContainsKey("Ominous_Mistlands"))
				{
					return true;
				}
				if (requiredTier <= 6 && sounder.m_customData.ContainsKey("Ominous_Ashlands"))
				{
					return true;
				}
			}
			return false;
		}

		public static void AttuneBiome(ItemData sounder, string biome, int tier)
		{
			if (sounder != null)
			{
				if (sounder.m_customData == null)
				{
					sounder.m_customData = new Dictionary<string, string>();
				}
				if (!string.IsNullOrEmpty(biome))
				{
					sounder.m_customData["Ominous_" + biome] = "true";
				}
				if (tier > sounder.m_quality)
				{
					sounder.m_quality = Mathf.Clamp(tier, 1, 6);
				}
			}
		}
	}
	[HarmonyPatch(typeof(ItemData), "GetTooltip", new Type[]
	{
		typeof(ItemData),
		typeof(int),
		typeof(bool),
		typeof(float),
		typeof(int),
		typeof(bool)
	})]
	public static class Patch_ItemData_GetTooltip
	{
		[HarmonyPostfix]
		public static void Postfix(ItemData item, ref string __result)
		{
			if (item != null && (((Object)(object)item.m_dropPrefab != (Object)null && ((Object)item.m_dropPrefab).name == "OminousRadar") || item.m_shared.m_name == "$item_ominous_radar" || item.m_shared.m_name == "Ominous Sounder"))
			{
				int num = Mathf.Clamp(item.m_quality, 1, 6);
				bool flag = OminousItem.IsBiomeAttuned(item, "Swamp", 2);
				bool flag2 = OminousItem.IsBiomeAttuned(item, "Mountain", 3);
				bool flag3 = OminousItem.IsBiomeAttuned(item, "Plains", 4);
				bool flag4 = OminousItem.IsBiomeAttuned(item, "Mistlands", 5);
				bool flag5 = OminousItem.IsBiomeAttuned(item, "Ashlands", 6);
				float num2 = 50f + (float)(num - 1) * 5f;
				float num3 = 14f + (float)(num - 1) * 2f;
				StringBuilder stringBuilder = new StringBuilder(__result);
				stringBuilder.AppendLine();
				stringBuilder.AppendLine($"\n<color=#FFD760>✦ Biome Attunements (Tier {num}/6) ✦</color>");
				stringBuilder.AppendLine($"<color=#40C8FF>Threat Radar Range:</color> {num2:N0}m");
				stringBuilder.AppendLine($"<color=#40C8FF>Beacon Aura Range:</color> {num3:N0}m");
				stringBuilder.AppendLine("<color=#60E070>  ✓ Meadows & Black Forest</color>");
				stringBuilder.AppendLine(flag ? "<color=#60E070>  ✓ Swamp & Ocean</color>" : "<color=#808080>  ✗ Swamp & Ocean (Locked)</color>");
				stringBuilder.AppendLine(flag2 ? "<color=#60E070>  ✓ Mountains</color>" : "<color=#808080>  ✗ Mountains (Locked)</color>");
				stringBuilder.AppendLine(flag3 ? "<color=#60E070>  ✓ Plains</color>" : "<color=#808080>  ✗ Plains (Locked)</color>");
				stringBuilder.AppendLine(flag4 ? "<color=#60E070>  ✓ Mistlands</color>" : "<color=#808080>  ✗ Mistlands (Locked)</color>");
				stringBuilder.AppendLine(flag5 ? "<color=#60E070>  ✓ Ashlands & Deep North</color>" : "<color=#808080>  ✗ Ashlands & Deep North (Locked)</color>");
				__result = stringBuilder.ToString();
			}
		}
	}
	[BepInPlugin("com.dfg.ominous", "Ominous", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class OminousPlugin : BaseUnityPlugin
	{
		public const string ModGUID = "com.dfg.ominous";

		public const string ModName = "Ominous";

		public const string ModVersion = "1.0.0";

		private Harmony m_harmony;

		private OminousWebGui m_webGui;

		private readonly ConcurrentQueue<Action> m_mainThreadQueue = new ConcurrentQueue<Action>();

		private ConfigEntry<bool> m_cfgEnabled;

		private ConfigEntry<int> m_cfgWebPort;

		private ConfigEntry<bool> m_cfgEnableWebGui;

		public static OminousPlugin Instance { get; private set; }

		public static ClipLibrary Library { get; private set; } = new ClipLibrary();

		private void Awake()
		{
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Expected O, but got Unknown
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			m_cfgEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable or disable Ominous proximity music system.");
			m_cfgEnableWebGui = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableWebGui", true, "Enable the localhost web management dashboard.");
			m_cfgWebPort = ((BaseUnityPlugin)this).Config.Bind<int>("General", "WebPort", 8788, "Localhost port for the web editor (default 8788).");
			OminousConfig.Load();
			OminousConfig.Data.Enabled = m_cfgEnabled.Value;
			m_harmony = Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), "com.dfg.ominous");
			Logger.LogInfo((object)"[Ominous] Harmony patches applied.");
			OminousItem.Init();
			GameObject val = new GameObject("[Ominous_Core]");
			Object.DontDestroyOnLoad((Object)val);
			val.AddComponent<OminousAudio>();
			val.AddComponent<OminousBeacon>();
			val.AddComponent<OminousTracker>();
			ReloadTracks();
			if (m_cfgEnableWebGui.Value)
			{
				int port = ((m_cfgWebPort.Value > 0) ? m_cfgWebPort.Value : 8788);
				m_webGui = new OminousWebGui(this, port);
				m_webGui.Start();
			}
			Logger.LogInfo((object)"[Ominous] Ominous v1.0.0 initialized successfully.");
		}

		private void Update()
		{
			Action result;
			while (m_mainThreadQueue.TryDequeue(out result))
			{
				try
				{
					result?.Invoke();
				}
				catch (Exception arg)
				{
					Logger.LogError((object)$"[Ominous] Main thread action error: {arg}");
				}
			}
		}

		private void OnDestroy()
		{
			m_webGui?.Stop();
			Harmony harmony = m_harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			Library?.Reset();
		}

		public void ReloadTracks()
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			foreach (string allReferencedTrack in OminousConfig.GetAllReferencedTracks())
			{
				hashSet.Add(allReferencedTrack);
			}
			try
			{
				if (Directory.Exists(OminousConfig.TracksDir))
				{
					string[] files = Directory.GetFiles(OminousConfig.TracksDir);
					foreach (string path in files)
					{
						string text = Path.GetExtension(path).ToLowerInvariant();
						if (text == ".wav" || text == ".mp3")
						{
							hashSet.Add(Path.GetFileName(path));
						}
					}
				}
			}
			catch (Exception ex)
			{
				Logger.LogWarning((object)("[Ominous] Error discovering tracks: " + ex.Message));
			}
			Logger.LogInfo((object)$"[Ominous] Loading {hashSet.Count} audio track(s)...");
			((MonoBehaviour)this).StartCoroutine(Library.LoadAll(hashSet, OminousConfig.TracksDir, delegate
			{
				Logger.LogInfo((object)$"[Ominous] Loaded {Library.Loaded} track(s) ({Library.Failed} failed).");
			}));
		}

		public void Ask(Action action)
		{
			if (action != null)
			{
				m_mainThreadQueue.Enqueue(action);
			}
		}
	}
	public class OminousTracker : MonoBehaviour
	{
		private readonly List<Character> m_nearbyChars = new List<Character>(64);

		private float m_scanTimer;

		private bool m_wasInThreat;

		public static OminousTracker Instance { get; private set; }

		public bool HasPlayer { get; private set; }

		public bool IsEquipped { get; private set; }

		public int ActiveQuality { get; private set; } = 1;

		public string ActiveCreaturePrefab { get; private set; } = "";

		public float ActiveCreatureDistance { get; private set; }

		public float ActiveProximityFactor { get; private set; }

		private void Awake()
		{
			if ((Object)(object)Instance != (Object)null && (Object)(object)Instance != (Object)(object)this)
			{
				Object.Destroy((Object)(object)((Component)this).gameObject);
				return;
			}
			Instance = this;
			Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
		}

		private void Update()
		{
			if (OminousConfig.Data == null || !OminousConfig.Data.Enabled)
			{
				if ((Object)(object)OminousAudio.Instance != (Object)null && OminousAudio.Instance.ActiveVolume > 0f)
				{
					OminousAudio.Instance.SetThreat(null, 0f, 1f);
				}
				if ((Object)(object)OminousBeacon.Instance != (Object)null)
				{
					OminousBeacon.Instance.IsActive = false;
				}
				ResetLiveState();
			}
			else
			{
				m_scanTimer += Time.deltaTime;
				float num = Mathf.Max(0.1f, OminousConfig.Data.ScanInterval);
				if (m_scanTimer >= num)
				{
					m_scanTimer = 0f;
					PerformScan();
				}
			}
		}

		private void PerformScan()
		{
			//IL_0116: 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_0198: 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_029d: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead() || (new Func<bool>(((Character)localPlayer).InIntro) != null && ((Character)localPlayer).InIntro()))
			{
				HasPlayer = false;
				IsEquipped = false;
				ActiveQuality = 1;
				if ((Object)(object)OminousBeacon.Instance != (Object)null)
				{
					OminousBeacon.Instance.IsActive = false;
				}
				ResetLiveState();
				OminousAudio.Instance?.SetThreat(null, 0f, 1f);
				return;
			}
			HasPlayer = true;
			ItemData equippedSounder = OminousItem.GetEquippedSounder(localPlayer);
			bool flag = (IsEquipped = equippedSounder != null);
			ActiveQuality = ((equippedSounder == null) ? 1 : OminousItem.GetSounderQuality(equippedSounder));
			if (OminousConfig.Data.RequireEquippedItem && !flag)
			{
				if ((Object)(object)OminousBeacon.Instance != (Object)null)
				{
					OminousBeacon.Instance.IsActive = false;
				}
				ResetLiveState();
				OminousAudio.Instance?.SetThreat(null, 0f, 1f);
				m_wasInThreat = false;
				return;
			}
			if ((Object)(object)OminousBeacon.Instance != (Object)null)
			{
				OminousBeacon.Instance.IsActive = true;
			}
			Vector3 position = ((Component)localPlayer).transform.position;
			float num = 60f + (float)(ActiveQuality - 1) * 5f;
			if (OminousConfig.Data.Creatures != null)
			{
				for (int i = 0; i < OminousConfig.Data.Creatures.Count; i++)
				{
					CreatureOminousEntry creatureOminousEntry = OminousConfig.Data.Creatures[i];
					if (creatureOminousEntry.Enabled && creatureOminousEntry.TriggerDistance > num)
					{
						num = creatureOminousEntry.TriggerDistance;
					}
				}
			}
			m_nearbyChars.Clear();
			Character.GetCharactersInRange(position, num, m_nearbyChars);
			CreatureOminousEntry creatureOminousEntry2 = null;
			float num2 = -1f;
			float num3 = 0f;
			float activeCreatureDistance = 9999f;
			string activeCreaturePrefab = "";
			for (int j = 0; j < m_nearbyChars.Count; j++)
			{
				Character val = m_nearbyChars[j];
				if ((Object)(object)val == (Object)null || val.IsPlayer() || val.IsTamed() || val.IsDead())
				{
					continue;
				}
				string text = ExtractCleanPrefabName(val);
				CreatureOminousEntry creatureOminousEntry3 = OminousConfig.FindEntry(text);
				if (creatureOminousEntry3 == null && !string.IsNullOrEmpty(val.m_name))
				{
					creatureOminousEntry3 = OminousConfig.FindEntry(val.m_name);
				}
				if (creatureOminousEntry3 == null || !creatureOminousEntry3.Enabled)
				{
					continue;
				}
				int requiredTier = ((creatureOminousEntry3.RequiredTier > 0) ? creatureOminousEntry3.RequiredTier : OminousConfig.InferTierForCreature(text, creatureOminousEntry3.Biome));
				if (OminousConfig.Data.RequireEquippedItem && !OminousItem.IsBiomeAttuned(equippedSounder, creatureOminousEntry3.Biome, requiredTier))
				{
					continue;
				}
				float num4 = Vector3.Distance(position, ((Component)val).transform.position);
				if (!(num4 > creatureOminousEntry3.TriggerDistance))
				{
					float num5 = Mathf.Clamp01(1f - (num4 - creatureOminousEntry3.MaxVolumeDistance) / Mathf.Max(1f, creatureOminousEntry3.TriggerDistance - creatureOminousEntry3.MaxVolumeDistance));
					float num6 = (float)creatureOminousEntry3.Priority * 10f + num5 * 10f;
					if (num6 > num2)
					{
						num2 = num6;
						creatureOminousEntry2 = creatureOminousEntry3;
						num3 = num5;
						activeCreatureDistance = num4;
						activeCreaturePrefab = text;
					}
				}
			}
			if (creatureOminousEntry2 != null)
			{
				ActiveCreaturePrefab = activeCreaturePrefab;
				ActiveCreatureDistance = activeCreatureDistance;
				ActiveProximityFactor = num3;
				if (!m_wasInThreat && OminousConfig.Data.PingOnPerimeterEnter)
				{
					OminousBeacon.Instance?.PlayPing();
				}
				m_wasInThreat = true;
				if ((Object)(object)OminousBeacon.Instance != (Object)null)
				{
					OminousBeacon.Instance.TensionFactor = num3;
				}
				float targetVolume = Mathf.Lerp(0.15f, 1f, num3) * creatureOminousEntry2.Volume * OminousConfig.Data.MasterVolume;
				float targetPitch = 1f;
				if (OminousConfig.Data.PitchScaling)
				{
					targetPitch = 1f + 0.15f * num3;
				}
				OminousAudio.Instance?.SetThreat(creatureOminousEntry2.Track, targetVolume, targetPitch);
			}
			else
			{
				m_wasInThreat = false;
				if ((Object)(object)OminousBeacon.Instance != (Object)null)
				{
					OminousBeacon.Instance.TensionFactor = 0f;
				}
				ResetLiveState();
				OminousAudio.Instance?.SetThreat(null, 0f, 1f);
			}
		}

		private static string ExtractCleanPrefabName(Character c)
		{
			if ((Object)(object)c == (Object)null)
			{
				return "";
			}
			string text = ((Object)c).name;
			int num = text.IndexOf("(Clone)", StringComparison.OrdinalIgnoreCase);
			if (num >= 0)
			{
				text = text.Substring(0, num);
			}
			return text.Trim();
		}

		private void ResetLiveState()
		{
			ActiveCreaturePrefab = "";
			ActiveCreatureDistance = 0f;
			ActiveProximityFactor = 0f;
		}
	}
	public class OminousWebGui
	{
		private readonly OminousPlugin m_plugin;

		private readonly int m_port;

		private HttpListener m_listener;

		private Thread m_thread;

		private volatile bool m_running;

		public bool IsRunning => m_running;

		public OminousWebGui(OminousPlugin plugin, int port)
		{
			m_plugin = plugin;
			m_port = port;
		}

		public void Start()
		{
			try
			{
				m_listener = new HttpListener();
				m_listener.Prefixes.Add("http://127.0.0.1:" + m_port + "/");
				m_listener.Start();
				m_running = true;
				m_thread = new Thread(Loop)
				{
					IsBackground = true
				};
				m_thread.Start();
				Logger.LogInfo((object)("[Ominous] Web manager available at http://127.0.0.1:" + m_port + "/"));
			}
			catch (Exception ex)
			{
				m_running = false;
				Logger.LogWarning((object)("[Ominous] Could not start web manager: " + ex.Message));
			}
		}

		public void Stop()
		{
			m_running = false;
			try
			{
				if (m_listener != null)
				{
					m_listener.Stop();
					m_listener.Close();
				}
			}
			catch
			{
			}
			m_listener = null;
		}

		private void Loop()
		{
			while (m_running && m_listener != null && m_listener.IsListening)
			{
				try
				{
					HttpListenerContext context = m_listener.GetContext();
					Handle(context);
				}
				catch
				{
				}
			}
		}

		private void Handle(HttpListenerContext context)
		{
			try
			{
				switch (context.Request.Url.AbsolutePath)
				{
				case "/":
				case "/index.html":
					Write(context, "text/html; charset=utf-8", Page.Html);
					break;
				case "/state":
					HandleState(context);
					break;
				case "/apply":
					HandleApply(context);
					break;
				case "/audio":
					HandleAudio(context);
					break;
				default:
					context.Response.StatusCode = 404;
					Write(context, "text/plain", "Not Found");
					break;
				}
			}
			catch (Exception ex)
			{
				try
				{
					context.Response.StatusCode = 500;
					Write(context, "text/plain", "Internal Error: " + ex.Message);
				}
				catch
				{
				}
			}
		}

		private void HandleState(HttpListenerContext context)
		{
			List<string> list = new List<string>();
			try
			{
				if (Directory.Exists(OminousConfig.TracksDir))
				{
					string[] files = Directory.GetFiles(OminousConfig.TracksDir);
					foreach (string path in files)
					{
						string text = Path.GetExtension(path).ToLowerInvariant();
						if (text == ".wav" || text == ".mp3")
						{
							list.Add(Path.GetFileName(path));
						}
					}
				}
			}
			catch
			{
			}
			string content = JsonConvert.SerializeObject((object)new
			{
				config = OminousConfig.Data,
				tracks = list,
				live = new
				{
					hasPlayer = ((Object)(object)OminousTracker.Instance != (Object)null && OminousTracker.Instance.HasPlayer),
					isEquipped = ((Object)(object)OminousTracker.Instance != (Object)null && OminousTracker.Instance.IsEquipped),
					activeQuality = ((!((Object)(object)OminousTracker.Instance != (Object)null)) ? 1 : OminousTracker.Instance.ActiveQuality),
					activeCreature = (((Object)(object)OminousTracker.Instance != (Object)null) ? OminousTracker.Instance.ActiveCreaturePrefab : ""),
					distance = (((Object)(object)OminousTracker.Instance != (Object)null) ? OminousTracker.Instance.ActiveCreatureDistance : 0f),
					proximityFactor = (((Object)(object)OminousTracker.Instance != (Object)null) ? OminousTracker.Instance.ActiveProximityFactor : 0f),
					activeTrack = (((Object)(object)OminousAudio.Instance != (Object)null) ? OminousAudio.Instance.ActiveTrack : ""),
					activeVolume = (((Object)(object)OminousAudio.Instance != (Object)null) ? OminousAudio.Instance.ActiveVolume : 0f),
					currentDucking = (((Object)(object)OminousAudio.Instance != (Object)null) ? OminousAudio.Instance.CurrentDucking : 0f)
				}
			});
			Write(context, "application/json; charset=utf-8", content);
		}

		private void HandleApply(HttpListenerContext context)
		{
			if (context.Request.HttpMethod != "POST")
			{
				context.Response.StatusCode = 405;
				Write(context, "text/plain", "Method Not Allowed");
				return;
			}
			string text;
			using (StreamReader streamReader = new StreamReader(context.Request.InputStream, context.Request.ContentEncoding))
			{
				text = streamReader.ReadToEnd();
			}
			OminousConfigData updated = JsonConvert.DeserializeObject<OminousConfigData>(text);
			if (updated != null)
			{
				m_plugin.Ask(delegate
				{
					OminousConfig.Data.Enabled = updated.Enabled;
					OminousConfig.Data.RequireEquippedItem = updated.RequireEquippedItem;
					OminousConfig.Data.EnableBeaconLight = updated.EnableBeaconLight;
					OminousConfig.Data.BeaconLightRange = updated.BeaconLightRange;
					OminousConfig.Data.BeaconBaseIntensity = updated.BeaconBaseIntensity;
					OminousConfig.Data.PingOnPerimeterEnter = updated.PingOnPerimeterEnter;
					OminousConfig.Data.MasterVolume = updated.MasterVolume;
					OminousConfig.Data.DuckingStrength = updated.DuckingStrength;
					OminousConfig.Data.PitchScaling = updated.PitchScaling;
					OminousConfig.Data.ScanInterval = updated.ScanInterval;
					OminousConfig.Data.Creatures = updated.Creatures ?? new List<CreatureOminousEntry>();
					OminousConfig.Save();
					m_plugin.ReloadTracks();
				});
				Write(context, "application/json", "{\"status\":\"ok\"}");
			}
			else
			{
				context.Response.StatusCode = 400;
				Write(context, "text/plain", "Invalid Config Payload");
			}
		}

		private void HandleAudio(HttpListenerContext context)
		{
			string text = context.Request.QueryString["file"];
			if (string.IsNullOrEmpty(text))
			{
				context.Response.StatusCode = 400;
				Write(context, "text/plain", "Missing file parameter");
				return;
			}
			string fileName = Path.GetFileName(text);
			string path = Path.Combine(OminousConfig.TracksDir, fileName);
			if (!File.Exists(path))
			{
				context.Response.StatusCode = 404;
				Write(context, "text/plain", "Audio file not found");
				return;
			}
			string contentType = ((Path.GetExtension(fileName).ToLowerInvariant() == ".mp3") ? "audio/mpeg" : "audio/wav");
			byte[] array = File.ReadAllBytes(path);
			context.Response.ContentType = contentType;
			context.Response.ContentLength64 = array.Length;
			context.Response.OutputStream.Write(array, 0, array.Length);
			context.Response.OutputStream.Close();
		}

		private static void Write(HttpListenerContext context, string contentType, string content)
		{
			byte[] bytes = Encoding.UTF8.GetBytes(content);
			context.Response.ContentType = contentType;
			context.Response.ContentLength64 = bytes.Length;
			context.Response.OutputStream.Write(bytes, 0, bytes.Length);
			context.Response.OutputStream.Close();
		}
	}
	public static class Page
	{
		private static string s_html;

		public static string Html
		{
			get
			{
				if (s_html == null)
				{
					s_html = LoadResource("Ominous.Page.html");
				}
				return s_html;
			}
		}

		private static string LoadResource(string resourceName)
		{
			try
			{
				using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName);
				if (stream == null)
				{
					return "<html><body><h1>Resource not found: " + resourceName + "</h1></body></html>";
				}
				using StreamReader streamReader = new StreamReader(stream);
				return streamReader.ReadToEnd();
			}
			catch (Exception ex)
			{
				return "<html><body><h1>Failed to load resource: " + ex.Message + "</h1></body></html>";
			}
		}
	}
	[HarmonyPatch(typeof(MusicMan), "UpdateMusic")]
	public static class Patch_MusicMan_UpdateMusic
	{
		private static readonly FieldRef<MusicMan, AudioSource> s_musicSource = AccessTools.FieldRefAccess<MusicMan, AudioSource>("m_musicSource");

		public static void Postfix(MusicMan __instance)
		{
			if ((Object)(object)OminousAudio.Instance == (Object)null || (Object)(object)__instance == (Object)null)
			{
				return;
			}
			float currentDucking = OminousAudio.Instance.CurrentDucking;
			if (currentDucking > 0.001f)
			{
				AudioSource val = s_musicSource.Invoke(__instance);
				if ((Object)(object)val != (Object)null)
				{
					val.volume *= Mathf.Clamp01(1f - currentDucking);
				}
			}
		}
	}
}