Decompiled source of Mimic v0.6.1

Mods/MimicMod.dll

Decompiled 17 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Text.Json;
using BoneLib.BoneMenu;
using MelonLoader;
using Microsoft.CodeAnalysis;
using MimicMod;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Mod), "Mimic Mod", "0.5.0", "Eryk", null)]
[assembly: MelonGame("Stress Level Zero", "BONELAB")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("MimicMod")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("MimicMod")]
[assembly: AssemblyTitle("MimicMod")]
[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 MimicMod
{
	internal sealed class MimicAI : IDisposable
	{
		private sealed class BotRuntime
		{
			public float NextAttackAt;

			public float AttackModeUntil;

			public float AttackWindupUntil;

			public bool AttackPending;

			public float NextCoverAt;

			public Vector3 LastGoal;

			public bool HasGoal;

			public bool EncounterRecorded;

			public float NextRushRecordAt;

			public MimicBehavior Behavior = new MimicBehavior();

			public float RestoreVisualAt;

			public Renderer[] FlickerRenderers;

			public bool[] RendererStates;
		}

		private readonly Instance _log;

		private MimicSettings _settings = new MimicSettings();

		private MimicLearning _learning;

		private MimicEncounters _encounters;

		private float _nextJumpscareAt;

		private float _nextLearningSampleAt;

		private readonly ArrayList _bots = new ArrayList();

		private readonly Dictionary<object, BotRuntime> _runtime = new Dictionary<object, BotRuntime>();

		private readonly List<GameObject> _goalObjects = new List<GameObject>();

		private readonly Dictionary<object, GameObject> _goalMarkers = new Dictionary<object, GameObject>();

		private Type _managerType;

		private PropertyInfo _allProperty;

		private bool _enabled;

		private bool _selfAware;

		private bool _coverEnabled = true;

		private bool _attacksEnabled = true;

		private bool _visualGlitches;

		private bool _disposed;

		private bool _coverWarningLogged;

		private float _nextRefresh;

		private float _nextDecision;

		private float _nextGlitch;

		private float _sceneRefreshUntil;

		private int _decision;

		private const BindingFlags Flags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		private const float AttackRange = 2.15f;

		private const float AttackWindup = 0.55f;

		private const float CoverRadius = 5.5f;

		public MimicAI(Instance logger)
		{
			_log = logger;
			ResolveDoppel();
		}

		public void NotifySceneLoaded(string sceneName)
		{
			_bots.Clear();
			_runtime.Clear();
			_goalMarkers.Clear();
			for (int i = 0; i < _goalObjects.Count; i++)
			{
				try
				{
					if ((Object)(object)_goalObjects[i] != (Object)null)
					{
						Object.Destroy((Object)(object)_goalObjects[i]);
					}
				}
				catch
				{
				}
			}
			_goalObjects.Clear();
			_managerType = null;
			_allProperty = null;
			_nextRefresh = 0f;
			_nextDecision = 0f;
			_nextLearningSampleAt = 0f;
			_sceneRefreshUntil = Time.time + 30f;
			_log.Msg("Mimic detected level load/reload ('" + (sceneName ?? "unknown") + "'); refreshing Doppel bots.");
		}

		public void Configure(MimicSettings settings, MimicLearning learning, MimicEncounters encounters)
		{
			_settings = settings ?? new MimicSettings();
			_settings.ClampValues();
			_learning = learning;
			_encounters = encounters;
			_selfAware = _settings.selfAwareEnabled;
			_coverEnabled = _settings.stalkingEnabled;
			_attacksEnabled = _settings.attacksEnabled && _settings.behaviorMode != MimicBehaviorMode.NonHostile && _settings.behaviorMode != MimicBehaviorMode.Jumpscare;
			_visualGlitches = _settings.visualGlitchesEnabled || _settings.behaviorMode == MimicBehaviorMode.Jumpscare;
		}

		public void SetEnabled(bool enabled)
		{
			if (_enabled != enabled)
			{
				_enabled = enabled;
				if (!enabled)
				{
					RestoreBots();
				}
				else
				{
					_nextRefresh = 0f;
					_sceneRefreshUntil = Time.time + 30f;
					RefreshBots();
					_nextDecision = Time.time + 0.5f;
				}
				_log.Msg("Mimic AI " + (enabled ? "enabled." : "disabled.") + ".");
			}
		}

		public void SetSelfAware(bool enabled)
		{
			_selfAware = enabled;
			_log.Msg("Experimental self-aware behavior " + (enabled ? "enabled." : "disabled.") + ".");
		}

		public void SetCoverEnabled(bool enabled)
		{
			_coverEnabled = enabled;
			_log.Msg("Cover seeking and stalking " + (enabled ? "enabled." : "disabled.") + ".");
		}

		public void SetAttacksEnabled(bool enabled)
		{
			_attacksEnabled = enabled;
			_log.Msg("Close-range attacks " + (enabled ? "enabled." : "disabled.") + ".");
		}

		public void SetVisualGlitches(bool enabled)
		{
			_visualGlitches = enabled;
			if (!enabled)
			{
				RestoreAllFlickers();
			}
			_log.Msg("Visual glitches " + (enabled ? "enabled." : "disabled.") + ".");
		}

		public void RefreshBots()
		{
			RefreshBots(logResult: true);
		}

		private void RefreshBots(bool logResult)
		{
			ResolveDoppel();
			_bots.Clear();
			if (_allProperty == null)
			{
				if (logResult)
				{
					_log.Warning("Doppel BotManager.All was not found. Ensure Doppel is installed and loaded.");
				}
				return;
			}
			try
			{
				if (_allProperty.GetValue(null, null) is IEnumerable enumerable)
				{
					foreach (object item in enumerable)
					{
						if (item != null)
						{
							_bots.Add(item);
							if (!_runtime.ContainsKey(item))
							{
								_runtime[item] = new BotRuntime();
							}
						}
					}
				}
				List<object> list = null;
				foreach (object key in _runtime.Keys)
				{
					bool flag = false;
					for (int i = 0; i < _bots.Count; i++)
					{
						if (key == _bots[i] || object.Equals(key, _bots[i]))
						{
							flag = true;
							break;
						}
					}
					if (!flag)
					{
						if (list == null)
						{
							list = new List<object>();
						}
						list.Add(key);
					}
				}
				if (list != null)
				{
					for (int j = 0; j < list.Count; j++)
					{
						_runtime.Remove(list[j]);
					}
				}
				if (logResult)
				{
					_log.Msg("Mimic found " + _bots.Count + " existing Doppel bot(s).");
				}
			}
			catch (Exception ex)
			{
				if (logResult)
				{
					_log.Error("Doppel bot refresh failed: " + ex.Message);
				}
			}
		}

		private void ResolveDoppel()
		{
			if (_managerType != null && _allProperty != null)
			{
				return;
			}
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				try
				{
					Type type = assembly.GetType("ThirdEyeBonelabMod.Bots.BotManager", throwOnError: false);
					if (type == null)
					{
						continue;
					}
					_managerType = type;
					_allProperty = type.GetProperty("All", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					break;
				}
				catch
				{
				}
			}
		}

		public void Update()
		{
			//IL_0142: 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)
			//IL_01cc: 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_01e1: 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_01f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_03bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0704: Unknown result type (might be due to invalid IL or missing references)
			//IL_070a: Unknown result type (might be due to invalid IL or missing references)
			//IL_049f: Unknown result type (might be due to invalid IL or missing references)
			//IL_071b: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0726: Unknown result type (might be due to invalid IL or missing references)
			//IL_0728: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0503: Unknown result type (might be due to invalid IL or missing references)
			//IL_0505: Unknown result type (might be due to invalid IL or missing references)
			if (_disposed)
			{
				return;
			}
			UpdateFlickerRestores();
			if (!_enabled)
			{
				return;
			}
			if (Time.time >= _nextRefresh)
			{
				bool flag = Time.time < _sceneRefreshUntil;
				_nextRefresh = Time.time + (flag ? 1f : 8f);
				RefreshBots(logResult: false);
				if (_bots.Count > 0)
				{
					_sceneRefreshUntil = 0f;
					_nextRefresh = Time.time + 8f;
				}
			}
			if (_visualGlitches && Time.time >= _nextGlitch)
			{
				_nextGlitch = Time.time + Random.Range(8f, 18f);
				TryStartRandomFlicker();
			}
			if (Time.time < _nextDecision || _bots.Count == 0)
			{
				return;
			}
			_nextDecision = Time.time + 0.35f;
			Transform val = (((Object)(object)Camera.main != (Object)null) ? ((Component)Camera.main).transform : null);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			MimicLearning mimicLearning = (_settings.learningEnabled ? _learning : null);
			if (mimicLearning != null && Time.time >= _nextLearningSampleAt)
			{
				_nextLearningSampleAt = Time.time + 0.4f;
				mimicLearning.Observe(val.position, val.forward, Time.time);
			}
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			int num4 = 0;
			for (int num5 = _bots.Count - 1; num5 >= 0; num5--)
			{
				object obj = _bots[num5];
				if (obj == null)
				{
					_bots.RemoveAt(num5);
					continue;
				}
				BotRuntime runtime = GetRuntime(obj);
				Transform botTransform = GetBotTransform(obj);
				if ((Object)(object)botTransform == (Object)null)
				{
					SetModeIfDifferent(obj, "Brain");
					continue;
				}
				float num6 = Vector3.Distance(botTransform.position, val.position);
				bool flag2 = IsPlayerLookingAt(val, botTransform.position, num6) && HasLineOfSight(val.position, botTransform.position);
				if (_encounters != null && num6 < 20f && !runtime.EncounterRecorded)
				{
					_encounters.Record("encounter", "Mimic entered player proximity; distance=" + num6.ToString("F1"));
					runtime.EncounterRecorded = true;
				}
				if (num6 > 25f)
				{
					runtime.EncounterRecorded = false;
				}
				if (_settings.behaviorMode == MimicBehaviorMode.Jumpscare)
				{
					if (num6 <= 5f && flag2 && Time.time >= _nextJumpscareAt)
					{
						_nextJumpscareAt = Time.time + _settings.jumpscareCooldown;
						if (_visualGlitches)
						{
							TryStartFlickerForBot(obj);
						}
						if (_encounters != null)
						{
							_encounters.Record("jumpscare", "Close-range jumpscare trigger; recorded audio feature is disabled.");
						}
					}
					if (num6 > 2.15f)
					{
						if (!TrySetRushMode(obj))
						{
							SetModeIfDifferent(obj, "Follow");
						}
					}
					else
					{
						SetModeIfDifferent(obj, "Stand");
					}
					continue;
				}
				if (_settings.behaviorMode == MimicBehaviorMode.NonHostile)
				{
					runtime.AttackPending = false;
					runtime.AttackModeUntil = 0f;
					if (!SetModeIfDifferent(obj, "Wander"))
					{
						SetModeIfDifferent(obj, "Stand");
					}
					continue;
				}
				if (_settings.behaviorMode == MimicBehaviorMode.RuthlessAttacker && num6 > 2.15f)
				{
					if (!TrySetRushMode(obj))
					{
						SetModeIfDifferent(obj, "Follow");
					}
					continue;
				}
				if (_settings.behaviorMode == MimicBehaviorMode.AdaptiveStalker)
				{
					MimicAction mimicAction = runtime.Behavior.Decide(_settings, mimicLearning, Time.time, num6, flag2, HasLineOfSight(val.position, botTransform.position), mimicLearning != null && mimicLearning.AverageSpeed() > 0.12f, num6 <= 2.15f, Time.time >= _nextJumpscareAt, _encounters);
					if (_coverEnabled && mimicAction == MimicAction.Rush)
					{
						if (!TrySetRushMode(obj))
						{
							SetModeIfDifferent(obj, "Follow");
						}
						if (_encounters != null && Time.time >= runtime.NextRushRecordAt)
						{
							_encounters.Record("rush", "Adaptive behavior selected a rush.");
							runtime.NextRushRecordAt = Time.time + 5f;
						}
						continue;
					}
					if (_coverEnabled && mimicAction == MimicAction.Stalk)
					{
						SetModeIfDifferent(obj, "Follow");
						continue;
					}
					if (_coverEnabled && mimicAction == MimicAction.Investigate)
					{
						if (TrySetGoal(obj, val.position))
						{
							SetModeIfDifferent(obj, "Brain");
						}
						else
						{
							SetModeIfDifferent(obj, "Follow");
						}
						continue;
					}
					if (mimicAction == MimicAction.TakeCover && _coverEnabled && TryFindCover(botTransform.position, val.position, out var result) && TrySetGoal(obj, result))
					{
						runtime.LastGoal = result;
						runtime.HasGoal = true;
						SetModeIfDifferent(obj, "Brain");
						continue;
					}
				}
				if (_selfAware && flag2 && num6 < 20f)
				{
					SetModeIfDifferent(obj, "Stand");
					num4++;
					continue;
				}
				if (_attacksEnabled && num6 <= 2.15f)
				{
					if (runtime.AttackPending)
					{
						if (Time.time < runtime.AttackWindupUntil)
						{
							SetModeIfDifferent(obj, "Stand");
							continue;
						}
						runtime.AttackPending = false;
						runtime.AttackModeUntil = Time.time + 0.8f;
						SetModeIfDifferent(obj, "Fists");
						if (_encounters != null)
						{
							_encounters.Record("attack", "Doppel Fists mode entered after wind-up.");
						}
						num++;
					}
					else if (runtime.AttackModeUntil > Time.time)
					{
						SetModeIfDifferent(obj, "Fists");
						num++;
					}
					else if (Time.time >= runtime.NextAttackAt)
					{
						runtime.AttackPending = true;
						runtime.AttackWindupUntil = Time.time + 0.55f;
						runtime.NextAttackAt = Time.time + Mathf.Max(0.5f, _settings.attackCooldown);
						SetModeIfDifferent(obj, "Stand");
					}
					else
					{
						SetModeIfDifferent(obj, "Brain");
					}
					continue;
				}
				if (runtime.AttackModeUntil > 0f && runtime.AttackModeUntil <= Time.time)
				{
					runtime.AttackModeUntil = 0f;
					SetModeIfDifferent(obj, "Brain");
				}
				if (num6 > 2.15f && runtime.AttackPending)
				{
					runtime.AttackPending = false;
				}
				bool flag3 = false;
				if (_coverEnabled && Time.time >= runtime.NextCoverAt)
				{
					runtime.NextCoverAt = Time.time + Random.Range(2.8f, 4.8f);
					if (TryFindCover(botTransform.position, val.position, out var result2) && TrySetGoal(obj, result2))
					{
						runtime.LastGoal = result2;
						runtime.HasGoal = true;
						if (_encounters != null)
						{
							_encounters.Record("cover", "Assigned cover goal.");
						}
						SetModeIfDifferent(obj, "Brain");
						num2++;
						flag3 = true;
					}
				}
				if (!flag3)
				{
					int num7 = (_decision + num5) % 9;
					string modeName = ((!_coverEnabled) ? "Brain" : (num7 switch
					{
						5 => "Follow", 
						0 => "Wander", 
						_ => "Brain", 
					}));
					SetModeIfDifferent(obj, modeName);
					num3++;
				}
			}
			_decision++;
			if (num > 0)
			{
				_log.Msg("Mimic: close-range Doppel combat active for " + num + " bot(s).");
			}
			else if (num2 > 0)
			{
				_log.Msg("Mimic: assigned physics-tested cover goal to " + num2 + " bot(s).");
			}
			else if (num4 > 0)
			{
				_log.Msg("Mimic self-aware behavior: pausing while observed.");
			}
		}

		private BotRuntime GetRuntime(object bot)
		{
			if (!_runtime.TryGetValue(bot, out var value))
			{
				value = new BotRuntime();
				_runtime[bot] = value;
			}
			return value;
		}

		private bool TryFindCover(Vector3 botPosition, Vector3 playerPosition, out Vector3 result)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: 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_0036: 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)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: 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_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: 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_009e: 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_00aa: 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_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: 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_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: 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_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: 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_010e: 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_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: 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_0129: 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_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: 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_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: 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_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: 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_0199: 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_01ae: Unknown result type (might be due to invalid IL or missing references)
			result = botPosition;
			Vector3 val = botPosition - playerPosition;
			val.y = 0f;
			if (((Vector3)(ref val)).sqrMagnitude < 0.1f)
			{
				val = Vector3.back;
			}
			((Vector3)(ref val)).Normalize();
			Vector3 val2 = Vector3.Cross(Vector3.up, val);
			Vector3 normalized = ((Vector3)(ref val2)).normalized;
			float[] array = new float[4] { 2f, 3.2f, 4.5f, 5.5f };
			float[] array2 = new float[5] { 0f, 1.3f, -1.3f, 2.4f, -2.4f };
			float num = float.MaxValue;
			bool result2 = false;
			RaycastHit val4 = default(RaycastHit);
			RaycastHit val7 = default(RaycastHit);
			for (int i = 0; i < array.Length; i++)
			{
				for (int j = 0; j < array2.Length; j++)
				{
					Vector3 val3 = playerPosition + val * array[i] + normalized * array2[j];
					if (!Physics.Raycast(val3 + Vector3.up * 2.2f, Vector3.down, ref val4, 5f, -1, (QueryTriggerInteraction)1))
					{
						continue;
					}
					val3 = ((RaycastHit)(ref val4)).point + Vector3.up * 0.1f;
					Vector3 val5 = val3 + Vector3.up * 1.1f;
					Vector3 val6 = playerPosition + Vector3.up * 1f - val5;
					float magnitude = ((Vector3)(ref val6)).magnitude;
					if (!(magnitude < 0.2f) && Physics.Raycast(val5, val6 / magnitude, ref val7, magnitude, -1, (QueryTriggerInteraction)1) && !Physics.CheckSphere(val3 + Vector3.up * 0.7f, 0.28f, -1, (QueryTriggerInteraction)1))
					{
						float num2 = Vector3.Distance(botPosition, val3) + Vector3.Distance(playerPosition, val3) * 0.25f;
						if (num2 < num && Vector3.Distance(botPosition, val3) > 1f)
						{
							num = num2;
							result = val3;
							result2 = true;
						}
					}
				}
			}
			return result2;
		}

		private static bool HasLineOfSight(Vector3 from, Vector3 to)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = to - from;
			float magnitude = ((Vector3)(ref val)).magnitude;
			if (magnitude < 0.1f)
			{
				return true;
			}
			RaycastHit val2 = default(RaycastHit);
			return !Physics.Raycast(from, val / magnitude, ref val2, magnitude, -1, (QueryTriggerInteraction)1);
		}

		private static bool IsPlayerLookingAt(Transform player, Vector3 target, float distance)
		{
			//IL_000a: 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_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: 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)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			if (distance > 20f)
			{
				return false;
			}
			Vector3 val = target + Vector3.up * 0.8f - player.position;
			if (((Vector3)(ref val)).sqrMagnitude < 0.001f)
			{
				return true;
			}
			return Vector3.Dot(player.forward, ((Vector3)(ref val)).normalized) > 0.84f;
		}

		private bool TrySetGoal(object bot, Vector3 goal)
		{
			//IL_0026: 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)
			try
			{
				Type type = bot.GetType();
				FieldInfo field = type.GetField("goal", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null && TryConvertGoal(bot, field.FieldType, goal, out var value))
				{
					field.SetValue(bot, value);
					return true;
				}
				PropertyInfo property = type.GetProperty("goal", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				if (property != null && property.CanWrite && TryConvertGoal(bot, property.PropertyType, goal, out var value2))
				{
					property.SetValue(bot, value2, null);
					return true;
				}
			}
			catch (Exception ex)
			{
				if (!_coverWarningLogged)
				{
					_log.Warning("Doppel goal assignment failed; using built-in AI modes instead. " + ex.Message);
					_coverWarningLogged = true;
				}
			}
			if (!_coverWarningLogged)
			{
				_log.Warning("This Doppel build does not expose a writable compatible 'goal' member. Cover positions can be detected, but custom goal navigation is unavailable; using Doppel Brain/Wander instead.");
				_coverWarningLogged = true;
			}
			return false;
		}

		private bool TryConvertGoal(object bot, Type targetType, Vector3 goal, out object value)
		{
			//IL_0018: 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_0069: Expected O, but got Unknown
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			value = null;
			if (targetType == typeof(Vector3))
			{
				value = goal;
				return true;
			}
			if (targetType == typeof(Transform) || targetType == typeof(GameObject))
			{
				if (!_goalMarkers.TryGetValue(bot, out var value2) || (Object)(object)value2 == (Object)null)
				{
					value2 = new GameObject("MimicMod_CoverGoal");
					((Object)value2).hideFlags = (HideFlags)61;
					_goalMarkers[bot] = value2;
					_goalObjects.Add(value2);
				}
				value2.transform.position = goal;
				value = ((targetType == typeof(Transform)) ? ((object)value2.transform) : ((object)value2));
				return true;
			}
			return false;
		}

		private static Transform GetBotTransform(object bot)
		{
			try
			{
				Component val = (Component)((bot is Component) ? bot : null);
				if (val != null)
				{
					return val.transform;
				}
				GameObject val2 = (GameObject)((bot is GameObject) ? bot : null);
				if (val2 != null)
				{
					return val2.transform;
				}
				Type type = bot.GetType();
				string[] array = new string[3] { "rig", "transform", "gameObject" };
				foreach (string name in array)
				{
					object obj = ReadMember(type, bot, name);
					Transform val3 = AsTransform(obj);
					if ((Object)(object)val3 != (Object)null)
					{
						return val3;
					}
					if (obj != null)
					{
						Transform val4 = AsTransform(ReadMember(obj.GetType(), obj, "transform"));
						if ((Object)(object)val4 != (Object)null)
						{
							return val4;
						}
					}
				}
			}
			catch
			{
			}
			return null;
		}

		private static object ReadMember(Type type, object instance, string name)
		{
			FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null)
			{
				return field.GetValue(instance);
			}
			PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (property != null && property.GetIndexParameters().Length == 0)
			{
				return property.GetValue(instance, null);
			}
			return null;
		}

		private static Transform AsTransform(object value)
		{
			Transform val = (Transform)((value is Transform) ? value : null);
			if (val != null)
			{
				return val;
			}
			Component val2 = (Component)((value is Component) ? value : null);
			if (val2 != null)
			{
				return val2.transform;
			}
			GameObject val3 = (GameObject)((value is GameObject) ? value : null);
			if (val3 != null)
			{
				return val3.transform;
			}
			return null;
		}

		private bool SetModeIfDifferent(object bot, string modeName)
		{
			try
			{
				Type type = bot.GetType();
				MethodInfo method = type.GetMethod("SetMode", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				if (method == null)
				{
					return false;
				}
				ParameterInfo[] parameters = method.GetParameters();
				if (parameters.Length != 1 || !parameters[0].ParameterType.IsEnum)
				{
					return false;
				}
				object obj = Enum.Parse(parameters[0].ParameterType, modeName, ignoreCase: true);
				object obj2 = ReadMember(type, bot, "mode");
				if (obj2 != null && obj2.Equals(obj))
				{
					return true;
				}
				method.Invoke(bot, new object[1] { obj });
				return true;
			}
			catch
			{
				return false;
			}
		}

		private bool TrySetRushMode(object bot)
		{
			if (!SetModeIfDifferent(bot, "Run") && !SetModeIfDifferent(bot, "Sprint") && !SetModeIfDifferent(bot, "Chase"))
			{
				return SetModeIfDifferent(bot, "Follow");
			}
			return true;
		}

		private void TryStartFlickerForBot(object bot)
		{
			Transform val = ((bot == null) ? null : GetBotTransform(bot));
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			Renderer[] componentsInChildren = ((Component)val).GetComponentsInChildren<Renderer>(true);
			if (componentsInChildren == null || componentsInChildren.Length == 0)
			{
				return;
			}
			BotRuntime runtime = GetRuntime(bot);
			if (runtime.RestoreVisualAt > Time.time)
			{
				return;
			}
			runtime.FlickerRenderers = componentsInChildren;
			runtime.RendererStates = new bool[componentsInChildren.Length];
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				runtime.RendererStates[i] = (Object)(object)componentsInChildren[i] != (Object)null && componentsInChildren[i].enabled;
				if ((Object)(object)componentsInChildren[i] != (Object)null)
				{
					componentsInChildren[i].enabled = false;
				}
			}
			runtime.RestoreVisualAt = Time.time + Random.Range(0.08f, 0.16f);
		}

		private void TryStartRandomFlicker()
		{
			if (_bots.Count == 0)
			{
				return;
			}
			for (int i = 0; i < _bots.Count; i++)
			{
				object obj = _bots[Random.Range(0, _bots.Count)];
				Transform val = ((obj == null) ? null : GetBotTransform(obj));
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				Renderer[] componentsInChildren = ((Component)val).GetComponentsInChildren<Renderer>(true);
				if (componentsInChildren == null || componentsInChildren.Length == 0)
				{
					break;
				}
				BotRuntime runtime = GetRuntime(obj);
				if (runtime.RestoreVisualAt > Time.time)
				{
					break;
				}
				runtime.FlickerRenderers = componentsInChildren;
				runtime.RendererStates = new bool[componentsInChildren.Length];
				for (int j = 0; j < componentsInChildren.Length; j++)
				{
					runtime.RendererStates[j] = (Object)(object)componentsInChildren[j] != (Object)null && componentsInChildren[j].enabled;
					if ((Object)(object)componentsInChildren[j] != (Object)null)
					{
						componentsInChildren[j].enabled = false;
					}
				}
				runtime.RestoreVisualAt = Time.time + Random.Range(0.07f, 0.14f);
				break;
			}
		}

		private void UpdateFlickerRestores()
		{
			foreach (KeyValuePair<object, BotRuntime> item in _runtime)
			{
				BotRuntime value = item.Value;
				if (!(value.RestoreVisualAt <= 0f) && !(Time.time < value.RestoreVisualAt))
				{
					RestoreFlicker(value);
				}
			}
		}

		private void RestoreFlicker(BotRuntime state)
		{
			if (state.FlickerRenderers != null && state.RendererStates != null)
			{
				for (int i = 0; i < state.FlickerRenderers.Length && i < state.RendererStates.Length; i++)
				{
					if ((Object)(object)state.FlickerRenderers[i] != (Object)null)
					{
						state.FlickerRenderers[i].enabled = state.RendererStates[i];
					}
				}
			}
			state.FlickerRenderers = null;
			state.RendererStates = null;
			state.RestoreVisualAt = 0f;
		}

		private void RestoreAllFlickers()
		{
			foreach (BotRuntime value in _runtime.Values)
			{
				RestoreFlicker(value);
			}
		}

		private void RestoreBots()
		{
			RestoreAllFlickers();
			foreach (object bot in _bots)
			{
				if (bot != null)
				{
					SetModeIfDifferent(bot, "Stand");
				}
			}
		}

		public void Dispose()
		{
			if (_disposed)
			{
				return;
			}
			_disposed = true;
			RestoreBots();
			_bots.Clear();
			foreach (GameObject goalObject in _goalObjects)
			{
				if ((Object)(object)goalObject != (Object)null)
				{
					Object.Destroy((Object)(object)goalObject);
				}
			}
			_goalObjects.Clear();
			_goalMarkers.Clear();
			_runtime.Clear();
			_log.Msg("Mimic AI disposed.");
		}
	}
	internal sealed class MimicAudio : IDisposable
	{
		private sealed class MicDevice
		{
			public uint Id;

			public string Name;
		}

		private sealed class WinMicCapture : IDisposable
		{
			private const uint CALLBACK_FUNCTION = 196608u;

			private const uint WIM_DATA = 960u;

			private const int BufferCount = 3;

			private const int BufferBytes = 4096;

			private readonly uint _deviceId;

			private readonly int _sampleRate;

			private readonly Instance _log;

			private readonly object _sync = new object();

			private readonly List<byte> _data = new List<byte>();

			private readonly NativeWaveIn.WaveInProc _callback;

			private readonly List<IntPtr> _headers = new List<IntPtr>();

			private IntPtr _handle;

			private bool _capturing;

			private bool _closed;

			public WinMicCapture(uint deviceId, int sampleRate, Instance logger)
			{
				_deviceId = deviceId;
				_sampleRate = sampleRate;
				_log = logger;
				_callback = OnWaveMessage;
			}

			public void Start()
			{
				NativeWaveIn.WAVEFORMATEX pwfx = new NativeWaveIn.WAVEFORMATEX
				{
					wFormatTag = 1,
					nChannels = 1,
					nSamplesPerSec = (uint)_sampleRate,
					wBitsPerSample = 16,
					nBlockAlign = 2,
					nAvgBytesPerSec = (uint)(_sampleRate * 2),
					cbSize = 0
				};
				int num = NativeWaveIn.waveInOpen(out _handle, _deviceId, ref pwfx, _callback, IntPtr.Zero, 196608u);
				if (num != 0)
				{
					throw new InvalidOperationException("waveInOpen failed with WinMM code " + num + ".");
				}
				_capturing = true;
				for (int i = 0; i < 3; i++)
				{
					IntPtr lpData = Marshal.AllocHGlobal(4096);
					IntPtr intPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(NativeWaveIn.WAVEHDR)));
					Marshal.StructureToPtr(new NativeWaveIn.WAVEHDR
					{
						lpData = lpData,
						dwBufferLength = 4096u,
						dwBytesRecorded = 0u,
						dwUser = UIntPtr.Zero,
						dwFlags = 0u,
						dwLoops = 0u,
						lpNext = IntPtr.Zero,
						reserved = UIntPtr.Zero
					}, intPtr, fDeleteOld: false);
					_headers.Add(intPtr);
					Check(NativeWaveIn.waveInPrepareHeader(_handle, intPtr, (uint)Marshal.SizeOf(typeof(NativeWaveIn.WAVEHDR))), "waveInPrepareHeader");
					Check(NativeWaveIn.waveInAddBuffer(_handle, intPtr, (uint)Marshal.SizeOf(typeof(NativeWaveIn.WAVEHDR))), "waveInAddBuffer");
				}
				Check(NativeWaveIn.waveInStart(_handle), "waveInStart");
			}

			private void OnWaveMessage(IntPtr handle, uint message, IntPtr instance, IntPtr param1, IntPtr param2)
			{
				if (message != 960 || param1 == IntPtr.Zero)
				{
					return;
				}
				try
				{
					NativeWaveIn.WAVEHDR structure = (NativeWaveIn.WAVEHDR)Marshal.PtrToStructure(param1, typeof(NativeWaveIn.WAVEHDR));
					if (structure.dwBytesRecorded != 0 && structure.lpData != IntPtr.Zero)
					{
						byte[] array = new byte[structure.dwBytesRecorded];
						Marshal.Copy(structure.lpData, array, 0, array.Length);
						lock (_sync)
						{
							if (_capturing)
							{
								_data.AddRange(array);
							}
						}
					}
					lock (_sync)
					{
						if (_capturing && !_closed)
						{
							structure.dwBytesRecorded = 0u;
							Marshal.StructureToPtr(structure, param1, fDeleteOld: false);
							NativeWaveIn.waveInAddBuffer(handle, param1, (uint)Marshal.SizeOf(typeof(NativeWaveIn.WAVEHDR)));
						}
					}
				}
				catch (Exception ex)
				{
					_log.Warning("Microphone callback error: " + ex.Message);
				}
			}

			public byte[] StopAndGetPcm()
			{
				lock (_sync)
				{
					_capturing = false;
				}
				if (_handle != IntPtr.Zero)
				{
					NativeWaveIn.waveInStop(_handle);
					NativeWaveIn.waveInReset(_handle);
				}
				lock (_sync)
				{
					return _data.ToArray();
				}
			}

			private static void Check(int code, string operation)
			{
				if (code != 0)
				{
					throw new InvalidOperationException(operation + " failed with WinMM code " + code + ".");
				}
			}

			public void Dispose()
			{
				if (_closed)
				{
					return;
				}
				_closed = true;
				lock (_sync)
				{
					_capturing = false;
				}
				if (_handle != IntPtr.Zero)
				{
					NativeWaveIn.waveInStop(_handle);
					NativeWaveIn.waveInReset(_handle);
					foreach (IntPtr header in _headers)
					{
						try
						{
							NativeWaveIn.waveInUnprepareHeader(_handle, header, (uint)Marshal.SizeOf(typeof(NativeWaveIn.WAVEHDR)));
						}
						catch
						{
						}
						try
						{
							NativeWaveIn.WAVEHDR wAVEHDR = (NativeWaveIn.WAVEHDR)Marshal.PtrToStructure(header, typeof(NativeWaveIn.WAVEHDR));
							if (wAVEHDR.lpData != IntPtr.Zero)
							{
								Marshal.FreeHGlobal(wAVEHDR.lpData);
							}
						}
						catch
						{
						}
						Marshal.FreeHGlobal(header);
					}
					NativeWaveIn.waveInClose(_handle);
					_handle = IntPtr.Zero;
				}
				_headers.Clear();
			}
		}

		private static class NativeWaveIn
		{
			[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
			internal struct WAVEINCAPS
			{
				public ushort wMid;

				public ushort wPid;

				public uint vDriverVersion;

				[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
				public string szPname;

				public uint dwFormats;

				public ushort wChannels;

				public ushort wReserved1;

				public uint dwSupport;
			}

			internal struct WAVEFORMATEX
			{
				public ushort wFormatTag;

				public ushort nChannels;

				public uint nSamplesPerSec;

				public uint nAvgBytesPerSec;

				public ushort nBlockAlign;

				public ushort wBitsPerSample;

				public ushort cbSize;
			}

			internal struct WAVEHDR
			{
				public IntPtr lpData;

				public uint dwBufferLength;

				public uint dwBytesRecorded;

				public UIntPtr dwUser;

				public uint dwFlags;

				public uint dwLoops;

				public IntPtr lpNext;

				public UIntPtr reserved;
			}

			internal delegate void WaveInProc(IntPtr hwi, uint uMsg, IntPtr dwInstance, IntPtr dwParam1, IntPtr dwParam2);

			[DllImport("winmm.dll")]
			internal static extern uint waveInGetNumDevs();

			[DllImport("winmm.dll", CharSet = CharSet.Unicode, EntryPoint = "waveInGetDevCapsW")]
			internal static extern int waveInGetDevCaps(UIntPtr uDeviceID, ref WAVEINCAPS pwic, uint cbwic);

			[DllImport("winmm.dll")]
			internal static extern int waveInOpen(out IntPtr phwi, uint uDeviceID, ref WAVEFORMATEX pwfx, WaveInProc dwCallback, IntPtr dwInstance, uint fdwOpen);

			[DllImport("winmm.dll")]
			internal static extern int waveInPrepareHeader(IntPtr hwi, IntPtr pwh, uint cbwh);

			[DllImport("winmm.dll")]
			internal static extern int waveInUnprepareHeader(IntPtr hwi, IntPtr pwh, uint cbwh);

			[DllImport("winmm.dll")]
			internal static extern int waveInAddBuffer(IntPtr hwi, IntPtr pwh, uint cbwh);

			[DllImport("winmm.dll")]
			internal static extern int waveInStart(IntPtr hwi);

			[DllImport("winmm.dll")]
			internal static extern int waveInStop(IntPtr hwi);

			[DllImport("winmm.dll")]
			internal static extern int waveInReset(IntPtr hwi);

			[DllImport("winmm.dll")]
			internal static extern int waveInClose(IntPtr hwi);
		}

		private readonly Instance _log;

		private readonly AudioClip[] _clips = (AudioClip[])(object)new AudioClip[12];

		private readonly List<MicDevice> _devices = new List<MicDevice>();

		private int _clipCount;

		private int _nextClip;

		private int _deviceIndex;

		private WinMicCapture _capture;

		private AudioSource _source;

		private GameObject _audioObject;

		private bool _recordingEnabled;

		private bool _recordingInProgress;

		private bool _autonomous;

		private bool _glitches;

		private bool _disposed;

		private float _recordStartedAt;

		private float _nextAutonomousAt;

		private float _nextScanAt;

		private const int SampleRate = 22050;

		private const int ClipSeconds = 5;

		public int MicrophoneCount => _devices.Count;

		public int SelectedMicrophoneIndex => _deviceIndex;

		public string SelectedMicrophoneName
		{
			get
			{
				if (_devices.Count > 0 && _deviceIndex >= 0 && _deviceIndex < _devices.Count)
				{
					return _devices[_deviceIndex].Name;
				}
				return "No microphone detected";
			}
		}

		public bool HasRecordedClip => _clipCount > 0;

		public MimicAudio(Instance logger)
		{
			_log = logger;
			RefreshDevices();
		}

		public void Update()
		{
			if (_disposed)
			{
				return;
			}
			if (Time.unscaledTime >= _nextScanAt)
			{
				_nextScanAt = Time.unscaledTime + 5f;
				if (_devices.Count == 0 && !_recordingInProgress)
				{
					RefreshDevices();
				}
			}
			if (_recordingInProgress && Time.unscaledTime - _recordStartedAt >= 5f)
			{
				FinishRecording();
			}
			if (_autonomous && _clipCount > 0 && Time.unscaledTime >= _nextAutonomousAt && ((Object)(object)_source == (Object)null || !_source.isPlaying))
			{
				PlayRandomClip();
				_nextAutonomousAt = Time.unscaledTime + Random.Range(8f, 22f);
			}
		}

		public void SetRecordingEnabled(bool enabled)
		{
			_recordingEnabled = enabled;
			if (!enabled && _recordingInProgress)
			{
				FinishRecording();
			}
			_log.Msg("Native Windows microphone recording " + (enabled ? "enabled." : "disabled."));
			if (enabled && _devices.Count == 0)
			{
				RefreshDevices();
			}
		}

		public void SetAutonomous(bool enabled)
		{
			_autonomous = enabled;
			_nextAutonomousAt = Time.unscaledTime + Random.Range(4f, 10f);
			_log.Msg("Autonomous voice playback " + (enabled ? "enabled." : "disabled."));
		}

		public void SetGlitches(bool enabled)
		{
			_glitches = enabled;
			_log.Msg("Audio glitches " + (enabled ? "enabled." : "disabled."));
		}

		public void CycleMicrophone()
		{
			if (_recordingInProgress)
			{
				_log.Warning("Finish the current recording before changing microphones.");
				return;
			}
			RefreshDevices();
			if (_devices.Count == 0)
			{
				_log.Warning("Windows waveIn did not report any recording devices.");
				return;
			}
			_deviceIndex = (_deviceIndex + 1) % _devices.Count;
			_log.Msg("Selected Windows microphone: " + _devices[_deviceIndex].Name + " (device " + _devices[_deviceIndex].Id + ").");
		}

		public void RefreshMicrophones()
		{
			RefreshDevices();
		}

		public void LogMicrophones()
		{
			RefreshDevices();
			if (_devices.Count == 0)
			{
				_log.Warning("No Windows microphones found.");
				return;
			}
			_log.Msg("=== Mimic Mod: Available Microphones ===");
			for (int i = 0; i < _devices.Count; i++)
			{
				string text = ((i == _deviceIndex) ? " [SELECTED]" : "");
				_log.Msg("[" + i + "] " + _devices[i].Name + text);
			}
		}

		public string[] GetMicrophoneNames()
		{
			string[] array = new string[_devices.Count];
			for (int i = 0; i < _devices.Count; i++)
			{
				array[i] = _devices[i].Name;
			}
			return array;
		}

		public void SelectMicrophone(int index)
		{
			if (_recordingInProgress)
			{
				_log.Warning("Finish the current recording before changing microphones.");
				return;
			}
			if (index < 0 || index >= _devices.Count)
			{
				_log.Warning("Microphone index is out of range. Refresh the device list.");
				return;
			}
			_deviceIndex = index;
			_log.Msg("Selected Windows microphone: " + _devices[_deviceIndex].Name + " (device " + _devices[_deviceIndex].Id + ").");
		}

		public void SelectMicrophone(string name)
		{
			if (_recordingInProgress)
			{
				_log.Warning("Finish the current recording before changing microphones.");
				return;
			}
			for (int i = 0; i < _devices.Count; i++)
			{
				if (string.Equals(_devices[i].Name, name, StringComparison.OrdinalIgnoreCase))
				{
					SelectMicrophone(i);
					return;
				}
			}
			_log.Warning("That microphone is no longer available. Refresh the microphone list.");
		}

		private void RefreshDevices()
		{
			try
			{
				string text = ((_devices.Count > 0 && _deviceIndex >= 0 && _deviceIndex < _devices.Count) ? _devices[_deviceIndex].Name : null);
				_devices.Clear();
				uint num = NativeWaveIn.waveInGetNumDevs();
				for (uint num2 = 0u; num2 < num; num2++)
				{
					NativeWaveIn.WAVEINCAPS pwic = default(NativeWaveIn.WAVEINCAPS);
					if (NativeWaveIn.waveInGetDevCaps(new UIntPtr(num2), ref pwic, (uint)Marshal.SizeOf(typeof(NativeWaveIn.WAVEINCAPS))) == 0)
					{
						_devices.Add(new MicDevice
						{
							Id = num2,
							Name = (string.IsNullOrEmpty(pwic.szPname) ? ("Windows microphone " + num2) : pwic.szPname)
						});
					}
				}
				_deviceIndex = 0;
				if (!string.IsNullOrEmpty(text))
				{
					for (int i = 0; i < _devices.Count; i++)
					{
						if (_devices[i].Name == text)
						{
							_deviceIndex = i;
							break;
						}
					}
				}
				if (_devices.Count == 0)
				{
					_log.Warning("No Windows waveIn recording devices found. Check Windows sound settings and Quest Link microphone routing.");
				}
				else
				{
					_log.Msg("Windows microphones found: " + _devices.Count + ". Selected: " + _devices[_deviceIndex].Name);
				}
			}
			catch (Exception ex)
			{
				_log.Error("Windows microphone enumeration failed: " + ex.Message);
			}
		}

		public void RecordTestClip()
		{
			if (_disposed)
			{
				return;
			}
			if (!_recordingEnabled)
			{
				_log.Warning("Turn on 'Microphone Recording' in BoneMenu first.");
				return;
			}
			if (_recordingInProgress)
			{
				_log.Warning("A recording is already in progress.");
				return;
			}
			if (_devices.Count == 0)
			{
				RefreshDevices();
			}
			if (_devices.Count == 0)
			{
				_log.Warning("No Windows microphone available.");
				return;
			}
			try
			{
				int index = Math.Max(0, Math.Min(_deviceIndex, _devices.Count - 1));
				_capture = new WinMicCapture(_devices[index].Id, 22050, _log);
				_capture.Start();
				_recordingInProgress = true;
				_recordStartedAt = Time.unscaledTime;
				_log.Msg("Recording a 5-second native Windows microphone test. Speak now.");
			}
			catch (Exception ex)
			{
				_recordingInProgress = false;
				if (_capture != null)
				{
					_capture.Dispose();
				}
				_capture = null;
				_log.Error("Native microphone capture failed: " + ex.Message);
			}
		}

		private void FinishRecording()
		{
			if (!_recordingInProgress)
			{
				return;
			}
			byte[] array = null;
			try
			{
				if (_capture != null)
				{
					array = _capture.StopAndGetPcm();
				}
				if (array == null || array.Length < 2048)
				{
					_log.Warning("Microphone capture returned too little audio. Check the selected Windows input device and its privacy permission.");
					return;
				}
				int num = array.Length / 2;
				float[] array2 = new float[num];
				int num2 = 0;
				int num3 = 0;
				while (num2 < num)
				{
					short num4 = (short)(array[num3] | (array[num3 + 1] << 8));
					array2[num2] = (float)num4 / 32768f;
					num2++;
					num3 += 2;
				}
				AudioClip val = AudioClip.Create("MimicMic_" + DateTime.Now.Ticks, num, 1, 22050, false);
				val.SetData(array2, 0);
				AudioClip val2 = _clips[_nextClip];
				if ((Object)(object)val2 != (Object)null)
				{
					Object.Destroy((Object)(object)val2);
				}
				_clips[_nextClip] = val;
				_nextClip = (_nextClip + 1) % _clips.Length;
				_clipCount = Math.Min(_clipCount + 1, _clips.Length);
				_log.Msg("Native mic test captured " + num + " samples. Stored clips: " + _clipCount + ". Use Play Latest Clip.");
			}
			catch (Exception ex)
			{
				_log.Error("Could not finish native microphone recording: " + ex.Message);
			}
			finally
			{
				_recordingInProgress = false;
				if (_capture != null)
				{
					_capture.Dispose();
				}
				_capture = null;
			}
		}

		public void PlayLatest()
		{
			AudioClip val = ((_clipCount > 0) ? _clips[(_nextClip - 1 + _clips.Length) % _clips.Length] : null);
			if ((Object)(object)val == (Object)null)
			{
				_log.Warning("No recorded clips yet. Enable recording and record a test clip first.");
			}
			else
			{
				PlayClip(val, null);
			}
		}

		public void PlayLatestAt(Transform location)
		{
			AudioClip val = ((_clipCount > 0) ? _clips[(_nextClip - 1 + _clips.Length) % _clips.Length] : null);
			if (!((Object)(object)val == (Object)null))
			{
				PlayClip(val, location);
			}
		}

		private void PlayRandomClip()
		{
			for (int i = 0; i < _clips.Length; i++)
			{
				AudioClip val = _clips[Random.Range(0, _clips.Length)];
				if ((Object)(object)val != (Object)null)
				{
					PlayClip(val, null);
					break;
				}
			}
		}

		private void PlayClip(AudioClip clip, Transform location)
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)clip == (Object)null || _disposed)
			{
				return;
			}
			EnsureAudioSource();
			if ((Object)(object)_source == (Object)null)
			{
				return;
			}
			_source.Stop();
			((Component)_source).transform.SetParent((Transform)null, false);
			if ((Object)(object)location != (Object)null)
			{
				((Component)_source).transform.position = location.position;
				_source.spatialBlend = 1f;
				_source.minDistance = 1f;
				_source.maxDistance = 22f;
				_source.rolloffMode = (AudioRolloffMode)0;
			}
			else
			{
				Transform val = (((Object)(object)Camera.main != (Object)null) ? ((Component)Camera.main).transform : null);
				if ((Object)(object)val != (Object)null)
				{
					((Component)_source).transform.SetParent(val, false);
				}
				((Component)_source).transform.localPosition = new Vector3(0f, 0f, 1.2f);
				_source.spatialBlend = 0f;
			}
			_source.clip = clip;
			_source.pitch = (_glitches ? Random.Range(0.78f, 1.2f) : 1f);
			_source.volume = (_glitches ? Random.Range(0.55f, 1f) : 1f);
			_source.Play();
			_log.Msg("Playing captured microphone audio" + (((Object)(object)location != (Object)null) ? " from Mimic position" : " near the player") + (_glitches ? " with glitch variation." : "."));
		}

		private void EnsureAudioSource()
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_source != (Object)null) && !_disposed)
			{
				_audioObject = new GameObject("MimicMod_Audio");
				Object.DontDestroyOnLoad((Object)(object)_audioObject);
				Transform val = (((Object)(object)Camera.main != (Object)null) ? ((Component)Camera.main).transform : null);
				if ((Object)(object)val != (Object)null)
				{
					_audioObject.transform.SetParent(val, false);
				}
				_audioObject.transform.localPosition = new Vector3(0f, 0f, 1.2f);
				_source = _audioObject.AddComponent<AudioSource>();
				_source.playOnAwake = false;
				_source.loop = false;
				_source.spatialBlend = 0f;
				_source.dopplerLevel = 0f;
			}
		}

		public void StopPlayback()
		{
			if ((Object)(object)_source != (Object)null)
			{
				_source.Stop();
			}
			_log.Msg("Mimic audio playback stopped.");
		}

		public void Dispose()
		{
			if (_disposed)
			{
				return;
			}
			_autonomous = false;
			if (_recordingInProgress)
			{
				FinishRecording();
			}
			if (_capture != null)
			{
				_capture.Dispose();
				_capture = null;
			}
			_disposed = true;
			if ((Object)(object)_source != (Object)null)
			{
				_source.Stop();
			}
			for (int i = 0; i < _clips.Length; i++)
			{
				if ((Object)(object)_clips[i] != (Object)null)
				{
					Object.Destroy((Object)(object)_clips[i]);
				}
			}
			if ((Object)(object)_audioObject != (Object)null)
			{
				Object.Destroy((Object)(object)_audioObject);
			}
		}
	}
	internal enum MimicAction
	{
		Observe,
		Stalk,
		TakeCover,
		Investigate,
		Rush,
		Attack,
		Jumpscare,
		Ignore
	}
	internal sealed class MimicBehavior
	{
		private float _nextDecisionAt;

		private float _lastJumpscareAt = -99999f;

		private float _lastAttackAt = -99999f;

		public MimicAction Decide(MimicSettings settings, MimicLearning learning, float now, float distanceToPlayer, bool playerLookingAtMimic, bool lineOfSight, bool playerMoving, bool canAttack, bool jumpscareAvailable, MimicEncounters encounters = null)
		{
			if (settings == null || !settings.enabled)
			{
				return MimicAction.Observe;
			}
			if (settings.behaviorMode == MimicBehaviorMode.NonHostile)
			{
				if (!lineOfSight)
				{
					return MimicAction.Investigate;
				}
				return MimicAction.Observe;
			}
			float num = settings.rushChance;
			if (encounters != null && encounters.totalEncounters >= 3)
			{
				float num2 = (float)encounters.totalRushes / (float)Math.Max(1, encounters.totalEncounters);
				float num3 = (float)encounters.totalAttacks / (float)Math.Max(1, encounters.totalEncounters);
				float num4 = Mathf.Clamp(0.85f + num2 * 0.35f + num3 * 0.15f, 0.75f, 1.5f);
				num = Mathf.Clamp01(num * num4);
			}
			if (settings.behaviorMode == MimicBehaviorMode.Jumpscare && jumpscareAvailable && now - _lastJumpscareAt >= settings.jumpscareCooldown && distanceToPlayer < 5f && lineOfSight)
			{
				_lastJumpscareAt = now;
				return MimicAction.Jumpscare;
			}
			if (settings.behaviorMode == MimicBehaviorMode.RuthlessAttacker)
			{
				if (settings.attacksEnabled && canAttack && now - _lastAttackAt >= settings.attackCooldown)
				{
					_lastAttackAt = now;
					return MimicAction.Attack;
				}
				if (distanceToPlayer > 2.2f)
				{
					return MimicAction.Rush;
				}
				if (!settings.attacksEnabled)
				{
					return MimicAction.Observe;
				}
				return MimicAction.Attack;
			}
			if (now < _nextDecisionAt)
			{
				return MimicAction.Observe;
			}
			_nextDecisionAt = now + Random.Range(0.65f, 1.8f);
			if (settings.attacksEnabled && canAttack && distanceToPlayer < 2.15f && now - _lastAttackAt >= settings.attackCooldown && Random.value < 0.25f)
			{
				_lastAttackAt = now;
				return MimicAction.Attack;
			}
			if (playerLookingAtMimic && distanceToPlayer > 3f)
			{
				return MimicAction.TakeCover;
			}
			if (lineOfSight && distanceToPlayer > 3f && learning != null && learning.AverageTurn() > 35f && Random.value < 0.2f)
			{
				return MimicAction.TakeCover;
			}
			if (!lineOfSight)
			{
				return MimicAction.Investigate;
			}
			if (distanceToPlayer > settings.stalkDistance)
			{
				if (!(Random.value < num))
				{
					return MimicAction.Stalk;
				}
				return MimicAction.Rush;
			}
			if (learning != null && learning.AveragePause() > 2.5f && Random.value < num * 0.5f)
			{
				return MimicAction.Rush;
			}
			if (!playerMoving)
			{
				if (!(Random.value < 0.35f))
				{
					return MimicAction.Observe;
				}
				return MimicAction.TakeCover;
			}
			return MimicAction.Stalk;
		}
	}
	internal sealed class MimicDataManager
	{
		private readonly Instance _log;

		private readonly JsonSerializerOptions _options = new JsonSerializerOptions
		{
			WriteIndented = true,
			IncludeFields = true
		};

		public MimicSettings Settings { get; private set; }

		public MimicLearning Learning { get; private set; }

		public MimicEncounters Encounters { get; private set; }

		public string DirectoryPath { get; private set; }

		public MimicDataManager(Instance logger, string rootDirectory = null)
		{
			_log = logger;
			DirectoryPath = Path.Combine(string.IsNullOrWhiteSpace(rootDirectory) ? Environment.CurrentDirectory : rootDirectory, "Userdata", "Mimic");
			Settings = new MimicSettings();
			Learning = new MimicLearning();
			Encounters = new MimicEncounters();
		}

		public void Load()
		{
			Directory.CreateDirectory(DirectoryPath);
			Settings = LoadFile("MimicSettings.json", Settings);
			Learning = LoadFile("MimicLearning.json", Learning);
			Encounters = LoadFile("MimicEncounters.json", Encounters);
			if (Settings == null)
			{
				Settings = new MimicSettings();
			}
			if (Learning == null)
			{
				Learning = new MimicLearning();
			}
			if (Encounters == null)
			{
				Encounters = new MimicEncounters();
			}
			Settings.ClampValues();
			SaveAll();
			_log.Msg("Mimic data directory: " + DirectoryPath);
		}

		public void SaveAll()
		{
			SaveFile("MimicSettings.json", Settings);
			SaveFile("MimicLearning.json", Learning);
			SaveFile("MimicEncounters.json", Encounters);
			SaveBrain(JsonSerializer.Serialize(new
			{
				version = 1,
				behaviorMode = Settings.behaviorMode.ToString(),
				enabled = Settings.enabled,
				learningEnabled = Settings.learningEnabled,
				sessions = Learning.sessions,
				averageSpeed = Learning.AverageSpeed(),
				averagePause = Learning.AveragePause(),
				averageTurn = Learning.AverageTurn(),
				savedUtc = DateTime.UtcNow.ToString("o")
			}, _options));
		}

		public void SaveBrain(string json)
		{
			try
			{
				WriteAtomically(Path.Combine(DirectoryPath, "MimicBrain.json"), json ?? "{}");
			}
			catch (Exception ex)
			{
				_log.Error("Could not save MimicBrain.json: " + ex.Message);
			}
		}

		private T LoadFile<T>(string name, T fallback) where T : class
		{
			string path = Path.Combine(DirectoryPath, name);
			if (!File.Exists(path))
			{
				return fallback;
			}
			try
			{
				return JsonSerializer.Deserialize<T>(File.ReadAllText(path), _options) ?? fallback;
			}
			catch (Exception ex)
			{
				_log.Warning("Could not load " + name + ": " + ex.Message + ". Keeping defaults.");
				return fallback;
			}
		}

		private void SaveFile<T>(string name, T value)
		{
			try
			{
				string content = JsonSerializer.Serialize(value, _options);
				WriteAtomically(Path.Combine(DirectoryPath, name), content);
			}
			catch (Exception ex)
			{
				_log.Error("Could not save " + name + ": " + ex.Message);
			}
		}

		private static void WriteAtomically(string path, string content)
		{
			Directory.CreateDirectory(Path.GetDirectoryName(path));
			string text = path + ".tmp";
			File.WriteAllText(text, content);
			if (File.Exists(path))
			{
				string destinationBackupFileName = path + ".bak";
				try
				{
					File.Replace(text, path, destinationBackupFileName, ignoreMetadataErrors: true);
					return;
				}
				catch
				{
					File.Copy(text, path, overwrite: true);
					File.Delete(text);
					return;
				}
			}
			File.Move(text, path);
		}
	}
	[Serializable]
	public sealed class MimicEncounterRecord
	{
		public string utc;

		public string eventType;

		public string note;
	}
	[Serializable]
	public sealed class MimicEncounters
	{
		public int version = 1;

		public int totalEncounters;

		public int totalRushes;

		public int totalAttacks;

		public int totalJumpscares;

		public string lastEncounterUtc = "";

		public List<MimicEncounterRecord> history = new List<MimicEncounterRecord>();

		public void Record(string eventType, string note = "")
		{
			string text = (eventType ?? "event").Trim().ToLowerInvariant();
			string utc = (lastEncounterUtc = DateTime.UtcNow.ToString("o"));
			if (text.Contains("encounter"))
			{
				totalEncounters++;
			}
			if (text.Contains("rush"))
			{
				totalRushes++;
			}
			if (text.Contains("attack"))
			{
				totalAttacks++;
			}
			if (text.Contains("jumpscare"))
			{
				totalJumpscares++;
			}
			history.Add(new MimicEncounterRecord
			{
				utc = utc,
				eventType = text,
				note = (note ?? "")
			});
			if (history.Count > 250)
			{
				history.RemoveAt(0);
			}
		}
	}
	[Serializable]
	public sealed class MimicLearning
	{
		public int version = 1;

		public int sessions;

		public List<float> movementGaps = new List<float>();

		public List<float> walkingBursts = new List<float>();

		public List<float> pauses = new List<float>();

		public List<float> turns = new List<float>();

		public List<float> directionChanges = new List<float>();

		public List<string> observations = new List<string>();

		[NonSerialized]
		private Vector3 _lastPosition;

		[NonSerialized]
		private Vector3 _lastForward;

		[NonSerialized]
		private float _lastSampleAt;

		[NonSerialized]
		private float _lastMovingAt;

		[NonSerialized]
		private bool _initialized;

		[NonSerialized]
		private bool _wasMoving;

		public void BeginSession()
		{
			sessions++;
			_initialized = false;
		}

		public void Observe(Vector3 position, Vector3 forward, float now)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: 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_004d: 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_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: 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_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: 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_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)
			if (!_initialized)
			{
				_lastPosition = position;
				_lastForward = forward;
				_lastSampleAt = now;
				_lastMovingAt = now;
				_initialized = true;
				return;
			}
			float num = Mathf.Clamp(now - _lastSampleAt, 0f, 5f);
			if (!(num <= 0.001f))
			{
				float num2 = Vector3.Distance(position, _lastPosition) / num;
				bool flag = num2 > 0.12f;
				Add(movementGaps, num, 80);
				if (flag)
				{
					Add(walkingBursts, num2, 80);
				}
				if (_wasMoving && !flag)
				{
					Add(pauses, Mathf.Max(0f, now - _lastMovingAt), 80);
					Add(observations, "pause:" + (now - _lastMovingAt).ToString("F2"), 150);
				}
				if (flag && !_wasMoving)
				{
					_lastMovingAt = now;
					Add(observations, "move-start:" + now.ToString("F2"), 150);
				}
				float num3 = Vector3.Angle(_lastForward, forward);
				if (num3 >= 12f)
				{
					Add(turns, num3, 80);
				}
				Vector3 val = position - _lastPosition;
				if (((Vector3)(ref val)).sqrMagnitude > 0.02f && flag && Vector3.Dot(((Vector3)(ref val)).normalized, forward) < -0.1f)
				{
					Add(directionChanges, num3, 80);
				}
				if (flag)
				{
					_lastMovingAt = now;
				}
				_wasMoving = flag;
				_lastPosition = position;
				_lastForward = forward;
				_lastSampleAt = now;
			}
		}

		public float AverageSpeed()
		{
			return Average(walkingBursts);
		}

		public float AveragePause()
		{
			return Average(pauses);
		}

		public float AverageTurn()
		{
			return Average(turns);
		}

		private static void Add(List<float> list, float value, int max)
		{
			list.Add(value);
			if (list.Count > max)
			{
				list.RemoveAt(0);
			}
		}

		private static void Add(List<string> list, string value, int max)
		{
			list.Add(value);
			if (list.Count > max)
			{
				list.RemoveAt(0);
			}
		}

		private static float Average(List<float> list)
		{
			if (list == null || list.Count == 0)
			{
				return 0f;
			}
			float num = 0f;
			foreach (float item in list)
			{
				num += item;
			}
			return num / (float)list.Count;
		}
	}
	public enum MimicBehaviorMode
	{
		AdaptiveStalker,
		Jumpscare,
		RuthlessAttacker,
		NonHostile
	}
	[Serializable]
	public sealed class MimicSettings
	{
		public int version = 1;

		public MimicBehaviorMode behaviorMode;

		public bool enabled;

		public bool stalkingEnabled = true;

		public bool attacksEnabled = true;

		public bool visualGlitchesEnabled;

		public bool learningEnabled = true;

		public bool ttsEnabled;

		public bool selfAwareEnabled;

		public float ttsPitch = 0.75f;

		public float ttsFrequency = 0.4f;

		public float stalkDistance = 7f;

		public float rushChance = 0.22f;

		public float attackCooldown = 4f;

		public float jumpscareCooldown = 30f;

		public void ClampValues()
		{
			ttsPitch = Clamp(ttsPitch, 0.5f, 1.5f);
			ttsFrequency = Clamp(ttsFrequency, 0f, 1f);
			stalkDistance = Clamp(stalkDistance, 2f, 25f);
			rushChance = Clamp(rushChance, 0f, 1f);
			attackCooldown = Clamp(attackCooldown, 0.5f, 60f);
			jumpscareCooldown = Clamp(jumpscareCooldown, 1f, 300f);
		}

		private static float Clamp(float v, float min, float max)
		{
			if (!(v < min))
			{
				if (!(v > max))
				{
					return v;
				}
				return max;
			}
			return min;
		}
	}
	internal sealed class MimicTTS : IDisposable
	{
		private readonly Instance _log;

		private object _voice;

		public bool Enabled { get; private set; }

		public float Pitch { get; private set; } = 0.75f;

		public float Frequency { get; private set; } = 0.4f;

		public MimicTTS(Instance logger)
		{
			_log = logger;
		}

		public void Configure(bool enabled, float pitch, float frequency)
		{
			Enabled = enabled;
			Pitch = Clamp(pitch, 0.5f, 1.5f);
			Frequency = Clamp(frequency, 0f, 1f);
		}

		public IReadOnlyList<string> GetInstalledVoices()
		{
			List<string> list = new List<string>();
			if (!OperatingSystem.IsWindows())
			{
				_log.Warning("Windows SAPI voice enumeration is only available on Windows.");
				return list;
			}
			object obj = null;
			try
			{
				object voice = GetVoice();
				obj = voice.GetType().InvokeMember("GetVoices", BindingFlags.InvokeMethod, null, voice, new object[2] { "", "" });
				if (obj == null)
				{
					return list;
				}
				int num = Convert.ToInt32(obj.GetType().InvokeMember("Count", BindingFlags.GetProperty, null, obj, null));
				for (int i = 0; i < num; i++)
				{
					object obj2 = obj.GetType().InvokeMember("Item", BindingFlags.InvokeMethod, null, obj, new object[1] { i });
					if (obj2 != null)
					{
						string text = Convert.ToString(obj2.GetType().InvokeMember("GetDescription", BindingFlags.InvokeMethod, null, obj2, new object[1] { 0 }));
						if (!string.IsNullOrWhiteSpace(text))
						{
							list.Add(text);
						}
					}
				}
			}
			catch (Exception ex)
			{
				_log.Warning("Could not enumerate Windows SAPI voices: " + ex.Message);
			}
			finally
			{
				ReleaseComObjectIfNeeded(obj);
			}
			return list;
		}

		public bool Speak(string text)
		{
			if (!Enabled || string.IsNullOrWhiteSpace(text))
			{
				return false;
			}
			if (!OperatingSystem.IsWindows())
			{
				_log.Warning("Windows SAPI speech is only available on Windows.");
				return false;
			}
			try
			{
				object voice = GetVoice();
				int num = (int)Math.Round((Pitch - 1f) * 20f);
				string text2 = SecurityElement.Escape(text.Trim());
				string text3 = "<pitch absmiddle=\"" + num + "\">" + text2 + "</pitch>";
				voice.GetType().InvokeMember("Speak", BindingFlags.InvokeMethod, null, voice, new object[2] { text3, 8 });
				return true;
			}
			catch (Exception ex)
			{
				_log.Error("Windows speech synthesis failed: " + ex.Message);
				return false;
			}
		}

		private object GetVoice()
		{
			if (_voice != null)
			{
				return _voice;
			}
			if (!OperatingSystem.IsWindows())
			{
				throw new PlatformNotSupportedException("Windows SAPI is only available on Windows.");
			}
			Type typeFromProgID = Type.GetTypeFromProgID("SAPI.SpVoice");
			if (typeFromProgID == null)
			{
				throw new InvalidOperationException("Windows SAPI.SpVoice is unavailable.");
			}
			_voice = Activator.CreateInstance(typeFromProgID);
			if (_voice == null)
			{
				throw new InvalidOperationException("Windows SAPI.SpVoice could not be created.");
			}
			return _voice;
		}

		public void Dispose()
		{
			object voice = _voice;
			_voice = null;
			if (voice == null)
			{
				return;
			}
			try
			{
				if (OperatingSystem.IsWindows())
				{
					voice.GetType().InvokeMember("Speak", BindingFlags.InvokeMethod, null, voice, new object[2] { "", 3 });
				}
			}
			catch (Exception ex)
			{
				_log.Warning("Could not stop Windows speech during shutdown: " + ex.Message);
			}
			finally
			{
				ReleaseComObjectIfNeeded(voice);
			}
		}

		private static void ReleaseComObjectIfNeeded(object value)
		{
			if (value == null || !OperatingSystem.IsWindows())
			{
				return;
			}
			try
			{
				if (Marshal.IsComObject(value))
				{
					Marshal.ReleaseComObject(value);
				}
			}
			catch
			{
			}
		}

		private static float Clamp(float value, float min, float max)
		{
			if (float.IsNaN(value) || float.IsInfinity(value))
			{
				return min;
			}
			if (!(value < min))
			{
				if (!(value > max))
				{
					return value;
				}
				return max;
			}
			return min;
		}
	}
	public sealed class Mod : MelonMod
	{
		private MimicAI _ai;

		private MimicDataManager _data;

		private MimicTTS _tts;

		private float _nextSaveAt;

		private bool _menuCreated;

		public override void OnInitializeMelon()
		{
			_data = new MimicDataManager(((MelonBase)this).LoggerInstance);
			_data.Load();
			if (_data.Learning != null)
			{
				_data.Learning.BeginSession();
			}
			_tts = new MimicTTS(((MelonBase)this).LoggerInstance);
			ConfigureTTS();
			_ai = new MimicAI(((MelonBase)this).LoggerInstance);
			ApplySettingsToAI();
			((MelonBase)this).LoggerInstance.Msg("Mimic 0.5.0 initialized. Microphone features are not included.");
		}

		public override void OnLateInitializeMelon()
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			if (!_menuCreated && _data != null)
			{
				_menuCreated = true;
				Page root = Page.Root.CreatePage("Mimic", Color.cyan, 0, true);
				BuildMainPage(root);
				BuildBehaviorPages(root);
				BuildLearningPage(root);
				BuildVoicePage(root);
				BuildDiagnosticsPage(root);
				((MelonBase)this).LoggerInstance.Msg("Mimic BoneMenu created with separate AI, behavior, learning, voice, and diagnostics categories. No microphone controls are present.");
			}
		}

		private void BuildMainPage(Page root)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: 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_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: 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_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: 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_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: 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)
			Page obj = root.CreatePage("AI Control", Color.cyan, 0, true);
			obj.CreateBool("Mimic AI Enabled", Color.cyan, _data.Settings.enabled, (Action<bool>)delegate(bool value)
			{
				_data.Settings.enabled = value;
				ApplySettingsToAI();
				Save();
			});
			obj.CreateEnum("Behavior Mode", Color.cyan, (Enum)_data.Settings.behaviorMode, (Action<Enum>)delegate(Enum value)
			{
				_data.Settings.behaviorMode = (MimicBehaviorMode)(object)value;
				ApplySettingsToAI();
				Save();
			});
			obj.CreateFunction("Refresh Existing Doppel Bots", Color.white, (Action)delegate
			{
				_ai?.RefreshBots();
				((MelonBase)this).LoggerInstance.Msg("Requested a refresh of existing Doppel bots.");
			});
			obj.CreateFunction("Reapply Settings", Color.white, (Action)delegate
			{
				ApplySettingsToAI();
				Save();
				((MelonBase)this).LoggerInstance.Msg("Mimic settings reapplied.");
			});
			obj.CreateFunction("Save All Data Now", Color.green, (Action)Save);
			Page obj2 = obj.CreatePage("Quick Profiles", Color.cyan, 0, true);
			obj2.CreateFunction("Balanced Stalker", Color.cyan, (Action)delegate
			{
				ApplyProfile(MimicBehaviorMode.AdaptiveStalker, stalking: true, attacks: true, glitches: false, learning: true, 7f, 0.22f, 4f, 30f);
			});
			obj2.CreateFunction("Patient / Less Aggressive", Color.cyan, (Action)delegate
			{
				ApplyProfile(MimicBehaviorMode.AdaptiveStalker, stalking: true, attacks: false, glitches: false, learning: true, 10f, 0.08f, 7f, 45f);
			});
			obj2.CreateFunction("Aggressive Pursuer", Color.red, (Action)delegate
			{
				ApplyProfile(MimicBehaviorMode.RuthlessAttacker, stalking: true, attacks: true, glitches: false, learning: true, 5f, 0.55f, 2f, 20f);
			});
			obj2.CreateFunction("Jumpscare Focus", Color.magenta, (Action)delegate
			{
				ApplyProfile(MimicBehaviorMode.Jumpscare, stalking: true, attacks: false, glitches: true, learning: true, 7f, 0.15f, 4f, 18f);
			});
			obj2.CreateFunction("Non-Hostile Observer", Color.white, (Action)delegate
			{
				ApplyProfile(MimicBehaviorMode.NonHostile, stalking: false, attacks: true, glitches: false, learning: true, 9f, 0f, 4f, 30f);
			});
		}

		private void BuildBehaviorPages(Page root)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: 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)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: 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_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: 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_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b5: 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_01f4: 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)
			Page obj = root.CreatePage("Behavior Tuning", Color.cyan, 0, true);
			Page obj2 = obj.CreatePage("Stalking & Movement", Color.cyan, 0, true);
			obj2.CreateBool("Stalking / Cover Enabled", Color.cyan, _data.Settings.stalkingEnabled, (Action<bool>)delegate(bool value)
			{
				_data.Settings.stalkingEnabled = value;
				ApplySettingsToAI();
				Save();
			});
			obj2.CreateFunction("Increase Stalk Distance (+1m)", Color.cyan, (Action)delegate
			{
				ChangeFloat("stalk distance", 1f, 2f, 25f, delegate(float value)
				{
					_data.Settings.stalkDistance = value;
				}, () => _data.Settings.stalkDistance);
			});
			obj2.CreateFunction("Decrease Stalk Distance (-1m)", Color.cyan, (Action)delegate
			{
				ChangeFloat("stalk distance", -1f, 2f, 25f, delegate(float value)
				{
					_data.Settings.stalkDistance = value;
				}, () => _data.Settings.stalkDistance);
			});
			obj2.CreateFunction("Increase Rush Chance (+5%)", Color.cyan, (Action)delegate
			{
				ChangeFloat("rush chance", 0.05f, 0f, 1f, delegate(float value)
				{
					_data.Settings.rushChance = value;
				}, () => _data.Settings.rushChance, percent: true);
			});
			obj2.CreateFunction("Decrease Rush Chance (-5%)", Color.cyan, (Action)delegate
			{
				ChangeFloat("rush chance", -0.05f, 0f, 1f, delegate(float value)
				{
					_data.Settings.rushChance = value;
				}, () => _data.Settings.rushChance, percent: true);
			});
			obj2.CreateFunction("Show Movement Tuning", Color.white, (Action)delegate
			{
				((MelonBase)this).LoggerInstance.Msg("Stalk distance=" + _data.Settings.stalkDistance.ToString("F1") + "m; rush chance=" + (_data.Settings.rushChance * 100f).ToString("F0") + "%.");
			});
			Page obj3 = obj.CreatePage("Combat & Cooldowns", Color.red, 0, true);
			obj3.CreateBool("Close-Range Attacks Enabled", Color.red, _data.Settings.attacksEnabled, (Action<bool>)delegate(bool value)
			{
				_data.Settings.attacksEnabled = value;
				ApplySettingsToAI();
				Save();
			});
			obj3.CreateFunction("Faster Attacks (-0.5s cooldown)", Color.red, (Action)delegate
			{
				ChangeFloat("attack cooldown", -0.5f, 0.5f, 60f, delegate(float value)
				{
					_data.Settings.attackCooldown = value;
				}, () => _data.Settings.attackCooldown);
			});
			obj3.CreateFunction("Slower Attacks (+0.5s cooldown)", Color.red, (Action)delegate
			{
				ChangeFloat("attack cooldown", 0.5f, 0.5f, 60f, delegate(float value)
				{
					_data.Settings.attackCooldown = value;
				}, () => _data.Settings.attackCooldown);
			});
			obj3.CreateFunction("Shorter Jumpscare Cooldown (-5s)", Color.magenta, (Action)delegate
			{
				ChangeFloat("jumpscare cooldown", -5f, 1f, 300f, delegate(float value)
				{
					_data.Settings.jumpscareCooldown = value;
				}, () => _data.Settings.jumpscareCooldown);
			});
			obj3.CreateFunction("Longer Jumpscare Cooldown (+5s)", Color.magenta, (Action)delegate
			{
				ChangeFloat("jumpscare cooldown", 5f, 1f, 300f, delegate(float value)
				{
					_data.Settings.jumpscareCooldown = value;
				}, () => _data.Settings.jumpscareCooldown);
			});
			obj3.CreateFunction("Show Combat Tuning", Color.white, (Action)delegate
			{
				((MelonBase)this).LoggerInstance.Msg("Attacks=" + _data.Settings.attacksEnabled + "; attack cooldown=" + _data.Settings.attackCooldown.ToString("F1") + "s; jumpscare cooldown=" + _data.Settings.jumpscareCooldown.ToString("F1") + "s.");
			});
			Page obj4 = obj.CreatePage("Experimental", Color.magenta, 0, true);
			obj4.CreateBool("Self-Aware Mode", Color.magenta, _data.Settings.selfAwareEnabled, (Action<bool>)delegate(bool value)
			{
				_data.Settings.selfAwareEnabled = value;
				ApplySettingsToAI();
				Save();
			});
			obj4.CreateBool("Visual Glitches", Color.magenta, _data.Settings.visualGlitchesEnabled, (Action<bool>)delegate(bool value)
			{
				_data.Settings.visualGlitchesEnabled = value;
				ApplySettingsToAI();
				Save();
			});
			obj4.CreateFunction("Reset Experimental Toggles", Color.white, (Action)delegate
			{
				_data.Settings.selfAwareEnabled = false;
				_data.Settings.visualGlitchesEnabled = false;
				ApplySettingsToAI();
				Save();
				((MelonBase)this).LoggerInstance.Msg("Self-aware mode and visual glitches disabled.");
			});
		}

		private void BuildLearningPage(Page root)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: 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_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			Page obj = root.CreatePage("Learning & Memory", Color.cyan, 0, true);
			obj.CreateBool("Player Learning Enabled", Color.cyan, _data.Settings.learningEnabled, (Action<bool>)delegate(bool value)
			{
				_data.Settings.learningEnabled = value;
				ApplySettingsToAI();
				Save();
			});
			obj.CreateFunction("Show Learning Statistics", Color.white, (Action)PrintLearningStats);
			obj.CreateFunction("Save Learning & Settings", Color.green, (Action)Save);
			obj.CreateFunction("Forget Learned Movement History", Color.red, (Action)delegate
			{
				_data.Learning.movementGaps.Clear();
				_data.Learning.walkingBursts.Clear();
				_data.Learning.pauses.Clear();
				_data.Learning.turns.Clear();
				_data.Learning.directionChanges.Clear();
				_data.Learning.observations.Clear();
				_data.Learning.sessions = 0;
				Save();
				((MelonBase)this).LoggerInstance.Msg("Mimic movement history cleared. Encounter history was preserved.");
			});
			obj.CreateFunction("Forget Encounter History", Color.red, (Action)delegate
			{
				_data.Encounters.history.Clear();
				_data.Encounters.totalEncounters = 0;
				_data.Encounters.totalRushes = 0;
				_data.Encounters.totalAttacks = 0;
				_data.Encounters.totalJumpscares = 0;
				_data.Encounters.lastEncounterUtc = string.Empty;
				Save();
				((MelonBase)this).LoggerInstance.Msg("Mimic encounter history cleared. Movement learning was preserved.");
			});
		}

		private void BuildVoicePage(Page root)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: 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_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: 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_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: 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)
			Page obj = root.CreatePage("Voice & TTS", Color.cyan, 0, true);
			obj.CreateBool("Windows TTS Enabled", Color.cyan, _data.Settings.ttsEnabled, (Action<bool>)delegate(bool value)
			{
				_data.Settings.ttsEnabled = value;
				ConfigureTTS();
				Save();
			});
			obj.CreateFunction("List Installed Windows Speech Voices", Color.white, (Action)ListTTSVoices);
			obj.CreateFunction("Lower Voice Pitch", Color.magenta, (Action)delegate
			{
				ChangeFloat("TTS pitch", -0.05f, 0.5f, 1.5f, delegate(float value)
				{
					_data.Settings.ttsPitch = value;
				}, () => _data.Settings.ttsPitch);
			});
			obj.CreateFunction("Raise Voice Pitch", Color.magenta, (Action)delegate
			{
				ChangeFloat("TTS pitch", 0.05f, 0.5f, 1.5f, delegate(float value)
				{
					_data.Settings.ttsPitch = value;
				}, () => _data.Settings.ttsPitch);
			});
			obj.CreateFunction("Lower Voice Frequency", Color.magenta, (Action)delegate
			{
				ChangeFloat("TTS frequency", -0.05f, 0f, 1f, delegate(float value)
				{
					_data.Settings.ttsFrequency = value;
				}, () => _data.Settings.ttsFrequency);
			});
			obj.CreateFunction("Raise Voice Frequency", Color.magenta, (Action)delegate
			{
				ChangeFloat("TTS frequency", 0.05f, 0f, 1f, delegate(float value)
				{
					_data.Settings.ttsFrequency = value;
				}, () => _data.Settings.ttsFrequency);
			});
			obj.CreateFunction("Test Windows TTS", Color.green, (Action)delegate
			{
				if (_tts == null || !_data.Settings.ttsEnabled || !_tts.Speak("Mimic voice test."))
				{
					((MelonBase)this).LoggerInstance.Warning("TTS test failed. Enable TTS and check Windows SAPI and the default audio output.");
				}
			});
			obj.CreateFunction("TTS Routing Note", Color.white, (Action)delegate
			{
				((MelonBase)this).LoggerInstance.Msg("This version uses Windows default audio output; speech is not yet spatially positioned at the bot.");
			});
		}

		private void BuildDiagnosticsPage(Page root)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: 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_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: 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)
			Page obj = root.CreatePage("Diagnostics & Tools", Color.white, 0, true);
			obj.CreateFunction("Refresh Existing Doppel Bots", Color.white, (Action)delegate
			{
				_ai?.RefreshBots();
			});
			obj.CreateFunction("Show Encounter Counts", Color.white, (Action)delegate
			{
				((MelonBase)this).LoggerInstance.Msg("Encounters=" + _data.Encounters.totalEncounters + ", rushes=" + _data.Encounters.totalRushes + ", attacks=" + _data.Encounters.totalAttacks + ", jumpscares=" + _data.Encounters.totalJumpscares);
			});
			obj.CreateFunction("Print Data Directory", Color.white, (Action)delegate
			{
				((MelonBase)this).LoggerInstance.Msg("Mimic data path: " + _data.DirectoryPath);
			});
			obj.CreateFunction("Print Current Settings", Color.white, (Action)PrintSettings);
			obj.CreateFunction("Save All JSON Files", Color.green, (Action)Save);
			obj.CreateFunction("Reapply Settings to AI", Color.cyan, (Action)ApplySettingsToAI);
		}

		private void ApplyProfile(MimicBehaviorMode mode, bool stalking, bool attacks, bool glitches, bool learning, float stalkDistance, float rushChance, float attackCooldown, float jumpscareCooldown)
		{
			MimicSettings settings = _data.Settings;
			settings.behaviorMode = mode;
			settings.enabled = true;
			settings.stalkingEnabled = stalking;
			settings.attacksEnabled = attacks;
			settings.visualGlitchesEnabled = glitches;
			settings.learningEnabled = learning;
			settings.stalkDistance = stalkDistance;
			settings.rushChance = rushChance;
			settings.attackCooldown = attackCooldown;
			settings.jumpscareCooldown = jumpscareCooldown;
			ApplySettingsToAI();
			Save();
			((MelonBase)this).LoggerInstance.Msg("Applied Mimic profile: " + mode.ToString() + ". Review the Behavior Tuning pages to customize it.");
		}

		private void ChangeFloat(string label, float delta, float min, float max, Action<float> setter, Func<float> getter, bool percent = false)
		{
			if (_data != null)
			{
				float num = Mathf.Clamp(getter() + delta, min, max);
				setter(num);
				_data.Settings.ClampValues();
				ApplySettingsToAI();
				Save();
				((MelonBase)this).LoggerInstance.Msg("Mimic " + label + " set to " + (percent ? ((num * 100f).ToString("F0") + "%") : num.ToString("F2")) + ".");
			}
		}

		private void PrintLearningStats()
		{
			MimicLearning learning = _data.Learning;
			((MelonBase)this).LoggerInstance.Msg("Mimic learning: sessions=" + learning.sessions + ", average movement speed=" + learning.AverageSpeed().ToString("F2") + ", average pause=" + learning.AveragePause().ToString("F2") + "s, average turn=" + learning.AverageTurn().ToString("F1") + " degrees, movement samples=" + learning.walkingBursts.Count + ", observations=" + learning.observations.Count + ". Data: " + _data.DirectoryPath);
		}

		private void PrintSettings()
		{
			MimicSettings settings = _data.Settings;
			((MelonBase)this).LoggerInstance.Msg("Mimic settings: enabled=" + settings.enabled + ", mode=" + settings.behaviorMode.ToString() + ", stalking=" + settings.stalkingEnabled + ", attacks=" + settings.attacksEnabled + ", selfAware=" + settings.selfAwareEnabled + ", visualGlitches=" + settings.visualGlitchesEnabled + ", learning=" + settings.learningEnabled + ", stalkDistance=" + settings.stalkDistance.ToString("F1") + ", rushChance=" + (settings.rushChance * 100f).ToString("F0") + "%, attackCooldown=" + settings.attackCooldown.ToString("F1") + "s, jumpscareCooldown=" + settings.jumpscareCooldown.ToString("F1") + "s, TTS=" + settings.ttsEnabled + ".");
		}

		private void ApplySettingsToAI()
		{
			if (_ai != null && _data != null)
			{
				MimicSettings settings = _data.Settings;
				settings.ClampValues();
				_ai.Configure(settings, _data.Learning, _data.Encounters);
				bool attacksEnabled = settings.attacksEnabled && settings.behaviorMode != MimicBehaviorMode.NonHostile && settings.behaviorMode != MimicBehaviorMode.Jumpscare;
				bool coverEnabled = settings.stalkingEnabled && settings.behaviorMode != MimicBehaviorMode.RuthlessAttacker;
				_ai.SetEnabled(settings.enabled);
				_ai.SetCoverEnabled(coverEnabled);
				_ai.SetAttacksEnabled(attacksEnabled);
				_ai.SetSelfAware(settings.selfAwareEnabled);
				_ai.SetVisualGlitches(settings.visualGlitchesEnabled);
			}
		}

		private void ConfigureTTS()
		{
			if (_tts != null && _data != null)
			{
				_tts.Configure(_data.Settings.ttsEnabled, _data.Settings.ttsPitch, _data.Settings.ttsFrequency);
			}
		}

		private void ListTTSVoices()
		{
			if (_tts == null)
			{
				return;
			}
			IReadOnlyList<string> installedVoices = _tts.GetInstalledVoices();
			if (installedVoices.Count == 0)
			{
				((MelonBase)this).LoggerInstance.Warning("No installed Windows SAPI voices were returned, or SAPI enumeration failed.");
				return;
			}
			((MelonBase)this).LoggerInstance.Msg("===== INSTALLED WINDOWS SPEECH VOICES =====");
			for (int i = 0; i < installedVoices.Count; i++)
			{
				((MelonBase)this).LoggerInstance.Msg(i + ": " + installedVoices[i]);
			}
			((MelonBase)this).LoggerInstance.Msg("===========================================");
		}

		private void Save()
		{
			if (_data != null)
			{
				_data.Settings.ClampValues();
				_data.SaveAll();
			}
		}

		public override void OnSceneWasLoaded(int buildIndex, string sceneName)
		{
			_ai?.NotifySceneLoaded(sceneName);
		}

		public override void OnUpdate()
		{
			_ai?.Update();
			if (_data != null && Time.unscaledTime >= _nextSaveAt)
			{
				_nextSaveAt = Time.unscaledTime + 30f;
				Save();
			}
		}

		public override void OnDeinitializeMelon()
		{
			Save();
			_ai?.Dispose();
			_tts?.Dispose();
			_ai = null;
			_tts = null;
			_data = null;
			((MelonBase)this).LoggerInstance.Msg("Mimic Mod shut down.");
		}
	}
}