Decompiled source of PaperPals v1.0.0

plugins/PaperPals.dll

Decompiled 2 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using LethalLib.Modules;
using Microsoft.CodeAnalysis;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.Rendering;
using UnityEngine.Rendering.HighDefinition;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("PaperPals")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+5cba7b5268d5f8e5ee36d0074b0fe356dd32378b")]
[assembly: AssemblyProduct("PaperPals")]
[assembly: AssemblyTitle("PaperPals")]
[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 PaperPals
{
	internal static class Net
	{
		private sealed class Pending
		{
			public int Count;

			public int Got;

			public int Rate;

			public float Yaw;

			public float Volume;

			public float Started;

			public byte[] Pcm;
		}

		[CompilerGenerated]
		private static class <>O
		{
			public static HandleNamedMessageDelegate <0>__OnVoice;

			public static HandleNamedMessageDelegate <1>__OnTeleport;
		}

		private const string VoiceMsg = "PaperPals.Voice";

		private const string TeleportMsg = "PaperPals.Teleport";

		private const int ChunkSize = 16384;

		private const int MaxPcm = 2000000;

		private static CustomMessagingManager registeredOn;

		private static int nextClipId;

		private static readonly Dictionary<(ulong, int), Pending> pending = new Dictionary<(ulong, int), Pending>();

		public static void EnsureRegistered()
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected O, but got Unknown
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Expected O, but got Unknown
			NetworkManager singleton = NetworkManager.Singleton;
			CustomMessagingManager val = ((singleton != null) ? singleton.CustomMessagingManager : null);
			if (val != null && val != registeredOn)
			{
				object obj = <>O.<0>__OnVoice;
				if (obj == null)
				{
					HandleNamedMessageDelegate val2 = OnVoice;
					<>O.<0>__OnVoice = val2;
					obj = (object)val2;
				}
				val.RegisterNamedMessageHandler("PaperPals.Voice", (HandleNamedMessageDelegate)obj);
				object obj2 = <>O.<1>__OnTeleport;
				if (obj2 == null)
				{
					HandleNamedMessageDelegate val3 = OnTeleport;
					<>O.<1>__OnTeleport = val3;
					obj2 = (object)val3;
				}
				val.RegisterNamedMessageHandler("PaperPals.Teleport", (HandleNamedMessageDelegate)obj2);
				registeredOn = val;
				pending.Clear();
			}
		}

		public static IEnumerator SendVoice(ulong npcId, float yaw, float volume, VoiceClip clip)
		{
			NetworkManager singleton = NetworkManager.Singleton;
			if ((Object)(object)singleton == (Object)null || !singleton.IsServer)
			{
				yield break;
			}
			EnsureRegistered();
			int clipId = ++nextClipId;
			int total = clip.Pcm.Length;
			int count = (total + 16384 - 1) / 16384;
			FastBufferWriter val = default(FastBufferWriter);
			for (int i = 0; i < count; i++)
			{
				singleton = NetworkManager.Singleton;
				if ((Object)(object)singleton == (Object)null || !singleton.IsServer || singleton.CustomMessagingManager == null)
				{
					break;
				}
				int num = i * 16384;
				int num2 = Math.Min(16384, total - num);
				((FastBufferWriter)(ref val))..ctor(num2 + 64, (Allocator)2, -1);
				try
				{
					((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref npcId, default(ForPrimitives));
					((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref clipId, default(ForPrimitives));
					((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref i, default(ForPrimitives));
					((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref count, default(ForPrimitives));
					((FastBufferWriter)(ref val)).WriteValueSafe<float>(ref yaw, default(ForPrimitives));
					((FastBufferWriter)(ref val)).WriteValueSafe<float>(ref volume, default(ForPrimitives));
					((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref clip.Rate, default(ForPrimitives));
					((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref total, default(ForPrimitives));
					((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref num2, default(ForPrimitives));
					((FastBufferWriter)(ref val)).WriteBytesSafe(clip.Pcm, num2, num);
					singleton.CustomMessagingManager.SendNamedMessageToAll("PaperPals.Voice", val, (NetworkDelivery)4);
				}
				finally
				{
					((FastBufferWriter)(ref val)).Dispose();
				}
				if (i + 1 < count)
				{
					yield return (object)new WaitForSecondsRealtime(0.05f);
				}
			}
		}

		private static void OnVoice(ulong sender, FastBufferReader r)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: 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_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: 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_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				NetworkManager singleton = NetworkManager.Singleton;
				if ((Object)(object)singleton == (Object)null || sender != 0L)
				{
					return;
				}
				ulong num = default(ulong);
				((FastBufferReader)(ref r)).ReadValueSafe<ulong>(ref num, default(ForPrimitives));
				int item = default(int);
				((FastBufferReader)(ref r)).ReadValueSafe<int>(ref item, default(ForPrimitives));
				int num2 = default(int);
				((FastBufferReader)(ref r)).ReadValueSafe<int>(ref num2, default(ForPrimitives));
				int num3 = default(int);
				((FastBufferReader)(ref r)).ReadValueSafe<int>(ref num3, default(ForPrimitives));
				float yaw = default(float);
				((FastBufferReader)(ref r)).ReadValueSafe<float>(ref yaw, default(ForPrimitives));
				float volume = default(float);
				((FastBufferReader)(ref r)).ReadValueSafe<float>(ref volume, default(ForPrimitives));
				int num4 = default(int);
				((FastBufferReader)(ref r)).ReadValueSafe<int>(ref num4, default(ForPrimitives));
				int num5 = default(int);
				((FastBufferReader)(ref r)).ReadValueSafe<int>(ref num5, default(ForPrimitives));
				int num6 = default(int);
				((FastBufferReader)(ref r)).ReadValueSafe<int>(ref num6, default(ForPrimitives));
				if (num5 <= 0 || num5 > 2000000 || num4 < 4000 || num4 > 96000 || num3 <= 0 || num2 < 0 || num2 >= num3 || num6 < 0 || num2 * 16384 + num6 > num5)
				{
					return;
				}
				(ulong, int) key = (num, item);
				if (!pending.TryGetValue(key, out var value))
				{
					if (pending.Count > 32)
					{
						DropStale();
					}
					value = new Pending
					{
						Count = num3,
						Rate = num4,
						Yaw = yaw,
						Volume = volume,
						Pcm = new byte[num5],
						Started = Time.realtimeSinceStartup
					};
					pending[key] = value;
				}
				byte[] src = new byte[num6];
				((FastBufferReader)(ref r)).ReadBytesSafe(ref src, num6, 0);
				Buffer.BlockCopy(src, 0, value.Pcm, num2 * 16384, num6);
				if (++value.Got >= value.Count)
				{
					pending.Remove(key);
					PaperPalAI paperPalAI = default(PaperPalAI);
					if (singleton.SpawnManager.SpawnedObjects.TryGetValue(num, out var value2) && (Object)(object)value2 != (Object)null && ((Component)value2).TryGetComponent<PaperPalAI>(ref paperPalAI))
					{
						paperPalAI.PlayVoice(value.Yaw, value.Volume, value.Rate, value.Pcm);
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Voice message: " + ex.Message));
			}
		}

		private static void DropStale()
		{
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			foreach (var item in new List<(ulong, int)>(pending.Keys))
			{
				if (realtimeSinceStartup - pending[item].Started > 15f)
				{
					pending.Remove(item);
				}
			}
		}

		public static void SendTeleport(ulong npcId, Vector3 pos, bool outside)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			NetworkManager singleton = NetworkManager.Singleton;
			if ((Object)(object)singleton == (Object)null || !singleton.IsServer)
			{
				return;
			}
			EnsureRegistered();
			FastBufferWriter val = default(FastBufferWriter);
			((FastBufferWriter)(ref val))..ctor(64, (Allocator)2, -1);
			try
			{
				((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref npcId, default(ForPrimitives));
				((FastBufferWriter)(ref val)).WriteValueSafe(ref pos);
				((FastBufferWriter)(ref val)).WriteValueSafe<bool>(ref outside, default(ForPrimitives));
				singleton.CustomMessagingManager.SendNamedMessageToAll("PaperPals.Teleport", val, (NetworkDelivery)3);
			}
			finally
			{
				((FastBufferWriter)(ref val)).Dispose();
			}
		}

		private static void OnTeleport(ulong sender, FastBufferReader r)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				NetworkManager singleton = NetworkManager.Singleton;
				if (!((Object)(object)singleton == (Object)null) && sender == 0L)
				{
					ulong key = default(ulong);
					((FastBufferReader)(ref r)).ReadValueSafe<ulong>(ref key, default(ForPrimitives));
					Vector3 pos = default(Vector3);
					((FastBufferReader)(ref r)).ReadValueSafe(ref pos);
					bool outside = default(bool);
					((FastBufferReader)(ref r)).ReadValueSafe<bool>(ref outside, default(ForPrimitives));
					PaperPalAI paperPalAI = default(PaperPalAI);
					if (singleton.SpawnManager.SpawnedObjects.TryGetValue(key, out var value) && (Object)(object)value != (Object)null && ((Component)value).TryGetComponent<PaperPalAI>(ref paperPalAI))
					{
						paperPalAI.ApplyTeleport(pos, outside);
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Teleport message: " + ex.Message));
			}
		}
	}
	[Serializable]
	public class NpcJson
	{
		public int format = 1;

		public string id = "";

		public string displayName = "";

		public string scanSubtext = "";

		public VoiceJson voice = new VoiceJson();

		public SpriteJson sprite = new SpriteJson();

		public PosesJson poses = new PosesJson();

		public FearJson fear = new FearJson();

		public StatsJson stats = new StatsJson();

		public FeaturesJson features = new FeaturesJson();
	}
	[Serializable]
	public class VoiceJson
	{
		public string targetPlayer = "";

		public string categories = "frase,afirmacion,risa,llamada,seguir,aviso,nave,botin,ayuda,nombre,respuesta";

		public string panicCategories = "grito,ayuda,aviso";

		public string fearCategories = "ayuda,aviso,grito";

		public string hurtCategories = "grito,ayuda";
	}
	[Serializable]
	public class SpriteJson
	{
		public float eyeLevel = 0.912f;

		public float heightCm = 180f;
	}
	[Serializable]
	public class PosesJson
	{
		public float crouchChance;
	}
	[Serializable]
	public class FearJson
	{
		public bool enabled = true;

		public float radius = 10f;

		public float calmDelay = 3f;

		public float runDistance = 4f;
	}
	[Serializable]
	public class StatsJson
	{
		public int hp = 6;

		public float walkSpeed = 2.6f;

		public int rarity = 25;
	}
	[Serializable]
	public class FeaturesJson
	{
		public bool meltdownPanic = true;

		public bool deathScream = true;
	}
	internal sealed class NpcDefinition
	{
		public NpcJson J;

		public string JsonPath;

		public string Dir;

		public SpriteSet Sprites;

		public VoiceLibrary Voice;

		public EnemyType Type;

		public int Index;

		public ConfigEntry<bool> Enabled;

		public ConfigEntry<string> TargetPlayerCfg;

		public ConfigEntry<string> ScanNameCfg;

		public ConfigEntry<string> FearEnabledCfg;

		public ConfigEntry<int> RarityCfg;

		public ConfigEntry<int> HpCfg;

		public ConfigEntry<float> WalkSpeedCfg;

		public ConfigEntry<float> HeightCmCfg;

		public ConfigEntry<float> FearRadiusCfg;

		public ConfigEntry<float> FearRunCfg;

		public string Id => J.id;

		public string DisplayName
		{
			get
			{
				if (string.IsNullOrWhiteSpace(ScanNameCfg?.Value))
				{
					if (string.IsNullOrWhiteSpace(J.displayName))
					{
						return J.id;
					}
					return J.displayName;
				}
				return ScanNameCfg.Value.Trim();
			}
		}

		public string TargetPlayer
		{
			get
			{
				if (string.IsNullOrWhiteSpace(TargetPlayerCfg?.Value))
				{
					return (J.voice.targetPlayer ?? "").Trim();
				}
				return TargetPlayerCfg.Value.Trim();
			}
		}

		public int Rarity
		{
			get
			{
				if (RarityCfg == null || RarityCfg.Value < 0)
				{
					return Math.Max(0, J.stats.rarity);
				}
				return RarityCfg.Value;
			}
		}

		public int Hp
		{
			get
			{
				if (HpCfg == null || HpCfg.Value <= 0)
				{
					return Math.Max(1, J.stats.hp);
				}
				return HpCfg.Value;
			}
		}

		public float WalkSpeed
		{
			get
			{
				if (WalkSpeedCfg == null || !(WalkSpeedCfg.Value > 0f))
				{
					return Mathf.Max(0.5f, J.stats.walkSpeed);
				}
				return WalkSpeedCfg.Value;
			}
		}

		public float HeightCm => ClampHeight((HeightCmCfg != null && HeightCmCfg.Value >= 0f) ? HeightCmCfg.Value : J.sprite.heightCm, warn: false);

		public float HeightScale => HeightCm / 180f;

		public float EyeLevel
		{
			get
			{
				if (!(J.sprite.eyeLevel > 0.05f) || !(J.sprite.eyeLevel <= 1f))
				{
					return 0.912f;
				}
				return J.sprite.eyeLevel;
			}
		}

		public bool FearEnabled
		{
			get
			{
				if (FearEnabledCfg == null || !bool.TryParse(FearEnabledCfg.Value?.Trim(), out var result))
				{
					return J.fear.enabled;
				}
				return result;
			}
		}

		public float FearRadius
		{
			get
			{
				if (FearRadiusCfg == null || !(FearRadiusCfg.Value >= 0f))
				{
					return Mathf.Max(0f, J.fear.radius);
				}
				return FearRadiusCfg.Value;
			}
		}

		public float FearRunDistance
		{
			get
			{
				if (FearRunCfg == null || !(FearRunCfg.Value >= 0f))
				{
					return Mathf.Max(0f, J.fear.runDistance);
				}
				return FearRunCfg.Value;
			}
		}

		public float CalmDelay => Mathf.Max(0.5f, J.fear.calmDelay);

		public float CrouchChance => Mathf.Clamp01(J.poses.crouchChance);

		public static float ClampHeight(float cm, bool warn, string who = null)
		{
			if (float.IsNaN(cm) || float.IsInfinity(cm))
			{
				cm = 180f;
			}
			float num = Mathf.Clamp(cm, 1f, 200f);
			if (warn && !Mathf.Approximately(num, cm))
			{
				Plugin.Log.LogWarning((object)$"{who}: heightCm {cm} clamped to {num} (allowed 1-200).");
			}
			return num;
		}

		public static NpcDefinition Load(string path)
		{
			string text;
			try
			{
				text = File.ReadAllText(path);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Cannot read " + path + ": " + ex.Message));
				return null;
			}
			NpcJson npcJson = new NpcJson();
			try
			{
				JsonUtility.FromJsonOverwrite(text, (object)npcJson);
			}
			catch (Exception ex2)
			{
				Plugin.Log.LogError((object)("Skipping " + path + ": invalid JSON (" + ex2.Message + ")."));
				return null;
			}
			if (npcJson.format != 1)
			{
				Plugin.Log.LogError((object)$"Skipping {path}: unknown npc.json format {npcJson.format} (this PaperPals reads format 1). Update PaperPals.");
				return null;
			}
			if (string.IsNullOrEmpty(npcJson.id) || !Regex.IsMatch(npcJson.id, "^[a-z0-9_]+$"))
			{
				Plugin.Log.LogError((object)("Skipping " + path + ": \"id\" must be lowercase letters, digits or _ (got \"" + npcJson.id + "\")."));
				return null;
			}
			NpcJson npcJson2 = npcJson;
			if (npcJson2.voice == null)
			{
				npcJson2.voice = new VoiceJson();
			}
			npcJson2 = npcJson;
			if (npcJson2.sprite == null)
			{
				npcJson2.sprite = new SpriteJson();
			}
			npcJson2 = npcJson;
			if (npcJson2.poses == null)
			{
				npcJson2.poses = new PosesJson();
			}
			npcJson2 = npcJson;
			if (npcJson2.fear == null)
			{
				npcJson2.fear = new FearJson();
			}
			npcJson2 = npcJson;
			if (npcJson2.stats == null)
			{
				npcJson2.stats = new StatsJson();
			}
			npcJson2 = npcJson;
			if (npcJson2.features == null)
			{
				npcJson2.features = new FeaturesJson();
			}
			if (!Regex.IsMatch(text, "\"heightCm\"\\s*:\\s*-?[0-9]"))
			{
				Plugin.Log.LogWarning((object)(npcJson.id + ": sprite.heightCm missing or not a number, using 180."));
				npcJson.sprite.heightCm = 180f;
			}
			npcJson.sprite.heightCm = ClampHeight(npcJson.sprite.heightCm, warn: true, npcJson.id);
			if (npcJson.sprite.eyeLevel <= 0.05f || npcJson.sprite.eyeLevel > 1f)
			{
				Plugin.Log.LogWarning((object)$"{npcJson.id}: sprite.eyeLevel {npcJson.sprite.eyeLevel} is not between 0.05 and 1, using 0.912.");
				npcJson.sprite.eyeLevel = 0.912f;
			}
			return new NpcDefinition
			{
				J = npcJson,
				JsonPath = path,
				Dir = Path.GetDirectoryName(path)
			};
		}

		public void BindConfig(ConfigFile cfg)
		{
			string id = J.id;
			Enabled = cfg.Bind<bool>(id, "Enabled", true, "Spawn " + J.displayName + " at all. Needs a restart.");
			TargetPlayerCfg = cfg.Bind<string>(id, "TargetPlayer", "", "Whose MirageVoces clips he speaks (Steam name, SteamID64 or 'yo' for the host). Empty = use npc.json (\"" + J.voice.targetPlayer + "\"). Host only.");
			ScanNameCfg = cfg.Bind<string>(id, "ScanName", "", "Scanner name. Empty = use npc.json (\"" + J.displayName + "\").");
			RarityCfg = cfg.Bind<int>(id, "Rarity", -1, $"Indoor spawn weight. -1 = use npc.json ({J.stats.rarity}). Needs a restart.");
			HpCfg = cfg.Bind<int>(id, "HP", -1, $"Hits he survives. -1 = use npc.json ({J.stats.hp}).");
			WalkSpeedCfg = cfg.Bind<float>(id, "WalkSpeed", -1f, $"Walking speed (m/s). -1 = use npc.json ({J.stats.walkSpeed}).");
			HeightCmCfg = cfg.Bind<float>(id, "HeightCm", -1f, $"How tall he looks, 1-200 (180 = eyes level with a player). -1 = use npc.json ({J.sprite.heightCm}). The picture updates live; hitbox and scan node after a restart. Every player should use the same value.");
			FearEnabledCfg = cfg.Bind<string>(id, "FearEnabled", "", "true/false. Empty = use npc.json (" + J.fear.enabled.ToString().ToLowerInvariant() + "). Host only.");
			FearRadiusCfg = cfg.Bind<float>(id, "FearRadius", -1f, $"Metres at which a visible monster scares him. -1 = use npc.json ({J.fear.radius}). Host only.");
			FearRunCfg = cfg.Bind<float>(id, "FearRunDistance", -1f, $"Closer than this he runs. -1 = use npc.json ({J.fear.runDistance}). Host only.");
		}
	}
	public class PaperPalAI : EnemyAI
	{
		public int DefIndex;

		internal const int Normal = 0;

		internal const int Panic = 1;

		internal const int FearSneak = 2;

		internal const int FearHold = 3;

		internal const int FearRun = 4;

		internal const int IdleCrouch = 5;

		internal const int InShip = 6;

		private Transform board;

		private MeshRenderer rend;

		private AudioSource voice;

		private AudioSource[] allSources;

		private Vector3 lastPos;

		private Vector3 facing = Vector3.forward;

		private float speed;

		private float stepTimer;

		private float mouthCloseAt;

		private float talkFaceUntil;

		private float talkYaw;

		private float sourcesRefreshAt;

		private float hitCrouchUntil;

		private int step;

		private bool walking;

		private bool mouthOpen;

		private Material shown;

		private readonly float[] rmsBuf = new float[256];

		private float nextTalkAt;

		private float busyUntil;

		private float pauseUntil;

		private float wanderDeadline;

		private float lastMovedAt;

		private float nextAloneAt;

		private float cooldownUntil;

		private float hitPauseUntil;

		private bool wandering;

		internal static bool PanicActive;

		private bool panicking;

		private bool arrivedShip;

		private bool shipTargetSet;

		private float panicMoveAt;

		private float nextShoutAt;

		private float doorStartedAt;

		private float nextHurtShoutAt;

		private float shipStartedAt;

		private float shipLastMovedAt;

		private Vector3 shipTarget;

		private Vector3 insideDoor;

		private Vector3 outsideDoor;

		private int shipNudges;

		private bool inFear;

		private bool hasSpot;

		private bool fearRunning;

		private float lastThreatAt;

		private float spotPickedAt;

		private Vector3 threatPos;

		private Vector3 spot;

		private float graceUntil;

		private bool everOutside;

		internal bool SelfKilling;

		private float lastStateAt = -10f;

		private float nextDebugAt;

		private bool riding;

		private Vector3 rideOffset;

		private int hiddenReports;

		private static float playerEyeHeight;

		private static float loggedEye;

		private const float DefaultPlayerEye = 2.35f;

		internal NpcDefinition Def
		{
			get
			{
				if (DefIndex < 0 || DefIndex >= Plugin.Defs.Count)
				{
					return null;
				}
				return Plugin.Defs[DefIndex];
			}
		}

		internal static EnemyBehaviourState[] MakeStates()
		{
			return ((IEnumerable<string>)new string[7] { "Normal", "Panic", "FearSneak", "FearHold", "FearRun", "IdleCrouch", "InShip" }).Select((Func<string, EnemyBehaviourState>)((string n) => new EnemyBehaviourState
			{
				name = n
			})).ToArray();
		}

		public override void Awake()
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Expected O, but got Unknown
			((EnemyAI)this).Awake();
			if (base.currentSearch == null)
			{
				base.currentSearch = new AISearchRoutine();
			}
		}

		public override void Start()
		{
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: 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_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			if (base.enemyBehaviourStates == null || base.enemyBehaviourStates.Length < 7)
			{
				base.enemyBehaviourStates = MakeStates();
			}
			((EnemyAI)this).Start();
			NpcDefinition def = Def;
			board = ((Component)this).transform.Find("Billboard");
			rend = (((Object)(object)board != (Object)null) ? ((Component)board).GetComponent<MeshRenderer>() : null);
			Transform obj = ((Component)this).transform.Find("Voice");
			voice = ((obj != null) ? ((Component)obj).GetComponent<AudioSource>() : null);
			if ((Object)(object)voice != (Object)null)
			{
				voice.maxDistance = Plugin.HearingDistance.Value;
			}
			allSources = ((Component)this).GetComponentsInChildren<AudioSource>(true);
			lastPos = ((Component)this).transform.position;
			facing = Flat(((Component)this).transform.forward, Vector3.forward);
			Net.EnsureRegistered();
			if (def != null)
			{
				if ((Object)(object)base.agent != (Object)null)
				{
					base.agent.speed = def.WalkSpeed;
				}
				base.enemyHP = def.Hp;
			}
			graceUntil = Time.time + Mathf.Max(0f, Plugin.GraceSeconds.Value);
			if (((NetworkBehaviour)this).IsServer)
			{
				nextTalkAt = Time.time + Random.Range(6f, 12f);
				pauseUntil = Time.time + Random.Range(0.5f, 2f);
			}
			if (Plugin.DebugLog.Value)
			{
				Plugin.Log.LogInfo((object)$"[Debug] {def?.Id} start: pos={((Component)this).transform.position} server={((NetworkBehaviour)this).IsServer} onNavMesh={(Object)(object)base.agent != (Object)null && base.agent.isOnNavMesh} outside={base.isOutside}");
			}
		}

		private void SetState(int s, bool force = false)
		{
			if (base.currentBehaviourStateIndex != s && (force || !(Time.time - lastStateAt < 0.6f)))
			{
				lastStateAt = Time.time;
				((EnemyAI)this).SwitchToBehaviourState(s);
			}
		}

		public override void DoAIInterval()
		{
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: Invalid comparison between Unknown and I4
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: Unknown result type (might be due to invalid IL or missing references)
			((EnemyAI)this).DoAIInterval();
			NpcDefinition def = Def;
			if (!((NetworkBehaviour)this).IsServer || base.isEnemyDead || (Object)(object)base.agent == (Object)null || def == null)
			{
				return;
			}
			float time = Time.time;
			DebugTick(def, time);
			if (PanicActive && !panicking && Plugin.PanicEnabled.Value && def.J.features.meltdownPanic)
			{
				BeginPanic(time);
			}
			if (panicking)
			{
				if (!arrivedShip)
				{
					SetState(1, force: true);
				}
				PanicTick(time);
			}
			else
			{
				if (!((Behaviour)base.agent).enabled || !base.agent.isOnNavMesh || time < hitPauseUntil || FearTick(def, time) || time < busyUntil)
				{
					return;
				}
				if (time >= nextTalkAt)
				{
					PlayerControllerB val = FindListener();
					if ((Object)(object)val != (Object)null && TrySpeakTo(val))
					{
						return;
					}
					nextTalkAt = time + 2f;
				}
				if (Plugin.AloneMin.Value > 0f)
				{
					if (nextAloneAt <= 0f)
					{
						ScheduleAlone(time);
					}
					else if (time >= nextAloneAt && time >= cooldownUntil)
					{
						ScheduleAlone(time);
						if (TrySpeakTo(null))
						{
							return;
						}
					}
				}
				if (time < pauseUntil)
				{
					return;
				}
				if (wandering)
				{
					bool flag = !base.agent.pathPending && (int)base.agent.pathStatus == 2;
					Vector3 velocity = base.agent.velocity;
					if (((Vector3)(ref velocity)).sqrMagnitude > 0.04f)
					{
						lastMovedAt = time;
					}
					else if (time - lastMovedAt > 3f)
					{
						flag = true;
					}
					if ((!base.agent.pathPending && base.agent.remainingDistance <= base.agent.stoppingDistance + 0.4f) || flag || !(time < wanderDeadline))
					{
						wandering = false;
						StandStill();
						pauseUntil = time + Random.Range(2f, 7f);
						SetState((def.CrouchChance > 0f && Random.value < def.CrouchChance) ? 5 : 0);
					}
				}
				else
				{
					SetState(0);
					PickWanderTarget();
				}
			}
		}

		private void DebugTick(NpcDefinition def, float now)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: 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)
			//IL_0219: Unknown result type (might be due to invalid IL or missing references)
			//IL_0224: Unknown result type (might be due to invalid IL or missing references)
			if (Plugin.DebugLog.Value && !(now < nextDebugAt))
			{
				nextDebugAt = now + 10f;
				ManualLogSource log = Plugin.Log;
				string[] obj = new string[6]
				{
					$"[Debug] {def.Id} pos={((Component)this).transform.position} state={base.currentBehaviourStateIndex} outside={base.isOutside} hp={base.enemyHP} ",
					null,
					null,
					null,
					null,
					null
				};
				object[] obj2 = new object[4]
				{
					panicking,
					inFear,
					base.agent.isOnNavMesh,
					null
				};
				Vector3 velocity = base.agent.velocity;
				obj2[3] = ((Vector3)(ref velocity)).magnitude;
				obj[1] = string.Format("panic={0} fear={1} onNavMesh={2} vel={3:0.0} ", obj2);
				obj[2] = $"sprite: enabled={(Object)(object)rend != (Object)null && ((Renderer)rend).enabled} active={(Object)(object)board != (Object)null && ((Component)board).gameObject.activeInHierarchy} ";
				obj[3] = $"layer={(((Object)(object)board != (Object)null) ? ((Component)board).gameObject.layer : (-1))} seen={(Object)(object)rend != (Object)null && ((Renderer)rend).isVisible} ";
				obj[4] = string.Format("h={0:0.00} mat={1} ", ((Object)(object)board != (Object)null) ? board.localScale.y : 0f, ((Object)(object)shown != (Object)null) ? ((Object)shown).name : "none");
				obj[5] = $"you={(((Object)(object)GameNetworkManager.Instance?.localPlayerController != (Object)null) ? Vector3.Distance(((Component)GameNetworkManager.Instance.localPlayerController).transform.position, ((Component)this).transform.position) : (-1f)):0} m";
				log.LogInfo((object)string.Concat(obj));
			}
		}

		private bool MoveTo(Vector3 target, bool checkForPath = false)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			if (!((EnemyAI)this).SetDestinationToPosition(target, checkForPath))
			{
				return false;
			}
			base.moveTowardsDestination = true;
			if (((Behaviour)base.agent).enabled && base.agent.isOnNavMesh && !base.agent.pathPending)
			{
				if (base.agent.hasPath)
				{
					Vector3 val = base.agent.destination - base.destination;
					if (!(((Vector3)(ref val)).sqrMagnitude > 1f))
					{
						goto IL_007e;
					}
				}
				base.agent.SetDestination(base.destination);
			}
			goto IL_007e;
			IL_007e:
			return true;
		}

		private void StandStill()
		{
			base.moveTowardsDestination = false;
			base.movingTowardsTargetPlayer = false;
			if (((Behaviour)base.agent).enabled && base.agent.isOnNavMesh)
			{
				base.agent.ResetPath();
			}
		}

		private void PickWanderTarget()
		{
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			base.agent.speed = Def.WalkSpeed;
			if (base.allAINodes == null || base.allAINodes.Length == 0)
			{
				pauseUntil = Time.time + 5f;
				return;
			}
			for (int i = 0; i < 8; i++)
			{
				GameObject val = base.allAINodes[Random.Range(0, base.allAINodes.Length)];
				if (!((Object)(object)val == (Object)null))
				{
					float num = Vector3.Distance(val.transform.position, ((Component)this).transform.position);
					if (!(num < 6f) && !(num > 40f) && MoveTo(val.transform.position, checkForPath: true))
					{
						base.moveTowardsDestination = true;
						wandering = true;
						lastMovedAt = Time.time + 1f;
						wanderDeadline = Time.time + num / Mathf.Max(0.5f, base.agent.speed) * 2f + 5f;
						return;
					}
				}
			}
			pauseUntil = Time.time + 3f;
		}

		private PlayerControllerB FindListener()
		{
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: 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_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			PlayerControllerB result = null;
			float num = Plugin.TalkRange.Value;
			int collidersAndRoomMaskAndDefault = StartOfRound.Instance.collidersAndRoomMaskAndDefault;
			PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts;
			foreach (PlayerControllerB val in allPlayerScripts)
			{
				if (!((Object)(object)val == (Object)null) && val.isPlayerControlled && !val.isPlayerDead && val.isInsideFactory != base.isOutside)
				{
					Vector3 val2 = (((Object)(object)val.gameplayCamera != (Object)null) ? ((Component)val.gameplayCamera).transform.position : (((Component)val).transform.position + Vector3.up * 1.5f));
					float num2 = Vector3.Distance(base.eye.position, val2);
					if (!(num2 > num) && !Physics.Linecast(base.eye.position, val2, collidersAndRoomMaskAndDefault, (QueryTriggerInteraction)1))
					{
						result = val;
						num = num2;
					}
				}
			}
			return result;
		}

		private void ScheduleAlone(float now)
		{
			nextAloneAt = now + Random.Range(Plugin.AloneMin.Value, Mathf.Max(Plugin.AloneMin.Value, Plugin.AloneMax.Value));
		}

		private bool TrySpeakTo(PlayerControllerB p)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: 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_0066: Unknown result type (might be due to invalid IL or missing references)
			VoiceClip voiceClip = Def.Voice.PickNormal();
			if (voiceClip == null)
			{
				nextTalkAt = Time.time + 60f;
				return false;
			}
			Quaternion val = Quaternion.LookRotation(((Object)(object)p != (Object)null) ? Flat(((Component)p).transform.position - ((Component)this).transform.position, facing) : facing);
			float y = ((Quaternion)(ref val)).eulerAngles.y;
			StandStill();
			wandering = false;
			busyUntil = Time.time + voiceClip.Seconds + (float)voiceClip.Pcm.Length / 16384f * 0.05f + 1.5f;
			pauseUntil = busyUntil + Random.Range(1f, 3f);
			nextTalkAt = busyUntil + Random.Range(Plugin.CooldownMin.Value, Mathf.Max(Plugin.CooldownMin.Value, Plugin.CooldownMax.Value));
			cooldownUntil = nextTalkAt;
			if ((Object)(object)p != (Object)null)
			{
				ScheduleAlone(Time.time);
			}
			Plugin.Log.LogInfo((object)string.Format("{0}: saying {1} ({2:0.0} s) to {3}.", Def.Id, Path.GetFileName(voiceClip.File), voiceClip.Seconds, ((Object)(object)p != (Object)null) ? p.playerUsername : "nobody"));
			((MonoBehaviour)this).StartCoroutine(Net.SendVoice(((NetworkBehaviour)this).NetworkObjectId, y, 1f, voiceClip));
			return true;
		}

		private bool FearTick(NpcDefinition def, float now)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: 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)
			if (!def.FearEnabled || def.FearRadius <= 0f)
			{
				if (inFear)
				{
					EndFear();
				}
				return false;
			}
			float dist;
			EnemyAI val = FindThreat(def, out dist);
			if ((Object)(object)val != (Object)null)
			{
				lastThreatAt = now;
				threatPos = ((Component)val).transform.position;
				if (!inFear)
				{
					StartFear(val);
				}
			}
			if (!inFear)
			{
				return false;
			}
			if ((Object)(object)val == (Object)null)
			{
				if (now - lastThreatAt > def.CalmDelay)
				{
					EndFear();
					return false;
				}
				if (!hasSpot || Arrived(spot))
				{
					HoldFacing();
				}
				FearShout(def, now, 0.6f);
				return true;
			}
			fearRunning = dist < def.FearRunDistance || (fearRunning && dist < def.FearRunDistance + 1f);
			if (fearRunning)
			{
				SetState(4);
				base.agent.speed = Mathf.Max(1f, Plugin.PanicSpeed.Value);
				if ((!hasSpot && now - spotPickedAt > 1f) || (hasSpot && Arrived(spot)) || now - spotPickedAt > 3f)
				{
					PickSpot(val, 6f);
				}
				FearShout(def, now, 1f);
				return true;
			}
			if (hasSpot && Arrived(spot) && !CanSee(val, base.eye.position))
			{
				HoldFacing();
			}
			else
			{
				SetState(2);
				base.agent.speed = Mathf.Max(0.3f, Plugin.SneakSpeed.Value);
				if ((!hasSpot && now - spotPickedAt > 1f) || (hasSpot && Arrived(spot)) || now - spotPickedAt > 6f)
				{
					PickSpot(val, 2f);
				}
			}
			FearShout(def, now, 0.6f);
			return true;
		}

		private void StartFear(EnemyAI threat)
		{
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			inFear = true;
			hasSpot = false;
			fearRunning = false;
			wandering = false;
			StandStill();
			busyUntil = 0f;
			nextShoutAt = Time.time + Random.Range(0.2f, 0.8f);
			Plugin.Log.LogInfo((object)$"{Def.Id}: scared of {threat.enemyType?.enemyName} at {Vector3.Distance(((Component)this).transform.position, ((Component)threat).transform.position):0.0} m.");
		}

		private void EndFear()
		{
			inFear = false;
			hasSpot = false;
			fearRunning = false;
			base.agent.speed = Def.WalkSpeed;
			StandStill();
			SetState(0, force: true);
			pauseUntil = Time.time + Random.Range(1f, 3f);
			Plugin.Log.LogInfo((object)(Def.Id + ": calmed down."));
		}

		private void HoldFacing()
		{
			//IL_000f: 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_001f: 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_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: 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_0046: Unknown result type (might be due to invalid IL or missing references)
			SetState(3);
			StandStill();
			Vector3 val = Flat(threatPos - ((Component)this).transform.position, -((Component)this).transform.forward);
			((Component)this).transform.rotation = Quaternion.LookRotation(val, Vector3.up);
		}

		private bool Arrived(Vector3 p)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			return Vector3.Distance(Flat3(((Component)this).transform.position), Flat3(p)) < 1.2f;
		}

		private static Vector3 Flat3(Vector3 v)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			v.y = 0f;
			return v;
		}

		private EnemyAI FindThreat(NpcDefinition def, out float dist)
		{
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: 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_017e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_0196: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
			dist = float.MaxValue;
			EnemyAI result = null;
			RoundManager instance = RoundManager.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return null;
			}
			string[] source = (from x in (Plugin.FearIgnore.Value ?? "").Split(',')
				select x.Trim() into x
				where x.Length > 0
				select x).ToArray();
			int collidersAndRoomMaskAndDefault = StartOfRound.Instance.collidersAndRoomMaskAndDefault;
			foreach (EnemyAI spawnedEnemy in instance.SpawnedEnemies)
			{
				if ((Object)(object)spawnedEnemy == (Object)null || (Object)(object)spawnedEnemy == (Object)(object)this || spawnedEnemy.isEnemyDead || spawnedEnemy is PaperPalAI || spawnedEnemy.isOutside != base.isOutside)
				{
					continue;
				}
				string n = (((Object)(object)spawnedEnemy.enemyType != (Object)null) ? spawnedEnemy.enemyType.enemyName : "");
				if (source.Any((string x) => string.Equals(x, n, StringComparison.OrdinalIgnoreCase)))
				{
					continue;
				}
				float num = Vector3.Distance(((Component)this).transform.position, ((Component)spawnedEnemy).transform.position);
				if (!(num > def.FearRadius) && !(num >= dist))
				{
					Vector3 val = (((Object)(object)spawnedEnemy.eye != (Object)null) ? spawnedEnemy.eye.position : (((Component)spawnedEnemy).transform.position + Vector3.up));
					if (!Physics.Linecast(base.eye.position, val, collidersAndRoomMaskAndDefault, (QueryTriggerInteraction)1))
					{
						result = spawnedEnemy;
						dist = num;
					}
				}
			}
			return result;
		}

		private bool CanSee(EnemyAI threat, Vector3 point)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			return !Physics.Linecast(((Object)(object)threat.eye != (Object)null) ? threat.eye.position : (((Component)threat).transform.position + Vector3.up), point, StartOfRound.Instance.collidersAndRoomMaskAndDefault, (QueryTriggerInteraction)1);
		}

		private void PickSpot(EnemyAI threat, float minGain)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: 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_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			spotPickedAt = Time.time;
			if (base.allAINodes == null || base.allAINodes.Length == 0)
			{
				hasSpot = false;
				return;
			}
			float curD = Vector3.Distance(((Component)this).transform.position, ((Component)threat).transform.position);
			List<Vector3> source = (from n in base.allAINodes
				where (Object)(object)n != (Object)null
				select n.transform.position into p
				where Vector3.Distance(p, ((Component)this).transform.position) < 30f && Vector3.Distance(p, ((Component)threat).transform.position) > curD + minGain
				orderby Vector3.Distance(p, ((Component)this).transform.position)
				select p).Take(14).ToList();
			bool[] array = new bool[2] { true, false };
			for (int num = 0; num < array.Length; num++)
			{
				IEnumerable<Vector3> enumerable2;
				if (!array[num])
				{
					IEnumerable<Vector3> enumerable = source.OrderByDescending((Vector3 c) => Vector3.Distance(c, ((Component)threat).transform.position));
					enumerable2 = enumerable;
				}
				else
				{
					enumerable2 = source.Where((Vector3 c) => !CanSee(threat, c + Vector3.up));
				}
				foreach (Vector3 item in enumerable2)
				{
					if (MoveTo(item, checkForPath: true))
					{
						spot = item;
						hasSpot = true;
						return;
					}
				}
			}
			hasSpot = false;
			StandStill();
		}

		private void FearShout(NpcDefinition def, float now, float volume)
		{
			if (!(now < nextShoutAt))
			{
				VoiceClip voiceClip = def.Voice.PickFear();
				if (voiceClip == null)
				{
					nextShoutAt = now + 6f;
					return;
				}
				nextShoutAt = now + voiceClip.Seconds + Random.Range(Plugin.ShoutPauseMin.Value, Mathf.Max(Plugin.ShoutPauseMin.Value, Plugin.ShoutPauseMax.Value));
				((MonoBehaviour)this).StartCoroutine(Net.SendVoice(((NetworkBehaviour)this).NetworkObjectId, float.NaN, volume, voiceClip));
			}
		}

		private void BeginPanic(float now)
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			panicking = true;
			inFear = false;
			wandering = false;
			arrivedShip = false;
			shipTargetSet = false;
			busyUntil = 0f;
			panicMoveAt = now + Random.Range(0.3f, 1.5f);
			nextShoutAt = panicMoveAt;
			doorStartedAt = now;
			insideDoor = RoundManager.FindMainEntrancePosition(true, false);
			outsideDoor = RoundManager.FindMainEntrancePosition(true, true);
			Plugin.Log.LogInfo((object)$"{Def.Id}: panics (meltdown), outside={base.isOutside}.");
		}

		private void PanicTick(float now)
		{
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: 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_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_023d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0242: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_03bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0298: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_02df: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0304: Unknown result type (might be due to invalid IL or missing references)
			//IL_0208: Unknown result type (might be due to invalid IL or missing references)
			//IL_03de: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0365: Unknown result type (might be due to invalid IL or missing references)
			//IL_0344: Unknown result type (might be due to invalid IL or missing references)
			//IL_0352: Unknown result type (might be due to invalid IL or missing references)
			//IL_0357: Unknown result type (might be due to invalid IL or missing references)
			//IL_022a: Unknown result type (might be due to invalid IL or missing references)
			//IL_022f: Unknown result type (might be due to invalid IL or missing references)
			//IL_047f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0409: Unknown result type (might be due to invalid IL or missing references)
			//IL_040e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0418: Unknown result type (might be due to invalid IL or missing references)
			//IL_042a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0430: Unknown result type (might be due to invalid IL or missing references)
			if (now < panicMoveAt)
			{
				return;
			}
			base.agent.speed = Mathf.Max(1f, Plugin.PanicSpeed.Value);
			base.agent.acceleration = 14f;
			if (now >= nextShoutAt)
			{
				Shout(arrivedShip ? 3f : 1f);
			}
			if (arrivedShip)
			{
				return;
			}
			bool flag = ((Behaviour)base.agent).enabled && base.agent.isOnNavMesh;
			if (!base.isOutside)
			{
				if (insideDoor == Vector3.zero || outsideDoor == Vector3.zero)
				{
					arrivedShip = true;
					StandStill();
					return;
				}
				bool flag2 = Vector3.Distance(((Component)this).transform.position, insideDoor) < 1.6f;
				bool flag3 = now - doorStartedAt > Mathf.Max(10f, Plugin.DoorTimeout.Value);
				if (flag2 || flag3)
				{
					if (flag3 && !flag2)
					{
						Plugin.Log.LogInfo((object)(Def.Id + ": could not reach the main entrance in time; using it anyway."));
					}
					Net.SendTeleport(((NetworkBehaviour)this).NetworkObjectId, outsideDoor, outside: true);
					doorStartedAt = now;
				}
				else if (flag)
				{
					MoveTo(insideDoor);
					base.moveTowardsDestination = true;
				}
				return;
			}
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)instance == (Object)null || instance.shipIsLeaving || instance.inShipPhase)
			{
				StandStill();
				return;
			}
			if (!shipTargetSet)
			{
				shipTargetSet = true;
				shipNudges = 0;
				shipStartedAt = (shipLastMovedAt = now);
				shipTarget = ((instance.insideShipPositions != null && instance.insideShipPositions.Length != 0) ? instance.insideShipPositions[Random.Range(0, instance.insideShipPositions.Length)].position : (((Object)(object)instance.middleOfShipNode != (Object)null) ? instance.middleOfShipNode.position : instance.shipLandingPosition.position));
				if (flag && !MoveTo(shipTarget, checkForPath: true) && (Object)(object)instance.middleOfShipNode != (Object)null)
				{
					shipTarget = instance.middleOfShipNode.position;
				}
			}
			if (flag)
			{
				Vector3 velocity = base.agent.velocity;
				if (((Vector3)(ref velocity)).sqrMagnitude > 0.25f)
				{
					shipLastMovedAt = now;
				}
			}
			if (now - shipLastMovedAt > 4f && shipNudges < 3 && now - shipStartedAt <= 45f)
			{
				shipNudges++;
				Vector3 val = Flat(shipTarget - ((Component)this).transform.position, ((Component)this).transform.forward);
				Vector3 val2 = ((Component)this).transform.position + val * (4f + 2f * (float)shipNudges);
				Plugin.Log.LogInfo((object)$"{Def.Id}: stuck on the way to the ship (path {base.agent.pathStatus}); nudging toward it (try {shipNudges}).");
				NavMeshHit val3 = default(NavMeshHit);
				if (NavMesh.SamplePosition(val2, ref val3, 5f, base.agent.areaMask))
				{
					base.agent.Warp(((NavMeshHit)(ref val3)).position);
					base.serverPosition = ((NavMeshHit)(ref val3)).position;
				}
				shipLastMovedAt = now;
				MoveTo(shipTarget);
				base.moveTowardsDestination = true;
				return;
			}
			if (now - shipStartedAt > 45f || now - shipLastMovedAt > 4f)
			{
				arrivedShip = true;
				StandStill();
				Plugin.Log.LogInfo((object)$"{Def.Id}: could not reach the ship; staying at {((Component)this).transform.position}.");
				return;
			}
			if (!flag)
			{
				return;
			}
			if (!(Vector3.Distance(((Component)this).transform.position, shipTarget) < 1.6f))
			{
				if ((Object)(object)instance.shipInnerRoomBounds != (Object)null)
				{
					Bounds bounds = instance.shipInnerRoomBounds.bounds;
					if (((Bounds)(ref bounds)).Contains(((Component)this).transform.position) && Vector3.Distance(((Component)this).transform.position, shipTarget) < 4f)
					{
						goto IL_0441;
					}
				}
				MoveTo(shipTarget);
				base.moveTowardsDestination = true;
				return;
			}
			goto IL_0441;
			IL_0441:
			arrivedShip = true;
			base.isInsidePlayerShip = true;
			StandStill();
			SetState(6, force: true);
			Plugin.Log.LogInfo((object)(Def.Id + ": reached the ship."));
		}

		private void Shout(float intervalScale)
		{
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			float time = Time.time;
			VoiceClip voiceClip = Def.Voice.PickPanic();
			if (voiceClip == null)
			{
				nextShoutAt = time + 5f;
				return;
			}
			nextShoutAt = time + voiceClip.Seconds + Random.Range(Plugin.ShoutPauseMin.Value, Mathf.Max(Plugin.ShoutPauseMin.Value, Plugin.ShoutPauseMax.Value)) * intervalScale;
			((MonoBehaviour)this).StartCoroutine(Net.SendVoice(((NetworkBehaviour)this).NetworkObjectId, float.NaN, 1f, voiceClip));
			if (base.isOutside && (Object)(object)RoundManager.Instance != (Object)null)
			{
				RoundManager.Instance.PlayAudibleNoise(((Component)this).transform.position, 25f, 0.9f, 0, base.isInsidePlayerShip && StartOfRound.Instance.hangarDoorsClosed, 7531);
			}
		}

		internal void ApplyTeleport(Vector3 pos, bool outside)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: 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_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			if (base.isEnemyDead)
			{
				return;
			}
			Vector3 val = (((Object)(object)RoundManager.Instance != (Object)null) ? RoundManager.Instance.GetNavMeshPosition(pos, default(NavMeshHit), 5f, -1) : pos);
			if (((NetworkBehaviour)this).IsOwner && (Object)(object)base.agent != (Object)null)
			{
				((Behaviour)base.agent).enabled = false;
				((Component)this).transform.position = val;
				((Behaviour)base.agent).enabled = true;
				if (!base.agent.isOnNavMesh)
				{
					base.agent.Warp(val);
				}
				base.agent.ResetPath();
			}
			else
			{
				((Component)this).transform.position = val;
			}
			base.serverPosition = val;
			lastPos = val;
			((EnemyAI)this).SetEnemyOutside(outside);
			if (outside && !everOutside)
			{
				everOutside = true;
				graceUntil = Time.time + Mathf.Max(0f, Plugin.GraceSeconds.Value);
			}
			EntranceTeleport val2 = RoundManager.FindMainEntranceScript(outside);
			if ((Object)(object)val2 != (Object)null && (Object)(object)val2.entrancePointAudio != (Object)null && val2.doorAudios != null && val2.doorAudios.Length != 0)
			{
				val2.entrancePointAudio.PlayOneShot(val2.doorAudios[0]);
			}
			if (((NetworkBehaviour)this).IsServer)
			{
				shipTargetSet = false;
				doorStartedAt = Time.time;
			}
		}

		internal bool MonsterDamageAllowed(out string why)
		{
			//IL_0052: 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_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			if (!base.isOutside)
			{
				why = "inside";
				return false;
			}
			if (Time.time < graceUntil)
			{
				why = "grace";
				return false;
			}
			Vector3 val = (((Object)(object)base.eye != (Object)null) ? base.eye.position : (((Component)this).transform.position + Vector3.up));
			int collidersAndRoomMaskAndDefault = StartOfRound.Instance.collidersAndRoomMaskAndDefault;
			PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts;
			foreach (PlayerControllerB val2 in allPlayerScripts)
			{
				if (!((Object)(object)val2 == (Object)null) && val2.isPlayerControlled && !val2.isPlayerDead)
				{
					Vector3 val3 = (((Object)(object)val2.gameplayCamera != (Object)null) ? ((Component)val2.gameplayCamera).transform.position : (((Component)val2).transform.position + Vector3.up * 1.5f));
					float num = Vector3.Distance(val3, val);
					if (num <= Plugin.WitnessCloseRange.Value)
					{
						why = "close witness";
						return true;
					}
					if (num <= Plugin.WitnessRange.Value && !Physics.Linecast(val3, val, collidersAndRoomMaskAndDefault, (QueryTriggerInteraction)1))
					{
						why = "witness in sight";
						return true;
					}
				}
			}
			why = "no witness";
			return false;
		}

		public override void HitEnemy(int force = 1, PlayerControllerB playerWhoHit = null, bool playHitSFX = false, int hitID = -1)
		{
			if (base.isEnemyDead)
			{
				return;
			}
			if ((Object)(object)playerWhoHit == (Object)null && !MonsterDamageAllowed(out var why))
			{
				if (Plugin.DebugLog.Value && ((NetworkBehaviour)this).IsServer)
				{
					Plugin.Log.LogInfo((object)("[Debug] " + Def?.Id + ": monster hit ignored (" + why + ")."));
				}
				return;
			}
			base.enemyHP -= force;
			hitCrouchUntil = Time.time + 1.5f;
			if (((NetworkBehaviour)this).IsServer)
			{
				if (!panicking && base.currentBehaviourStateIndex != 4)
				{
					hitPauseUntil = Time.time + 1.5f;
					StandStill();
				}
				if (base.enemyHP > 0 && Time.time >= nextHurtShoutAt)
				{
					nextHurtShoutAt = Time.time + 0.3f;
					VoiceClip voiceClip = Def?.Voice.PickHurt();
					if (voiceClip != null)
					{
						((MonoBehaviour)this).StartCoroutine(Net.SendVoice(((NetworkBehaviour)this).NetworkObjectId, float.NaN, 1f, voiceClip));
					}
				}
			}
			if (base.enemyHP > 0 || !((NetworkBehaviour)this).IsOwner)
			{
				return;
			}
			SelfKilling = true;
			try
			{
				((EnemyAI)this).KillEnemyOnOwnerClient(false);
			}
			finally
			{
				SelfKilling = false;
			}
		}

		public override void KillEnemy(bool destroy = false)
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			((EnemyAI)this).KillEnemy(destroy);
			if (!base.isEnemyDead)
			{
				return;
			}
			if ((Object)(object)voice != (Object)null)
			{
				voice.Stop();
			}
			panicking = false;
			inFear = false;
			Plugin.Log.LogInfo((object)$"{Def?.Id}: died at {((Component)this).transform.position}.");
			if (((NetworkBehaviour)this).IsServer && Def != null && Def.J.features.deathScream)
			{
				VoiceClip voiceClip = Def.Voice.Pick("grito");
				if (voiceClip != null)
				{
					((MonoBehaviour)this).StartCoroutine(Net.SendVoice(((NetworkBehaviour)this).NetworkObjectId, float.NaN, 1f, voiceClip));
				}
			}
		}

		public override void OnCollideWithPlayer(Collider other)
		{
		}

		internal void PlayVoice(float yaw, float volume, int rate, byte[] pcm)
		{
			if (!((Object)(object)voice == (Object)null))
			{
				int num = pcm.Length / 2;
				float[] array = new float[num];
				for (int i = 0; i < num; i++)
				{
					array[i] = (float)(short)(pcm[2 * i] | (pcm[2 * i + 1] << 8)) / 32768f;
				}
				AudioClip val = AudioClip.Create("PaperPalLine", num, 1, rate, false);
				val.SetData(array, 0);
				if ((Object)(object)voice.clip != (Object)null)
				{
					voice.Stop();
					Object.Destroy((Object)(object)voice.clip);
				}
				voice.clip = val;
				voice.volume = Mathf.Clamp01(Plugin.Volume.Value * Mathf.Clamp01((volume <= 0f) ? 1f : volume));
				voice.Play();
				if (!float.IsNaN(yaw))
				{
					talkYaw = yaw;
					talkFaceUntil = Time.time + val.length + 0.5f;
				}
			}
		}

		private void LateUpdate()
		{
			//IL_0034: 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_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: 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_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: 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_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_044e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0453: Unknown result type (might be due to invalid IL or missing references)
			//IL_0455: Unknown result type (might be due to invalid IL or missing references)
			//IL_045b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0460: Unknown result type (might be due to invalid IL or missing references)
			//IL_0465: Unknown result type (might be due to invalid IL or missing references)
			//IL_046a: Unknown result type (might be due to invalid IL or missing references)
			//IL_046d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0472: Unknown result type (might be due to invalid IL or missing references)
			//IL_0474: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_04be: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c3: Unknown result type (might be due to invalid IL or missing references)
			NpcDefinition def = Def;
			if ((Object)(object)board == (Object)null || (Object)(object)rend == (Object)null || def == null)
			{
				return;
			}
			float num = SpriteHeight(def);
			if (!Mathf.Approximately(board.localScale.y, num))
			{
				board.localScale = new Vector3(num, num, num);
			}
			EnsureVisible();
			RideShip();
			if (base.isEnemyDead)
			{
				LieDown(def, num);
				return;
			}
			float num2 = Mathf.Max(Time.deltaTime, 0.0001f);
			int currentBehaviourStateIndex = base.currentBehaviourStateIndex;
			Vector3 position = ((Component)this).transform.position;
			Vector3 val = position - lastPos;
			val.y = 0f;
			lastPos = position;
			float num3 = ((Vector3)(ref val)).magnitude / num2;
			if (num3 > 20f)
			{
				num3 = 0f;
			}
			speed = Mathf.Lerp(speed, num3, 1f - Mathf.Exp(-10f * num2));
			Vector3 val2 = facing;
			if (currentBehaviourStateIndex == 3 && speed < 0.4f)
			{
				val2 = Flat(((Component)this).transform.forward, facing);
			}
			else if (Time.time < talkFaceUntil)
			{
				val2 = Quaternion.Euler(0f, talkYaw, 0f) * Vector3.forward;
			}
			else if (num3 > 0.3f)
			{
				val2 = ((Vector3)(ref val)).normalized;
			}
			facing = Vector3.RotateTowards(facing, val2, 8f * num2, 0f);
			bool flag = speed > 0.3f;
			if (flag)
			{
				if (!walking)
				{
					step = 0;
					stepTimer = 0f;
				}
				stepTimer += num2 * Mathf.Clamp(speed / Mathf.Max(0.5f, def.WalkSpeed), 0.4f, 2.5f);
				float num4 = Mathf.Clamp(Plugin.StepTime.Value, 0.05f, 2f);
				if (stepTimer >= num4)
				{
					stepTimer -= num4;
					step ^= 1;
				}
			}
			walking = flag;
			SpriteSet sprites = def.Sprites;
			Pose pose = ((!walking) ? (((Time.time < hitCrouchUntil || currentBehaviourStateIndex == 3 || currentBehaviourStateIndex == 2 || currentBehaviourStateIndex == 5) && sprites.HasCrouch) ? Pose.Crouch : Pose.Idle) : ((currentBehaviourStateIndex == 2) ? ((!sprites.HasSneak) ? (sprites.HasCrouch ? Pose.Crouch : ((step == 0) ? Pose.StepA : Pose.StepB)) : ((step == 0) ? Pose.CrouchA : Pose.CrouchB)) : (((currentBehaviourStateIndex != 1 && currentBehaviourStateIndex != 4) || !sprites.HasRun) ? ((step == 0) ? Pose.StepA : Pose.StepB) : ((step == 0) ? Pose.RunA : Pose.RunB))));
			if (Time.time > sourcesRefreshAt)
			{
				allSources = ((Component)this).GetComponentsInChildren<AudioSource>(true);
				sourcesRefreshAt = Time.time + 2f;
			}
			float num5 = 0f;
			AudioSource[] array = allSources;
			foreach (AudioSource val3 in array)
			{
				if (!((Object)(object)val3 == (Object)null) && val3.isPlaying && !val3.mute)
				{
					val3.GetOutputData(rmsBuf, 0);
					float num6 = 0f;
					for (int j = 0; j < rmsBuf.Length; j++)
					{
						num6 += rmsBuf[j] * rmsBuf[j];
					}
					num5 = Mathf.Max(num5, Mathf.Sqrt(num6 / (float)rmsBuf.Length) / Mathf.Max(0.05f, val3.volume));
				}
			}
			if (num5 > Plugin.MouthThreshold.Value)
			{
				mouthOpen = true;
				mouthCloseAt = Time.time + 0.08f;
			}
			else if (Time.time > mouthCloseAt)
			{
				mouthOpen = false;
			}
			Camera val4 = (((Object)(object)StartOfRound.Instance != (Object)null && (Object)(object)StartOfRound.Instance.activeCamera != (Object)null) ? StartOfRound.Instance.activeCamera : Camera.main);
			if (!((Object)(object)val4 == (Object)null))
			{
				Vector3 val5 = Flat(((Component)val4).transform.position - position, -facing);
				float num7 = Vector3.SignedAngle(facing, val5, Vector3.up);
				int view = ((!(Mathf.Abs(num7) <= 45f)) ? ((Mathf.Abs(num7) >= 135f) ? 1 : ((num7 > 0f) ? 3 : 2)) : 0);
				board.rotation = Quaternion.LookRotation(-val5, Vector3.up);
				Show(sprites.Get(view, pose, mouthOpen ? 1 : 0));
			}
		}

		private void RideShip()
		{
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: 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_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			Transform val = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.elevatorTransform : null);
			if ((Object)(object)val == (Object)null || (base.currentBehaviourStateIndex != 6 && !riding))
			{
				return;
			}
			if (!riding)
			{
				riding = true;
				rideOffset = val.InverseTransformPoint(((Component)this).transform.position);
				if ((Object)(object)base.agent != (Object)null)
				{
					base.agent.updatePosition = false;
				}
			}
			Vector3 val2 = val.TransformPoint(rideOffset);
			((Component)this).transform.position = val2;
			base.serverPosition = val2;
			lastPos = val2;
			if ((Object)(object)base.agent != (Object)null && ((Behaviour)base.agent).enabled && base.agent.isOnNavMesh)
			{
				base.agent.nextPosition = val2;
			}
		}

		private void EnsureVisible()
		{
			bool flag = false;
			int layer = ((Component)board).gameObject.layer;
			if (layer != 19)
			{
				((Component)board).gameObject.layer = 19;
				flag = true;
			}
			if (!((Renderer)rend).enabled)
			{
				((Renderer)rend).enabled = true;
				flag = true;
			}
			if (!((Component)board).gameObject.activeSelf)
			{
				((Component)board).gameObject.SetActive(true);
				flag = true;
			}
			if (flag && hiddenReports < 5)
			{
				hiddenReports++;
				Plugin.Log.LogInfo((object)($"{Def?.Id}: sprite had been hidden (layer {layer}, outside={base.isOutside}, " + $"you inside={GameNetworkManager.Instance?.localPlayerController?.isInsideFactory}); shown again."));
			}
		}

		private void Show(Material m)
		{
			if ((Object)(object)m != (Object)null && (Object)(object)m != (Object)(object)shown)
			{
				((Renderer)rend).sharedMaterial = m;
				shown = m;
			}
		}

		private void LieDown(NpcDefinition def, float h)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: 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_0017: 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_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: 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_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = Flat(facing, Vector3.forward);
			board.rotation = Quaternion.LookRotation(val, Vector3.up) * Quaternion.Euler(90f, 0f, 0f);
			board.position = ((Component)this).transform.position + Vector3.up * Mathf.Clamp(Plugin.LieOffset.Value, 0f, 1f) - val * (h * 0.5f);
			Show(def.Sprites.Get(1, Pose.Idle, 0));
		}

		private static float SpriteHeight(NpcDefinition def)
		{
			return Mathf.Clamp((Plugin.MatchPlayerEyes.Value ? MeasurePlayerEye() : 2.35f) / def.EyeLevel * def.HeightScale, 0.02f, 8f);
		}

		private static float MeasurePlayerEye()
		{
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			PlayerControllerB val = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : null);
			if ((Object)(object)val != (Object)null && (Object)(object)val.gameplayCamera != (Object)null && !val.isPlayerDead && !val.isCrouching && !val.isClimbingLadder && !val.inSpecialInteractAnimation && (Object)(object)val.thisController != (Object)null && val.thisController.isGrounded)
			{
				Vector3 velocity = val.thisController.velocity;
				if (((Vector3)(ref velocity)).sqrMagnitude < 0.01f && !val.isInHangarShipRoom)
				{
					float num = ((Component)val.gameplayCamera).transform.position.y - ((Component)val).transform.position.y;
					if (num > 0.8f && num < 5f)
					{
						playerEyeHeight = ((playerEyeHeight <= 0f) ? num : Mathf.Lerp(playerEyeHeight, num, 0.02f));
						if (Mathf.Abs(playerEyeHeight - loggedEye) > 0.02f)
						{
							loggedEye = playerEyeHeight;
							Plugin.Log.LogInfo((object)$"Player eye height {playerEyeHeight:0.00}.");
						}
					}
				}
			}
			if (!(playerEyeHeight > 0f))
			{
				return 2.35f;
			}
			return playerEyeHeight;
		}

		private static Vector3 Flat(Vector3 v, Vector3 fallback)
		{
			//IL_0044: 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_0036: Unknown result type (might be due to invalid IL or missing references)
			v.y = 0f;
			if (((Vector3)(ref v)).sqrMagnitude < 1E-06f)
			{
				fallback.y = 0f;
				if (!(((Vector3)(ref fallback)).sqrMagnitude < 1E-06f))
				{
					return ((Vector3)(ref fallback)).normalized;
				}
				return Vector3.forward;
			}
			return ((Vector3)(ref v)).normalized;
		}

		public override void OnDestroy()
		{
			if ((Object)(object)voice != (Object)null && (Object)(object)voice.clip != (Object)null)
			{
				Object.Destroy((Object)(object)voice.clip);
			}
			((EnemyAI)this).OnDestroy();
		}
	}
	[HarmonyPatch]
	internal static class Patches
	{
		private static float panicTestAt = -1f;

		private static float nextSweep;

		private static bool IsHost
		{
			get
			{
				if ((Object)(object)NetworkManager.Singleton != (Object)null)
				{
					return NetworkManager.Singleton.IsServer;
				}
				return false;
			}
		}

		[HarmonyPatch(typeof(StartOfRound), "OnShipLandedMiscEvents")]
		[HarmonyPostfix]
		private static void OnLanded()
		{
			//IL_00fc: 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_011e: 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_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			PaperPalAI.PanicActive = false;
			panicTestAt = ((Plugin.PanicAfterLanding.Value > 0f && IsHost) ? (Time.time + Plugin.PanicAfterLanding.Value) : (-1f));
			try
			{
				if (!Plugin.GuaranteedSpawn.Value || !IsHost || Plugin.Defs.Count == 0)
				{
					return;
				}
				RoundManager instance = RoundManager.Instance;
				GameObject[] array = GameObject.FindGameObjectsWithTag("AINode");
				if ((Object)(object)instance == (Object)null || array == null || array.Length == 0)
				{
					Plugin.Log.LogInfo((object)"No interior on this moon; no guaranteed PaperPals.");
					return;
				}
				int num = Mathf.Max(1, Plugin.GuaranteedSpawnCount.Value);
				List<NpcDefinition> list = Plugin.Defs.OrderBy((NpcDefinition _) => Random.value).ToList();
				for (int num2 = 0; num2 < num; num2++)
				{
					NpcDefinition npcDefinition = list[num2 % list.Count];
					Vector3 val = array[Random.Range(0, array.Length)].transform.position;
					if (num2 == 0 && Plugin.SpawnNearEntrance.Value)
					{
						val = NearMainEntrance(array, val);
					}
					val = instance.GetNavMeshPosition(val, default(NavMeshHit), 5f, -1);
					instance.SpawnEnemyGameObject(val, Random.Range(0f, 360f), -1, npcDefinition.Type);
					Plugin.Log.LogInfo((object)$"Guaranteed spawn: {npcDefinition.Id} at {val}.");
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Guaranteed spawn failed: " + ex));
			}
		}

		private static Vector3 NearMainEntrance(GameObject[] nodes, Vector3 fallback)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: 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_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			EntranceTeleport val = ((IEnumerable<EntranceTeleport>)Object.FindObjectsOfType<EntranceTeleport>()).FirstOrDefault((Func<EntranceTeleport, bool>)((EntranceTeleport t) => !t.isEntranceToBuilding && t.entranceId == 0));
			if ((Object)(object)val == (Object)null)
			{
				return fallback;
			}
			Vector3 val2 = (((Object)(object)val.entrancePoint != (Object)null) ? val.entrancePoint.position : ((Component)val).transform.position);
			Vector3 result = fallback;
			float num = float.MaxValue;
			for (int num2 = 0; num2 < nodes.Length; num2++)
			{
				Vector3 position = nodes[num2].transform.position;
				float num3 = Vector2.Distance(new Vector2(position.x, position.z), new Vector2(val2.x, val2.z));
				if (!(num3 < 2f))
				{
					float num4 = num3 + Mathf.Abs(position.y - val2.y) * 6f;
					if (num4 < num)
					{
						num = num4;
						result = position;
					}
				}
			}
			return result;
		}

		[HarmonyPatch(typeof(StartOfRound), "Update")]
		[HarmonyPostfix]
		private static void PanicTestTimer()
		{
			if (!(panicTestAt < 0f) && !(Time.time < panicTestAt))
			{
				panicTestAt = -1f;
				if (IsHost && !PaperPalAI.PanicActive && Plugin.Defs.Count != 0)
				{
					PaperPalAI.PanicActive = true;
					Plugin.Log.LogInfo((object)"[Test] PanicAfterLanding: PaperPals panic (no apparatus pulled).");
				}
			}
		}

		[HarmonyPatch(typeof(LungProp), "EquipItem")]
		[HarmonyPrefix]
		private static void ApparatusPulled(LungProp __instance)
		{
			try
			{
				if (IsHost && __instance.isLungDocked && !PaperPalAI.PanicActive && Plugin.Defs.Count != 0)
				{
					PaperPalAI.PanicActive = true;
					Plugin.Log.LogInfo((object)"Apparatus pulled: PaperPals panic.");
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Apparatus hook failed: " + ex));
			}
		}

		[HarmonyPatch(typeof(SandWormAI), "OnCollideWithEnemy")]
		[HarmonyPrefix]
		private static bool WormWitness(EnemyAI enemyScript)
		{
			if (!(enemyScript is PaperPalAI paperPalAI) || paperPalAI.MonsterDamageAllowed(out var why))
			{
				return true;
			}
			if (Plugin.DebugLog.Value && IsHost)
			{
				Plugin.Log.LogInfo((object)("[Debug] " + paperPalAI.Def?.Id + ": sand worm ignored (" + why + ")."));
			}
			return false;
		}

		[HarmonyPatch(typeof(EnemyAI), "KillEnemyOnOwnerClient")]
		[HarmonyPrefix]
		private static bool KillGuard(EnemyAI __instance)
		{
			if (!(__instance is PaperPalAI { SelfKilling: false, isEnemyDead: false } paperPalAI))
			{
				return true;
			}
			if (paperPalAI.MonsterDamageAllowed(out var why))
			{
				return true;
			}
			if (Plugin.DebugLog.Value)
			{
				Plugin.Log.LogInfo((object)("[Debug] " + paperPalAI.Def?.Id + ": instant kill ignored (" + why + ")."));
			}
			return false;
		}

		[HarmonyPatch(typeof(RoundManager), "SpawnEnemyGameObject")]
		[HarmonyPrefix]
		private static bool ReplaceSpawn(RoundManager __instance, ref int enemyNumber, ref EnemyType enemyType, ref NetworkObjectReference __result)
		{
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.OnlyPaperPals.Value || !((NetworkBehaviour)__instance).IsServer || Plugin.Defs.Count == 0)
			{
				return true;
			}
			EnemyType original = enemyType;
			if ((Object)(object)original != (Object)null && Plugin.Defs.Any((NpcDefinition d) => (Object)(object)d.Type == (Object)(object)original))
			{
				return true;
			}
			if ((Object)(object)original == (Object)null)
			{
				if (enemyNumber == -2 || enemyNumber == -3)
				{
					original = null;
				}
				else if (enemyNumber >= 0 && enemyNumber < __instance.currentLevel.Enemies.Count)
				{
					original = __instance.currentLevel.Enemies[enemyNumber].enemyType;
				}
				else
				{
					original = Plugin.Defs[0].Type;
				}
			}
			if ((Object)(object)original != (Object)null && Plugin.Defs.Any((NpcDefinition d) => (Object)(object)d.Type == (Object)(object)original))
			{
				return true;
			}
			if ((Object)(object)original == (Object)null || original.isOutsideEnemy || original.isDaytimeEnemy)
			{
				Plugin.Log.LogInfo((object)("[OnlyPaperPals] cancelled outside spawn (" + (original?.enemyName ?? "outside/daytime") + ")."));
				__result = default(NetworkObjectReference);
				return false;
			}
			NpcDefinition npcDefinition = Plugin.Defs[Random.Range(0, Plugin.Defs.Count)];
			Plugin.Log.LogInfo((object)("[OnlyPaperPals] " + original.enemyName + " -> " + npcDefinition.Id));
			enemyType = npcDefinition.Type;
			return true;
		}

		[HarmonyPatch(typeof(RoundManager), "SpawnEnemiesOutside")]
		[HarmonyPrefix]
		private static bool NoOutside()
		{
			return !Plugin.OnlyPaperPals.Value;
		}

		[HarmonyPatch(typeof(RoundManager), "SpawnDaytimeEnemiesOutside")]
		[HarmonyPrefix]
		private static bool NoDaytime()
		{
			return !Plugin.OnlyPaperPals.Value;
		}

		[HarmonyPatch(typeof(RoundManager), "Update")]
		[HarmonyPostfix]
		private static void Sweep(RoundManager __instance)
		{
			if (!Plugin.OnlyPaperPals.Value || Time.time < nextSweep || !((NetworkBehaviour)__instance).IsServer)
			{
				return;
			}
			nextSweep = Time.time + 2f;
			try
			{
				EnemyAI[] array = Object.FindObjectsOfType<EnemyAI>();
				foreach (EnemyAI val in array)
				{
					if (!((Object)(object)val == (Object)null) && !(val is PaperPalAI))
					{
						NetworkObject networkObject = ((NetworkBehaviour)val).NetworkObject;
						if (!((Object)(object)networkObject == (Object)null) && networkObject.IsSpawned)
						{
							Plugin.Log.LogInfo((object)("[OnlyPaperPals] despawned " + (val.enemyType?.enemyName ?? ((Object)val).name) + "."));
							__instance.SpawnedEnemies.Remove(val);
							networkObject.Despawn(true);
						}
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[OnlyPaperPals] sweep: " + ex.Message));
			}
		}
	}
	[BepInPlugin("luver_inn.paperpals", "PaperPals", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "luver_inn.paperpals";

		public const string Version = "1.0.0";

		internal static ManualLogSource Log;

		internal static string PluginDir;

		internal static readonly List<NpcDefinition> Defs = new List<NpcDefinition>();

		internal static ConfigEntry<string> MirageFolder;

		internal static ConfigEntry<string> FearIgnore;

		internal static ConfigEntry<bool> OnlyPaperPals;

		internal static ConfigEntry<bool> GuaranteedSpawn;

		internal static ConfigEntry<bool> SpawnNearEntrance;

		internal static ConfigEntry<bool> DebugLog;

		internal static ConfigEntry<bool> MatchPlayerEyes;

		internal static ConfigEntry<bool> PanicEnabled;

		internal static ConfigEntry<int> GuaranteedSpawnCount;

		internal static ConfigEntry<float> Volume;

		internal static ConfigEntry<float> HearingDistance;

		internal static ConfigEntry<float> MouthThreshold;

		internal static ConfigEntry<float> StepTime;

		internal static ConfigEntry<float> CooldownMin;

		internal static ConfigEntry<float> CooldownMax;

		internal static ConfigEntry<float> AloneMin;

		internal static ConfigEntry<float> AloneMax;

		internal static ConfigEntry<float> TalkRange;

		internal static ConfigEntry<float> PanicSpeed;

		internal static ConfigEntry<float> PanicAfterLanding;

		internal static ConfigEntry<float> ShoutPauseMin;

		internal static ConfigEntry<float> ShoutPauseMax;

		internal static ConfigEntry<float> DoorTimeout;

		internal static ConfigEntry<float> LieOffset;

		internal static ConfigEntry<float> SneakSpeed;

		internal static ConfigEntry<float> WitnessRange;

		internal static ConfigEntry<float> WitnessCloseRange;

		internal static ConfigEntry<float> GraceSeconds;

		private void Awake()
		{
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			PluginDir = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location);
			BindGlobalConfig();
			foreach (NpcDefinition item in FindDefinitions())
			{
				item.BindConfig(((BaseUnityPlugin)this).Config);
				if (!item.Enabled.Value)
				{
					Log.LogInfo((object)(item.Id + ": disabled in config."));
					continue;
				}
				item.Sprites = SpriteSet.Load(item);
				if (item.Sprites.Ok)
				{
					item.Voice = new VoiceLibrary(item);
					item.Index = Defs.Count;
					Defs.Add(item);
					try
					{
						BuildAndRegister(item);
					}
					catch (Exception arg)
					{
						Log.LogError((object)$"{item.Id}: registration failed: {arg}");
						Defs.Remove(item);
					}
				}
			}
			new Harmony("luver_inn.paperpals").PatchAll(typeof(Patches));
			Log.LogInfo((object)(string.Format("PaperPals {0} loaded: {1} PaperPal(s) [{2}]", "1.0.0", Defs.Count, string.Join(", ", Defs.Select((NpcDefinition d) => d.Id))) + ((OnlyPaperPals.Value || GuaranteedSpawn.Value) ? "  ** TEST HELPERS ON **" : "")));
		}

		private void BindGlobalConfig()
		{
			MirageFolder = ((BaseUnityPlugin)this).Config.Bind<string>("Voice", "MirageFolder", "", "Mirage data folder. Empty = <game folder>/Mirage. Host only.");
			Volume = ((BaseUnityPlugin)this).Config.Bind<float>("Voice", "Volume", 1f, "0 to 1.");
			HearingDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Voice", "HearingDistance", 28f, "Metres at which their voices fade out.");
			CooldownMin = ((BaseUnityPlugin)this).Config.Bind<float>("Voice", "CooldownMin", 20f, "Minimum seconds between lines to a player. Host only.");
			CooldownMax = ((BaseUnityPlugin)this).Config.Bind<float>("Voice", "CooldownMax", 60f, "Maximum seconds between lines to a player. Host only.");
			AloneMin = ((BaseUnityPlugin)this).Config.Bind<float>("Voice", "AloneMin", 90f, "Lines said to nobody: minimum seconds between them. 0 = never. Host only.");
			AloneMax = ((BaseUnityPlugin)this).Config.Bind<float>("Voice", "AloneMax", 240f, "Lines said to nobody: maximum seconds between them. Host only.");
			TalkRange = ((BaseUnityPlugin)this).Config.Bind<float>("Voice", "TalkRange", 12f, "They talk to a player in view within this many metres. Host only.");
			MouthThreshold = ((BaseUnityPlugin)this).Config.Bind<float>("Sprite", "MouthThreshold", 0.02f, "Audio loudness (RMS) above which the mouth opens.");
			StepTime = ((BaseUnityPlugin)this).Config.Bind<float>("Sprite", "StepTime", 0.32f, "Seconds each walking frame is shown at normal speed. Live.");
			MatchPlayerEyes = ((BaseUnityPlugin)this).Config.Bind<bool>("Sprite", "MatchPlayerEyeHeight", true, "Size every pal from a standing player's eye height (a 180 cm pal has his eyes level with yours).");
			PanicEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Meltdown", "Enabled", true, "Pulling the apparatus makes every pal panic and run to the ship. Host only.");
			PanicSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Meltdown", "RunSpeed", 6.5f, "Running speed (meltdown and fleeing).");
			ShoutPauseMin = ((BaseUnityPlugin)this).Config.Bind<float>("Meltdown", "ShoutPauseMin", 1.5f, "Seconds between panic/fear lines (min).");
			ShoutPauseMax = ((BaseUnityPlugin)this).Config.Bind<float>("Meltdown", "ShoutPauseMax", 4f, "Seconds between panic/fear lines (max).");
			DoorTimeout = ((BaseUnityPlugin)this).Config.Bind<float>("Meltdown", "DoorTimeout", 40f, "If a pal can't reach the main entrance in time, he goes through it anyway.");
			LieOffset = ((BaseUnityPlugin)this).Config.Bind<float>("Health", "LieOffset", 0.12f, "Dead pals lie this far above the floor so clutter doesn't poke through.");
			SneakSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Fear", "SneakSpeed", 1.4f, "Crouched sneaking speed (m/s).");
			FearIgnore = ((BaseUnityPlugin)this).Config.Bind<string>("Fear", "FearIgnore", "Manticoil,Docile Locust Bees", "Monsters (enemyName, comma-separated) that never scare pals. Other PaperPals never do.");
			WitnessRange = ((BaseUnityPlugin)this).Config.Bind<float>("Witness", "WitnessRange", 30f, "Outside, monsters may hurt a pal only while a player within this range can see him...");
			WitnessCloseRange = ((BaseUnityPlugin)this).Config.Bind<float>("Witness", "WitnessCloseRange", 15f, "...or a player is within this range (no sight needed).");
			GraceSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Witness", "GraceSeconds", 10f, "Full immunity from monsters after spawning and after first stepping outside.");
			OnlyPaperPals = ((BaseUnityPlugin)this).Config.Bind<bool>("Testing", "OnlyPaperPals", false, "TEST ONLY (host): every inside monster spawn becomes a random pal; no other monsters.");
			GuaranteedSpawn = ((BaseUnityPlugin)this).Config.Bind<bool>("Testing", "GuaranteedSpawn", false, "TEST ONLY (host): spawn pals on every landing.");
			GuaranteedSpawnCount = ((BaseUnityPlugin)this).Config.Bind<int>("Testing", "GuaranteedSpawnCount", 1, "How many (cycles through the installed pals).");
			SpawnNearEntrance = ((BaseUnityPlugin)this).Config.Bind<bool>("Testing", "SpawnNearEntrance", false, "TEST ONLY: the first guaranteed pal spawns by the main entrance.");
			PanicAfterLanding = ((BaseUnityPlugin)this).Config.Bind<float>("Testing", "PanicAfterLanding", 0f, "TEST ONLY (host): start the meltdown panic this many seconds after landing, without pulling the apparatus. 0 = off.");
			DebugLog = ((BaseUnityPlugin)this).Config.Bind<bool>("Testing", "DebugLog", false, "Log state every 10 s and every ignored monster hit (host).");
		}

		private static IEnumerable<NpcDefinition> FindDefinitions()
		{
			List<string> list = new List<string>();
			try
			{
				list = (from f in Directory.GetFiles(Paths.PluginPath, "npc.json", SearchOption.AllDirectories)
					where Path.GetFileName(f) == "npc.json"
					where !InTemplateFolder(f)
					select f).OrderBy<string, string>((string f) => f, StringComparer.Ordinal).ToList();
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Scanning for npc.json failed: " + ex.Message));
			}
			Log.LogInfo((object)$"{list.Count} npc.json file(s) found.");
			Dictionary<string, NpcDefinition> dictionary = new Dictionary<string, NpcDefinition>();
			foreach (string item in list)
			{
				NpcDefinition npcDefinition = NpcDefinition.Load(item);
				if (npcDefinition != null)
				{
					if (dictionary.TryGetValue(npcDefinition.Id, out var value))
					{
						Log.LogError((object)("Duplicate PaperPal id '" + npcDefinition.Id + "' in " + item + "; keeping " + value.JsonPath + "."));
					}
					else
					{
						dictionary[npcDefinition.Id] = npcDefinition;
					}
				}
			}
			return dictionary.Values.OrderBy<NpcDefinition, string>((NpcDefinition d) => d.Id, StringComparer.Ordinal);
		}

		private static bool InTemplateFolder(string file)
		{
			if (!file.StartsWith(PluginDir, StringComparison.OrdinalIgnoreCase))
			{
				return false;
			}
			bool num = file.Substring(PluginDir.Length).Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar).Any((string p) => p == "Template");
			if (num)
			{
				Log.LogInfo((object)("Skipping template file " + file + "."));
			}
			return num;
		}

		private static void BuildAndRegister(NpcDefinition def)
		{
			//IL_0091: 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_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: 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_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_020c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0223: Unknown result type (might be due to invalid IL or missing references)
			//IL_022d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0235: Unknown result type (might be due to invalid IL or missing references)
			//IL_0251: Unknown result type (might be due to invalid IL or missing references)
			//IL_0261: Unknown result type (might be due to invalid IL or missing references)
			//IL_02be: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0334: Unknown result type (might be due to invalid IL or missing references)
			//IL_033e: Unknown result type (might be due to invalid IL or missing references)
			//IL_03cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f9: Expected O, but got Unknown
			//IL_040d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0412: Unknown result type (might be due to invalid IL or missing references)
			//IL_0423: Unknown result type (might be due to invalid IL or missing references)
			//IL_0428: Unknown result type (might be due to invalid IL or missing references)
			//IL_042d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0437: Expected O, but got Unknown
			//IL_043a: Unknown result type (might be due to invalid IL or missing references)
			float heightScale = def.HeightScale;
			GameObject val = NetworkPrefabs.CreateNetworkPrefab("PaperPal_" + def.Id);
			NavMeshAgent obj = val.AddComponent<NavMeshAgent>();
			obj.speed = def.WalkSpeed;
			obj.angularSpeed = 360f;
			obj.acceleration = 8f;
			obj.stoppingDistance = 0.3f;
			obj.radius = Mathf.Max(0.1f, 0.35f * heightScale);
			obj.height = Mathf.Max(0.3f, 1.8f * heightScale);
			obj.obstacleAvoidanceType = (ObstacleAvoidanceType)1;
			obj.avoidancePriority = 99;
			Transform transform = new GameObject("Eye").transform;
			transform.SetParent(val.transform, false);
			transform.localPosition = new Vector3(0f, 1.6f * heightScale, 0f);
			GameObject val2 = new GameObject("Billboard");
			val2.transform.SetParent(val.transform, false);
			val2.AddComponent<MeshFilter>().sharedMesh = def.Sprites.Quad;