Decompiled source of BetterEnemyAI v1.5.1

BetterEnemyAI.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace BetterEnemyAI;

[HarmonyPatch(typeof(Terminal), "InitTerminal")]
internal static class Commands
{
	private const float DefaultRange = 40f;

	private static void Postfix()
	{
		//IL_0031: 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_0022: Expected O, but got Unknown
		new ConsoleCommand("bea_creatures", "[range] - BetterEnemyAI: list creatures near you by the names used in its settings", (ConsoleEvent)delegate(ConsoleEventArgs args)
		{
			//IL_0089: 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)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				args.Context.AddString("Join a world first.");
			}
			else
			{
				if (args.Length < 2 || !float.TryParse(args[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
				{
					result = 40f;
				}
				SortedDictionary<string, int> sortedDictionary = new SortedDictionary<string, int>();
				Dictionary<string, string> dictionary = new Dictionary<string, string>();
				foreach (Character allCharacter in Character.GetAllCharacters())
				{
					if (!((Object)(object)allCharacter == (Object)null) && !allCharacter.IsPlayer() && !(Vector3.Distance(((Component)allCharacter).transform.position, ((Component)localPlayer).transform.position) > result))
					{
						MonsterAI component = ((Component)allCharacter).GetComponent<MonsterAI>();
						if (!((Object)(object)component == (Object)null))
						{
							AiState aiState = AiState.Get(component);
							sortedDictionary.TryGetValue(aiState.PrefabName, out var value);
							sortedDictionary[aiState.PrefabName] = value + 1;
							dictionary[aiState.PrefabName] = Describe(allCharacter, aiState);
						}
					}
				}
				if (sortedDictionary.Count != 0)
				{
					args.Context.AddString("Creatures within " + result.ToString("0", CultureInfo.InvariantCulture) + " m:");
					{
						foreach (KeyValuePair<string, int> item in sortedDictionary)
						{
							args.Context.AddString("  " + item.Key + " x" + item.Value + " - " + dictionary[item.Key]);
						}
						return;
					}
				}
				args.Context.AddString("No creatures within " + result.ToString("0", CultureInfo.InvariantCulture) + " m.");
			}
		}, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
	}

	private static string Describe(Character character, AiState state)
	{
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		if (!Plugin.Enabled.Value)
		{
			return "mod is off";
		}
		if (character.IsTamed() || (int)character.GetFaction() == 0)
		{
			return "friendly, not affected";
		}
		if (character.IsBoss() && !Plugin.AffectBosses.Value)
		{
			return "boss, not affected";
		}
		if (Plugin.IsExcluded(state.PrefabName))
		{
			return "excluded";
		}
		if (!state.Settings.Enabled)
		{
			return "turned off in CreatureOverrides";
		}
		if (state.Settings != CreatureSettings.Defaults)
		{
			return "affected, has CreatureOverrides";
		}
		return "affected";
	}
}
internal interface ISynced
{
	string Key { get; }

	string Serialize();

	void ApplyServer(string text);

	void ClearServer();
}
public class Synced<T> : ISynced
{
	public readonly ConfigEntry<T> Entry;

	private T serverValue;

	private bool hasServerValue;

	public T Value
	{
		get
		{
			if (!hasServerValue)
			{
				return Entry.Value;
			}
			return serverValue;
		}
	}

	public string Key => ((ConfigEntryBase)Entry).Definition.Section + "/" + ((ConfigEntryBase)Entry).Definition.Key;

	public event Action Changed;

	public Synced(ConfigEntry<T> entry)
	{
		Entry = entry;
		EventHandler eventHandler = delegate
		{
			RaiseChanged();
			ConfigSync.OnLocalChanged();
		};
		entry.SettingChanged += eventHandler;
		ConfigSync.Register(this);
	}

	public string Serialize()
	{
		return TomlTypeConverter.ConvertToString((object)Entry.Value, typeof(T));
	}

	public void ApplyServer(string text)
	{
		serverValue = (T)TomlTypeConverter.ConvertToValue(text, typeof(T));
		hasServerValue = true;
		RaiseChanged();
	}

	public void ClearServer()
	{
		if (hasServerValue)
		{
			hasServerValue = false;
			RaiseChanged();
		}
	}

	private void RaiseChanged()
	{
		if (this.Changed != null)
		{
			this.Changed();
		}
	}
}
internal static class ConfigSync
{
	[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
	private static class NewConnectionPatch
	{
		private static void Postfix(ZNet __instance, ZNetPeer __0)
		{
			if (__instance.IsServer())
			{
				__0.m_rpc.Register<string>("BetterEnemyAI_Hello", (Action<ZRpc, string>)OnHello);
			}
			else
			{
				__0.m_rpc.Register<ZPackage>("BetterEnemyAI_Config", (Action<ZRpc, ZPackage>)OnConfig);
			}
		}
	}

	[HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")]
	private static class PeerInfoPatch
	{
		private static void Postfix(ZNet __instance, ZRpc __0)
		{
			if (__instance.IsServer())
			{
				ZNetPeer val = FindPeer(__0);
				if (val != null)
				{
					Send(val);
					((MonoBehaviour)Plugin.Instance).StartCoroutine(CheckHello(val));
				}
			}
		}
	}

	[HarmonyPatch(typeof(ZNet), "OnDestroy")]
	private static class LeavePatch
	{
		private static void Postfix()
		{
			foreach (ISynced setting in settings)
			{
				setting.ClearServer();
			}
			peersWithMod.Clear();
		}
	}

	private const string ConfigRpc = "BetterEnemyAI_Config";

	private const string HelloRpc = "BetterEnemyAI_Hello";

	private const float HelloTimeout = 20f;

	private static readonly List<ISynced> settings = new List<ISynced>();

	private static readonly HashSet<long> peersWithMod = new HashSet<long>();

	private static bool IsServer
	{
		get
		{
			if ((Object)(object)ZNet.instance != (Object)null)
			{
				return ZNet.instance.IsServer();
			}
			return false;
		}
	}

	internal static void Register(ISynced setting)
	{
		settings.Add(setting);
	}

	internal static void OnLocalChanged()
	{
		if (!IsServer)
		{
			return;
		}
		foreach (ZNetPeer peer in ZNet.instance.GetPeers())
		{
			if (peer.m_rpc != null)
			{
				Send(peer);
			}
		}
	}

	private static void Send(ZNetPeer peer)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Expected O, but got Unknown
		ZPackage val = new ZPackage();
		val.Write("1.5.1");
		val.Write(settings.Count);
		foreach (ISynced setting in settings)
		{
			val.Write(setting.Key);
			val.Write(setting.Serialize());
		}
		peer.m_rpc.Invoke("BetterEnemyAI_Config", new object[1] { val });
	}

	private static void OnConfig(ZRpc rpc, ZPackage pkg)
	{
		string text = pkg.ReadString();
		int num = pkg.ReadInt();
		Dictionary<string, string> dictionary = new Dictionary<string, string>();
		for (int i = 0; i < num; i++)
		{
			string key = pkg.ReadString();
			dictionary[key] = pkg.ReadString();
		}
		foreach (ISynced setting in settings)
		{
			if (dictionary.TryGetValue(setting.Key, out var value))
			{
				try
				{
					setting.ApplyServer(value);
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("Could not apply server value for " + setting.Key + ": " + ex.Message));
				}
			}
		}
		Plugin.Log.LogInfo((object)"Using the server's BetterEnemyAI settings.");
		if (text != "1.5.1")
		{
			Warn("BetterEnemyAI version mismatch: server has " + text + ", you have 1.5.1.");
		}
		rpc.Invoke("BetterEnemyAI_Hello", new object[1] { "1.5.1" });
	}

	private static void OnHello(ZRpc rpc, string clientVersion)
	{
		ZNetPeer val = FindPeer(rpc);
		if (val != null)
		{
			peersWithMod.Add(val.m_uid);
			if (clientVersion != "1.5.1")
			{
				Warn(val.m_playerName + " has BetterEnemyAI " + clientVersion + ", the server has 1.5.1.");
			}
		}
	}

	private static IEnumerator CheckHello(ZNetPeer peer)
	{
		yield return (object)new WaitForSeconds(20f);
		if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.GetPeers().Contains(peer) && !peersWithMod.Contains(peer.m_uid))
		{
			Warn(peer.m_playerName + " doesn't have BetterEnemyAI installed. Enemies near them will behave like vanilla.");
		}
	}

	private static ZNetPeer FindPeer(ZRpc rpc)
	{
		foreach (ZNetPeer peer in ZNet.instance.GetPeers())
		{
			if (peer.m_rpc == rpc)
			{
				return peer;
			}
		}
		return null;
	}

	private static void Warn(string message)
	{
		Plugin.Log.LogWarning((object)message);
		if ((Object)(object)MessageHud.instance != (Object)null)
		{
			MessageHud.instance.ShowMessage((MessageType)1, message, 0, (Sprite)null, false, true);
		}
	}
}
internal class CreatureSettings
{
	internal static readonly CreatureSettings Defaults = new CreatureSettings();

	public bool? enabled;

	public bool? noCircleAfterAttack;

	public float? groundFrontArc;

	public FlyingMode? flyingAfterAttack;

	public float? retreatDistance;

	public float? flyingWander;

	public float? flyingFrontArc;

	public bool? noPeriodicCircling;

	public float? giveUpWhenNotSensed;

	public float? giveUpWhenNoCombat;

	public float? maxChaseDistance;

	public float? giveUpWhenUnreachable;

	public float? ignoreAfterGiveUp;

	public bool? returnAfterGiveUp;

	public bool Enabled => enabled ?? true;

	public bool NoCircleAfterAttack => noCircleAfterAttack ?? Plugin.NoCircleAfterAttack.Value;

	public float GroundFrontArc => groundFrontArc ?? Plugin.GroundFrontArc.Value;

	public FlyingMode FlyingAfterAttack => flyingAfterAttack ?? Plugin.FlyingAfterAttack.Value;

	public float RetreatDistance => retreatDistance ?? Plugin.RetreatDistance.Value;

	public float FlyingWander => flyingWander ?? Plugin.FlyingWander.Value;

	public float FlyingFrontArc => flyingFrontArc ?? Plugin.FlyingFrontArc.Value;

	public bool NoPeriodicCircling => noPeriodicCircling ?? Plugin.NoPeriodicCircling.Value;

	public float GiveUpWhenNotSensed => giveUpWhenNotSensed ?? Plugin.GiveUpWhenNotSensed.Value;

	public float GiveUpWhenNoCombat => giveUpWhenNoCombat ?? Plugin.GiveUpWhenNoCombat.Value;

	public float MaxChaseDistance => maxChaseDistance ?? Plugin.MaxChaseDistance.Value;

	public float GiveUpWhenUnreachable => giveUpWhenUnreachable ?? Plugin.GiveUpWhenUnreachable.Value;

	public float IgnoreAfterGiveUp => ignoreAfterGiveUp ?? Plugin.IgnoreAfterGiveUp.Value;

	public bool ReturnAfterGiveUp => returnAfterGiveUp ?? Plugin.ReturnAfterGiveUp.Value;
}
internal static class CreatureOverrides
{
	private static Dictionary<string, CreatureSettings> byName = new Dictionary<string, CreatureSettings>(StringComparer.OrdinalIgnoreCase);

	internal static int Version;

	internal static CreatureSettings Find(string prefabName)
	{
		if (!byName.TryGetValue(prefabName, out var value))
		{
			return CreatureSettings.Defaults;
		}
		return value;
	}

	internal static void Parse(string text)
	{
		Dictionary<string, CreatureSettings> dictionary = new Dictionary<string, CreatureSettings>(StringComparer.OrdinalIgnoreCase);
		string[] array = text.Split(';');
		foreach (string text2 in array)
		{
			string text3 = text2.Trim();
			if (text3.Length == 0)
			{
				continue;
			}
			int num = text3.IndexOf(':');
			if (num <= 0)
			{
				Warn(text3, "expected 'CreatureName: Setting=Value'");
				continue;
			}
			string text4 = text3.Substring(0, num).Trim();
			if (!dictionary.TryGetValue(text4, out var value))
			{
				value = (dictionary[text4] = new CreatureSettings());
			}
			string[] array2 = text3.Substring(num + 1).Split(',');
			foreach (string text5 in array2)
			{
				string text6 = text5.Trim();
				if (text6.Length != 0)
				{
					int num2 = text6.IndexOf('=');
					string text7 = ((num2 <= 0) ? "expected Setting=Value" : Apply(value, text6.Substring(0, num2).Trim().ToLowerInvariant(), text6.Substring(num2 + 1).Trim()));
					if (text7 != null)
					{
						Warn(text4 + ": " + text6, text7);
					}
				}
			}
		}
		byName = dictionary;
		Version++;
	}

	private static string Apply(CreatureSettings s, string key, string value)
	{
		switch (key)
		{
		case "enabled":
			return ParseBool(value, out s.enabled);
		case "nocircleafterattack":
			return ParseBool(value, out s.noCircleAfterAttack);
		case "noperiodiccircling":
			return ParseBool(value, out s.noPeriodicCircling);
		case "groundfrontarc":
			return ParseFloat(value, out s.groundFrontArc);
		case "flyingafterattack":
			return ParseFlying(value, out s.flyingAfterAttack);
		case "retreatdistance":
			return ParseFloat(value, out s.retreatDistance);
		case "flyingwander":
			return ParseFloat(value, out s.flyingWander);
		case "flyingfrontarc":
			return ParseFloat(value, out s.flyingFrontArc);
		case "giveupwhennotsensedseconds":
			return ParseFloat(value, out s.giveUpWhenNotSensed);
		case "giveupwhennocombatseconds":
			return ParseFloat(value, out s.giveUpWhenNoCombat);
		case "maxchasedistance":
			return ParseFloat(value, out s.maxChaseDistance);
		case "giveupwhenunreachableseconds":
			return ParseFloat(value, out s.giveUpWhenUnreachable);
		case "ignoreaftergiveupseconds":
			return ParseFloat(value, out s.ignoreAfterGiveUp);
		case "returnaftergiveup":
			return ParseBool(value, out s.returnAfterGiveUp);
		case "chase":
			if (!IsVanilla(value))
			{
				return "only Chase=Vanilla is supported";
			}
			s.giveUpWhenNotSensed = 0f;
			s.giveUpWhenNoCombat = 0f;
			s.maxChaseDistance = 0f;
			s.giveUpWhenUnreachable = 0f;
			return null;
		case "circling":
			if (!IsVanilla(value))
			{
				return "only Circling=Vanilla is supported";
			}
			s.noCircleAfterAttack = false;
			s.noPeriodicCircling = false;
			s.flyingAfterAttack = FlyingMode.Vanilla;
			return null;
		default:
			return "unknown setting '" + key + "'";
		}
	}

	private static bool IsVanilla(string value)
	{
		return string.Equals(value, "vanilla", StringComparison.OrdinalIgnoreCase);
	}

	private static string ParseBool(string value, out bool? result)
	{
		result = null;
		switch (value.ToLowerInvariant())
		{
		case "true":
		case "on":
		case "yes":
		case "1":
			result = true;
			return null;
		case "false":
		case "off":
		case "no":
		case "0":
			result = false;
			return null;
		default:
			return "'" + value + "' is not true/false";
		}
	}

	private static string ParseFloat(string value, out float? result)
	{
		result = null;
		if (!float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result2))
		{
			return "'" + value + "' is not a number (use a dot for decimals, e.g. 1.5)";
		}
		result = result2;
		return null;
	}

	private static string ParseFlying(string value, out FlyingMode? result)
	{
		result = null;
		foreach (FlyingMode value2 in Enum.GetValues(typeof(FlyingMode)))
		{
			if (string.Equals(value2.ToString(), value, StringComparison.OrdinalIgnoreCase))
			{
				result = value2;
				return null;
			}
		}
		return "'" + value + "' is not Vanilla, HitAndRetreat or Hover";
	}

	private static void Warn(string entry, string problem)
	{
		Plugin.Log.LogWarning((object)("CreatureOverrides: skipped '" + entry + "': " + problem));
	}
}
public enum FlyingMode
{
	Vanilla,
	HitAndRetreat,
	Hover
}
[BepInPlugin("BetterEnemyAI", "BetterEnemyAI", "1.5.1")]
public class Plugin : BaseUnityPlugin
{
	public const string Guid = "BetterEnemyAI";

	public const string ModName = "BetterEnemyAI";

	public const string Version = "1.5.1";

	internal static Synced<bool> Enabled;

	internal static Synced<string> ExcludedCreatures;

	internal static Synced<bool> AffectBosses;

	internal static Synced<bool> AffectRaidMonsters;

	internal static Synced<bool> TamingDoesntBlockSleep;

	internal static Synced<bool> NoCircleAfterAttack;

	internal static Synced<float> GroundFrontArc;

	internal static Synced<FlyingMode> FlyingAfterAttack;

	internal static Synced<float> RetreatDistance;

	internal static Synced<float> FlyingWander;

	internal static Synced<float> FlyingFrontArc;

	internal static Synced<bool> NoPeriodicCircling;

	internal static Synced<float> GiveUpWhenNotSensed;

	internal static Synced<float> GiveUpWhenNoCombat;

	internal static Synced<float> MaxChaseDistance;

	internal static Synced<float> GiveUpWhenUnreachable;

	internal static Synced<float> IgnoreAfterGiveUp;

	internal static Synced<bool> ReturnAfterGiveUp;

	internal static Synced<string> CreatureOverridesText;

	internal static ConfigEntry<bool> DebugLog;

	internal static Plugin Instance;

	internal static ManualLogSource Log;

	private static HashSet<string> excluded = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

	private Synced<T> Bind<T>(string section, string key, T defaultValue, ConfigDescription description)
	{
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		//IL_0031: Expected O, but got Unknown
		return new Synced<T>(((BaseUnityPlugin)this).Config.Bind<T>(section, key, defaultValue, new ConfigDescription(description.Description + " In multiplayer, the server's value is used.", description.AcceptableValues, new object[0])));
	}

	private Synced<T> Bind<T>(string section, string key, T defaultValue, string description)
	{
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0017: Expected O, but got Unknown
		return Bind(section, key, defaultValue, new ConfigDescription(description, (AcceptableValueBase)null, new object[0]));
	}

	private void Awake()
	{
		//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00eb: Expected O, but got Unknown
		//IL_0154: Unknown result type (might be due to invalid IL or missing references)
		//IL_015e: Expected O, but got Unknown
		//IL_018d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0197: Expected O, but got Unknown
		//IL_0302: Unknown result type (might be due to invalid IL or missing references)
		Instance = this;
		Log = ((BaseUnityPlugin)this).Logger;
		Enabled = Bind("1 - General", "Enabled", defaultValue: true, "Turns the mod on or off.");
		ExcludedCreatures = Bind("1 - General", "ExcludedCreatures", "", "Prefab names of creatures the mod should not affect, separated by commas. Example: Wolf,Deathsquito,Troll");
		AffectBosses = Bind("1 - General", "AffectBosses", defaultValue: false, "Should bosses be affected too?");
		AffectRaidMonsters = Bind("1 - General", "AffectRaidMonsters", defaultValue: false, "Also shorten chases by creatures hunting the player during a raid? (Circling settings always apply.)");
		TamingDoesntBlockSleep = Bind("1 - General", "TamingDoesntBlockSleep", defaultValue: true, "An animal you're taming (fed, taming started) no longer counts as an enemy nearby when you try to sleep. Wild creatures still do.");
		NoCircleAfterAttack = Bind("2 - Circling", "NoCircleAfterAttack", defaultValue: true, "Ground enemies stop circling around you after attacking while their next attack recharges; they stand still and face you instead.");
		GroundFrontArc = Bind("2 - Circling", "GroundFrontArc", 90f, new ConfigDescription("With NoCircleAfterAttack, a ground enemy that ends up beside or behind you after attacking (e.g. a boar charging past) walks back around to your front before waiting, if it is more than this many degrees from where you face. 90 = it waits only in front of you, 180 = it waits wherever it is.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(30f, 180f), new object[0]));
		FlyingAfterAttack = Bind("2 - Circling", "FlyingAfterAttack", FlyingMode.HitAndRetreat, "What flying enemies (deathsquitos, bats, drakes, etc.) do while their next attack recharges. Vanilla = circle around you as usual. HitAndRetreat = back off a short distance, hover facing you, then dive when ready. Hover = stay where they are and face you.");
		RetreatDistance = Bind("2 - Circling", "RetreatDistance", 6f, "How many meters flying enemies back off from you in HitAndRetreat mode.");
		FlyingWander = Bind("2 - Circling", "FlyingWander", 1f, new ConfigDescription("How much flying enemies move around you while waiting in HitAndRetreat mode. 0 = hover in place, 1 = slow, loose circling (default), 2 = faster and wider.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), new object[0]));
		FlyingFrontArc = Bind("2 - Circling", "FlyingFrontArc", 90f, new ConfigDescription("In HitAndRetreat mode, how far (in degrees) flying enemies may circle to either side of where you are facing. 90 = they stay in front of you or at your sides, 180 = anywhere, including behind you (like vanilla).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(30f, 180f), new object[0]));
		NoPeriodicCircling = Bind("2 - Circling", "NoPeriodicCircling", defaultValue: true, "Stops some enemies from periodically breaking off their attack to circle around their target.");
		GiveUpWhenNotSensed = Bind("3 - Chase", "GiveUpWhenNotSensedSeconds", 10f, "An enemy gives up the chase if it can't see or hear you for this many seconds. Vanilla is 30. 0 = vanilla behavior.");
		GiveUpWhenNoCombat = Bind("3 - Chase", "GiveUpWhenNoCombatSeconds", 20f, "An enemy gives up the chase if for this many seconds it neither hits you nor takes damage from you (even if it can see you). Vanilla is 60. 0 = off.");
		MaxChaseDistance = Bind("3 - Chase", "MaxChaseDistance", 60f, "An enemy gives up the chase once it is this many meters away from where the chase started (unless you're fighting: it attacked or got hit in the last 5 seconds). 0 = off.");
		GiveUpWhenUnreachable = Bind("3 - Chase", "GiveUpWhenUnreachableSeconds", 6f, "A ground enemy gives up the chase if it can't find a path to you (e.g. you're on a rock) for this many seconds, unless you're fighting (it attacked or got hit in the last 5 seconds). Near your buildings, enemies that attack buildings keep vanilla behavior. 0 = off.");
		IgnoreAfterGiveUp = Bind("3 - Chase", "IgnoreAfterGiveUpSeconds", 8f, "An enemy that gave up won't look for a new target for this many seconds. If you hit it, it fights back right away.");
		ReturnAfterGiveUp = Bind("3 - Chase", "ReturnAfterGiveUp", defaultValue: true, "An enemy that gave up walks straight back to where the chase started, instead of slowly wandering around where it stopped.");
		CreatureOverridesText = Bind("4 - Per creature", "CreatureOverrides", "", "Different settings for specific creatures. Format: 'Name: Setting=Value, Setting=Value; Name: ...'. Use the setting names above, e.g. 'Troll: GiveUpWhenNoCombatSeconds=30, MaxChaseDistance=80; Greydwarf: GiveUpWhenNoCombatSeconds=15'. Shortcuts: Enabled=false (mod off for that creature), Chase=Vanilla (normal chasing), Circling=Vanilla (normal circling), e.g. 'Deathsquito: Chase=Vanilla'. Use a dot for decimals. To see creature names, open the console (F5) near them and type: bea_creatures");
		DebugLog = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "DebugLog", false, "Writes what enemies decide (e.g. why one gave up a chase) to the BepInEx log. Useful when reporting a problem. Not synced in multiplayer.");
		ExcludedCreatures.Changed += ParseExcluded;
		ParseExcluded();
		CreatureOverridesText.Changed += delegate
		{
			CreatureOverrides.Parse(CreatureOverridesText.Value);
		};
		CreatureOverrides.Parse(CreatureOverridesText.Value);
		new Harmony("BetterEnemyAI").PatchAll(typeof(Plugin).Assembly);
	}

	private static void ParseExcluded()
	{
		excluded.Clear();
		string[] array = ExcludedCreatures.Value.Split(',');
		foreach (string text in array)
		{
			string text2 = text.Trim();
			if (text2.Length > 0)
			{
				excluded.Add(text2);
			}
		}
	}

	internal static bool Affects(Character character, AiState state)
	{
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		if (!Enabled.Value || (Object)(object)character == (Object)null)
		{
			return false;
		}
		if (character.IsTamed() || (int)character.GetFaction() == 0)
		{
			return false;
		}
		if (character.IsBoss() && !AffectBosses.Value)
		{
			return false;
		}
		if (!excluded.Contains(state.PrefabName))
		{
			return state.Settings.Enabled;
		}
		return false;
	}

	internal static void Debug(string message)
	{
		if (DebugLog != null && DebugLog.Value)
		{
			Log.LogInfo((object)message);
		}
	}

	internal static bool IsExcluded(string prefabName)
	{
		return excluded.Contains(prefabName);
	}
}
internal class AiState
{
	private static readonly ConditionalWeakTable<MonsterAI, AiState> table = new ConditionalWeakTable<MonsterAI, AiState>();

	public string PrefabName;

	private CreatureSettings settings;

	private int settingsVersion = -1;

	public bool Modified;

	public bool Circulate;

	public bool CirculateFlying;

	public float CircleInterval;

	public Character ChaseTarget;

	public Vector3 ChaseStart;

	public float UnreachableSince = -1f;

	public float NextPathCheck;

	public bool Repositioning;

	public bool RunUpPending;

	private bool? isCharger;

	public bool Returning;

	public Vector3 ReturnTarget;

	public float ReturnBestDistance;

	public float ReturnProgressTime;

	public float NoiseSeed = Random.Range(0f, 1000f);

	public float LastRetreatTime = -1f;

	public Vector3 OrbitCenter;

	public float OrbitAngle;

	public float OrbitRadius;

	public Vector3 Move;

	public bool Running;

	public CreatureSettings Settings
	{
		get
		{
			if (settingsVersion != CreatureOverrides.Version)
			{
				settings = CreatureOverrides.Find(PrefabName);
				settingsVersion = CreatureOverrides.Version;
			}
			return settings;
		}
	}

	public bool IsCharger(Character character)
	{
		if (!isCharger.HasValue)
		{
			Humanoid val = (Humanoid)(object)((character is Humanoid) ? character : null);
			isCharger = (Object)(object)val != (Object)null && RunUpPatch.ShortestMinRange(val) > 0f;
		}
		return isCharger.Value;
	}

	public static AiState Get(MonsterAI ai)
	{
		if (!table.TryGetValue(ai, out var value))
		{
			value = new AiState();
			string name = ((Object)((Component)ai).gameObject).name;
			int num = name.IndexOf("(Clone)", StringComparison.Ordinal);
			value.PrefabName = ((num >= 0) ? name.Substring(0, num).Trim() : name);
			table.Add(ai, value);
		}
		return value;
	}
}
[HarmonyPatch(typeof(MonsterAI), "UpdateAI")]
internal static class CirclingPatch
{
	internal static MonsterAI Retreater;

	internal static MonsterAI Repositioner;

	private static void Prefix(MonsterAI __instance, Character ___m_character)
	{
		Retreater = null;
		Repositioner = null;
		AiState aiState = AiState.Get(__instance);
		if (!Plugin.Affects(___m_character, aiState))
		{
			if (aiState.Modified)
			{
				__instance.m_circulateWhileCharging = aiState.Circulate;
				__instance.m_circulateWhileChargingFlying = aiState.CirculateFlying;
				__instance.m_circleTargetInterval = aiState.CircleInterval;
				aiState.Modified = false;
			}
			return;
		}
		if (!aiState.Modified)
		{
			aiState.Circulate = __instance.m_circulateWhileCharging;
			aiState.CirculateFlying = __instance.m_circulateWhileChargingFlying;
			aiState.CircleInterval = __instance.m_circleTargetInterval;
			aiState.Modified = true;
		}
		CreatureSettings settings = aiState.Settings;
		bool avoidLand = __instance.m_avoidLand;
		bool flag = ___m_character.IsSwimming();
		bool flag2 = settings.NoCircleAfterAttack && settings.GroundFrontArc < 180f;
		__instance.m_circulateWhileCharging = aiState.Circulate && (avoidLand || !settings.NoCircleAfterAttack || flag2);
		FlyingMode flyingAfterAttack = settings.FlyingAfterAttack;
		__instance.m_circulateWhileChargingFlying = aiState.CirculateFlying && flyingAfterAttack != FlyingMode.Hover;
		__instance.m_circleTargetInterval = ((settings.NoPeriodicCircling && !avoidLand) ? 0f : aiState.CircleInterval);
		if (___m_character.IsFlying())
		{
			if (flyingAfterAttack == FlyingMode.HitAndRetreat && aiState.CirculateFlying)
			{
				Retreater = __instance;
			}
		}
		else if (flag2 && aiState.Circulate && !avoidLand && !flag)
		{
			Repositioner = __instance;
		}
	}

	private static void Postfix()
	{
		Retreater = null;
		Repositioner = null;
	}
}
[HarmonyPatch(typeof(BaseAI), "RandomMovementArroundPoint")]
internal static class RetreatPatch
{
	private const float NewRetreatAfter = 0.5f;

	private const float OrbitSpeed = 30f;

	private const float CenterLag = 1.5f;

	private const float SteerSmoothing = 0.45f;

	private const float HoverHeight = 2.5f;

	private const float RadiusWobble = 1.5f;

	private const float HeightWobble = 0.8f;

	private static readonly Action<BaseAI, Vector3> lookTowards = AccessTools.MethodDelegate<Action<BaseAI, Vector3>>(AccessTools.Method(typeof(BaseAI), "LookTowards", new Type[1] { typeof(Vector3) }, (Type[])null), (object)null, true);

	private static readonly Action<BaseAI> chargeStop = AccessTools.MethodDelegate<Action<BaseAI>>(AccessTools.Method(typeof(BaseAI), "ChargeStop", (Type[])null, (Type[])null), (object)null, true);

	private static bool Prefix(BaseAI __instance, float __0, Vector3 __1, Character ___m_character)
	{
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		//IL_003b: 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_0134: Unknown result type (might be due to invalid IL or missing references)
		//IL_0139: Unknown result type (might be due to invalid IL or missing references)
		//IL_014e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0153: 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_0095: 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_009b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0269: Unknown result type (might be due to invalid IL or missing references)
		//IL_0281: Unknown result type (might be due to invalid IL or missing references)
		//IL_0288: Unknown result type (might be due to invalid IL or missing references)
		//IL_028d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0292: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e2: 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_02e9: Unknown result type (might be due to invalid IL or missing references)
		//IL_02eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ed: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ef: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ca: 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_00cb: 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)
		//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_030f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0313: Unknown result type (might be due to invalid IL or missing references)
		//IL_0325: Unknown result type (might be due to invalid IL or missing references)
		//IL_0308: Unknown result type (might be due to invalid IL or missing references)
		//IL_032a: Unknown result type (might be due to invalid IL or missing references)
		//IL_032e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0333: Unknown result type (might be due to invalid IL or missing references)
		//IL_0349: Unknown result type (might be due to invalid IL or missing references)
		//IL_034e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0386: Unknown result type (might be due to invalid IL or missing references)
		//IL_038b: Unknown result type (might be due to invalid IL or missing references)
		//IL_038d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0392: Unknown result type (might be due to invalid IL or missing references)
		//IL_03c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_03c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_041a: Unknown result type (might be due to invalid IL or missing references)
		//IL_042f: 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_03eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0402: Unknown result type (might be due to invalid IL or missing references)
		//IL_0407: Unknown result type (might be due to invalid IL or missing references)
		MonsterAI retreater = CirclingPatch.Retreater;
		if ((Object)(object)retreater == (Object)null || (Object)(object)retreater != (Object)(object)__instance)
		{
			return true;
		}
		Character targetCreature = ((BaseAI)retreater).GetTargetCreature();
		if (!((Object)(object)targetCreature == (Object)null))
		{
			Vector3 val = ((Component)targetCreature).transform.position - __1;
			if (!(((Vector3)(ref val)).sqrMagnitude > 0.01f))
			{
				AiState aiState = AiState.Get(retreater);
				CreatureSettings settings = aiState.Settings;
				float num = Mathf.Max(settings.FlyingWander, 0f);
				Vector3 position = ((Component)retreater).transform.position;
				if (Time.time - aiState.LastRetreatTime > 0.5f)
				{
					Vector3 val2 = position - __1;
					val2.y = 0f;
					if (((Vector3)(ref val2)).sqrMagnitude < 0.01f)
					{
						val2 = -((Component)retreater).transform.forward;
					}
					aiState.OrbitCenter = __1;
					aiState.OrbitAngle = Mathf.Atan2(val2.x, val2.z) * 57.29578f;
					aiState.OrbitRadius = Mathf.Max(settings.RetreatDistance, 1f) * Random.Range(0.9f, 1.2f);
					aiState.Move = Vector3.zero;
					aiState.Running = true;
				}
				aiState.LastRetreatTime = Time.time;
				aiState.OrbitCenter = Vector3.Lerp(aiState.OrbitCenter, __1, 1f - Mathf.Exp((0f - __0) / 1.5f));
				float time = Time.time;
				float noiseSeed = aiState.NoiseSeed;
				float num2 = (Mathf.PerlinNoise(noiseSeed, time * 0.25f) - 0.5f) * 2.5f;
				aiState.OrbitAngle += num2 * 30f * num * __0;
				float num3 = Mathf.Clamp(settings.FlyingFrontArc, 0f, 180f);
				if (num3 < 180f)
				{
					Vector3 forward = ((Component)targetCreature).transform.forward;
					float num4 = Mathf.Atan2(forward.x, forward.z) * 57.29578f;
					float num5 = Mathf.DeltaAngle(num4, aiState.OrbitAngle);
					if (Mathf.Abs(num5) > num3)
					{
						aiState.OrbitAngle = Mathf.MoveTowardsAngle(aiState.OrbitAngle, num4 + Mathf.Sign(num5) * num3, 60f * __0);
					}
				}
				float num6 = aiState.OrbitRadius + (Mathf.PerlinNoise(noiseSeed + 13.1f, time * 0.3f) - 0.5f) * 2f * 1.5f * num;
				float num7 = aiState.OrbitAngle * ((float)Math.PI / 180f);
				Vector3 point = aiState.OrbitCenter + new Vector3(Mathf.Sin(num7), 0f, Mathf.Cos(num7)) * num6;
				point.y = aiState.OrbitCenter.y + 2.5f + (Mathf.PerlinNoise(noiseSeed + 47.9f, time * 0.35f) - 0.5f) * 2f * 0.8f * Mathf.Min(num, 1f);
				point = KeepAboveGround(retreater, point);
				Vector3 val3 = point - position;
				float magnitude = ((Vector3)(ref val3)).magnitude;
				Vector3 val4 = ((magnitude > 0.1f) ? (val3 / magnitude * Mathf.Clamp01(magnitude / 2f)) : Vector3.zero);
				aiState.Move = Vector3.Lerp(aiState.Move, val4, 1f - Mathf.Exp((0f - __0) / 0.45f));
				if (aiState.Running && magnitude < 3f)
				{
					aiState.Running = false;
				}
				else if (!aiState.Running && magnitude > 6f)
				{
					aiState.Running = true;
				}
				Vector3 val5 = targetCreature.GetTopPoint() - position;
				val5.y = 0f;
				Vector3 val6 = default(Vector3);
				((Vector3)(ref val6))..ctor(aiState.Move.x, 0f, aiState.Move.z);
				Vector3 arg = val5;
				if (((Vector3)(ref val6)).sqrMagnitude > 0.0001f && ((Vector3)(ref val5)).sqrMagnitude > 0.0001f)
				{
					arg = Vector3.Slerp(((Vector3)(ref val5)).normalized, ((Vector3)(ref val6)).normalized, Mathf.Clamp01(((Vector3)(ref val6)).magnitude * 1.5f));
				}
				chargeStop((BaseAI)(object)retreater);
				lookTowards((BaseAI)(object)retreater, arg);
				___m_character.SetRun(aiState.Running);
				___m_character.SetMoveDir(aiState.Move);
				return false;
			}
		}
		return true;
	}

	private static Vector3 KeepAboveGround(MonsterAI ai, Vector3 point)
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		float num = ZoneSystem.instance.GetSolidHeight(point) + ((BaseAI)ai).m_flyAltitudeMin + 0.5f;
		if (point.y < num)
		{
			point.y = num;
		}
		return point;
	}
}
[HarmonyPatch(typeof(BaseAI), "RandomMovementArroundPoint")]
internal static class RepositionPatch
{
	private const float Gap = 1.5f;

	private const float BackInFront = 0.7f;

	private const float MinRangeMargin = 1f;

	private const float TooCloseMargin = 0.75f;

	private static readonly Func<BaseAI, float, Vector3, float, bool, bool> moveTo = AccessTools.MethodDelegate<Func<BaseAI, float, Vector3, float, bool, bool>>(AccessTools.Method(typeof(BaseAI), "MoveTo", new Type[4]
	{
		typeof(float),
		typeof(Vector3),
		typeof(float),
		typeof(bool)
	}, (Type[])null), (object)null, true);

	private static readonly Action<BaseAI, Vector3> lookAt = AccessTools.MethodDelegate<Action<BaseAI, Vector3>>(AccessTools.Method(typeof(BaseAI), "LookAt", new Type[1] { typeof(Vector3) }, (Type[])null), (object)null, true);

	private static readonly Action<BaseAI> stopMoving = AccessTools.MethodDelegate<Action<BaseAI>>(AccessTools.Method(typeof(BaseAI), "StopMoving", (Type[])null, (Type[])null), (object)null, true);

	private static readonly Action<BaseAI> chargeStop = AccessTools.MethodDelegate<Action<BaseAI>>(AccessTools.Method(typeof(BaseAI), "ChargeStop", (Type[])null, (Type[])null), (object)null, true);

	private static bool Prefix(BaseAI __instance, float __0, Vector3 __1, Character ___m_character)
	{
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		//IL_003b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0075: Unknown result type (might be due to invalid IL or missing references)
		//IL_007a: 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_007e: Unknown result type (might be due to invalid IL or missing references)
		//IL_007f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0084: Unknown result type (might be due to invalid IL or missing references)
		//IL_0098: Unknown result type (might be due to invalid IL or missing references)
		//IL_009d: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_0238: Unknown result type (might be due to invalid IL or missing references)
		//IL_0164: Unknown result type (might be due to invalid IL or missing references)
		//IL_0178: 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_018a: Unknown result type (might be due to invalid IL or missing references)
		//IL_018f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0198: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f0: Unknown result type (might be due to invalid IL or missing references)
		//IL_0200: Unknown result type (might be due to invalid IL or missing references)
		//IL_0209: Unknown result type (might be due to invalid IL or missing references)
		//IL_020a: Unknown result type (might be due to invalid IL or missing references)
		//IL_020e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0213: Unknown result type (might be due to invalid IL or missing references)
		MonsterAI repositioner = CirclingPatch.Repositioner;
		if ((Object)(object)repositioner == (Object)null || (Object)(object)repositioner != (Object)(object)__instance)
		{
			return true;
		}
		Character targetCreature = ((BaseAI)repositioner).GetTargetCreature();
		if (!((Object)(object)targetCreature == (Object)null))
		{
			Vector3 val = ((Component)targetCreature).transform.position - __1;
			if (!(((Vector3)(ref val)).sqrMagnitude > 0.01f))
			{
				AiState aiState = AiState.Get(repositioner);
				float num = Mathf.Clamp(aiState.Settings.GroundFrontArc, 0f, 180f);
				Vector3 position = ((Component)repositioner).transform.position;
				Vector3 val2 = position - __1;
				val2.y = 0f;
				Vector3 forward = ((Component)targetCreature).transform.forward;
				float num2 = Mathf.Atan2(forward.x, forward.z) * 57.29578f;
				float num3 = Mathf.Atan2(val2.x, val2.z) * 57.29578f;
				float num4 = Mathf.DeltaAngle(num2, num3);
				float num5 = StandDistance(___m_character, targetCreature);
				if (aiState.IsCharger(___m_character))
				{
					aiState.Repositioning = false;
				}
				else if (Mathf.Abs(num4) > num)
				{
					aiState.Repositioning = true;
				}
				else if (Mathf.Abs(num4) < num * 0.7f)
				{
					aiState.Repositioning = false;
				}
				chargeStop((BaseAI)(object)repositioner);
				if (aiState.Repositioning)
				{
					float num6 = ((num4 < 0f) ? (-1f) : 1f);
					float num7 = (num2 + num6 * num * 0.5f) * ((float)Math.PI / 180f);
					Vector3 arg = __1 + new Vector3(Mathf.Sin(num7), 0f, Mathf.Cos(num7)) * (num5 + 0.5f);
					moveTo((BaseAI)(object)repositioner, __0, arg, 0.5f, arg5: true);
					return false;
				}
				float magnitude = ((Vector3)(ref val2)).magnitude;
				if (magnitude > num5 + 0.5f)
				{
					moveTo((BaseAI)(object)repositioner, __0, __1, num5, arg5: true);
				}
				else if (magnitude < num5 - 0.75f)
				{
					Vector3 val3 = ((magnitude > 0.01f) ? (val2 / magnitude) : (-((Component)repositioner).transform.forward));
					moveTo((BaseAI)(object)repositioner, __0, __1 + val3 * num5, 0.5f, arg5: true);
				}
				else
				{
					stopMoving((BaseAI)(object)repositioner);
					lookAt((BaseAI)(object)repositioner, targetCreature.GetTopPoint());
				}
				return false;
			}
		}
		return true;
	}

	private static float StandDistance(Character character, Character target)
	{
		float num = character.GetRadius() + target.GetRadius() + 1.5f;
		Humanoid val = (Humanoid)(object)((character is Humanoid) ? character : null);
		ItemData val2 = (((Object)(object)val != (Object)null) ? val.GetCurrentWeapon() : null);
		if (val2 == null)
		{
			return num;
		}
		float aiAttackRangeMin = val2.m_shared.m_aiAttackRangeMin;
		if (aiAttackRangeMin <= 0f)
		{
			return num;
		}
		num = Mathf.Max(num, aiAttackRangeMin + 1f);
		float num2 = val2.m_shared.m_aiAttackRange + target.GetRadius();
		if (num2 > aiAttackRangeMin + 1f)
		{
			num = Mathf.Min(num, num2 - 0.3f);
		}
		return num;
	}
}
[HarmonyPatch(typeof(BaseAI), "IdleMovement")]
internal static class RunUpPatch
{
	private const float Margin = 1f;

	private static readonly FieldInfo updateWeaponTimer = AccessTools.Field(typeof(MonsterAI), "m_updateWeaponTimer");

	private static readonly Func<BaseAI, float, Vector3, float, bool, bool> moveTo = AccessTools.MethodDelegate<Func<BaseAI, float, Vector3, float, bool, bool>>(AccessTools.Method(typeof(BaseAI), "MoveTo", new Type[4]
	{
		typeof(float),
		typeof(Vector3),
		typeof(float),
		typeof(bool)
	}, (Type[])null), (object)null, true);

	private static bool Prefix(BaseAI __instance, float __0, Character ___m_character)
	{
		//IL_0096: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_0109: Unknown result type (might be due to invalid IL or missing references)
		//IL_010d: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
		//IL_0102: Unknown result type (might be due to invalid IL or missing references)
		//IL_0112: Unknown result type (might be due to invalid IL or missing references)
		//IL_0121: Unknown result type (might be due to invalid IL or missing references)
		//IL_0126: 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)
		MonsterAI val = (MonsterAI)(object)((__instance is MonsterAI) ? __instance : null);
		if ((Object)(object)val == (Object)null)
		{
			return true;
		}
		Humanoid val2 = (Humanoid)(object)((___m_character is Humanoid) ? ___m_character : null);
		if ((Object)(object)val2 == (Object)null || val2.GetCurrentWeapon() != null || ___m_character.IsFlying() || ___m_character.IsSwimming() || val.m_avoidLand)
		{
			return true;
		}
		Character targetCreature = ((BaseAI)val).GetTargetCreature();
		if ((Object)(object)targetCreature == (Object)null)
		{
			return true;
		}
		AiState aiState = AiState.Get(val);
		if (aiState.Returning || !Plugin.Affects(___m_character, aiState) || !aiState.Settings.NoCircleAfterAttack)
		{
			return true;
		}
		float num = ShortestMinRange(val2);
		if (num <= 0f)
		{
			return true;
		}
		Vector3 val3 = ((Component)val).transform.position - ((Component)targetCreature).transform.position;
		val3.y = 0f;
		float magnitude = ((Vector3)(ref val3)).magnitude;
		if (magnitude >= num)
		{
			if (aiState.RunUpPending)
			{
				updateWeaponTimer.SetValue(val, 0f);
				aiState.RunUpPending = false;
			}
			return true;
		}
		Vector3 val4 = ((magnitude > 0.01f) ? (val3 / magnitude) : (-((Component)val).transform.forward));
		moveTo((BaseAI)(object)val, __0, ((Component)targetCreature).transform.position + val4 * (num + 1f), 0.5f, arg5: true);
		if (!aiState.RunUpPending)
		{
			Plugin.Debug(aiState.PrefabName + " backs off " + num + " m for a run-up");
		}
		aiState.RunUpPending = true;
		return false;
	}

	internal static float ShortestMinRange(Humanoid humanoid)
	{
		float num = float.MaxValue;
		foreach (ItemData allItem in humanoid.GetInventory().GetAllItems())
		{
			if (allItem.IsWeapon())
			{
				num = Mathf.Min(num, allItem.m_shared.m_aiAttackRangeMin);
			}
		}
		if (num != float.MaxValue)
		{
			return Mathf.Max(num, 0f);
		}
		return 0f;
	}
}
[HarmonyPatch(typeof(MonsterAI), "UpdateTarget")]
internal static class ChasePatch
{
	private const float CombatGrace = 5f;

	private const float PathCheckInterval = 1f;

	private const float BaseRadius = 12f;

	private static readonly MethodInfo setAlerted = AccessTools.Method(typeof(BaseAI), "SetAlerted", (Type[])null, (Type[])null);

	private static readonly Func<BaseAI, Vector3, bool> havePath = AccessTools.MethodDelegate<Func<BaseAI, Vector3, bool>>(AccessTools.Method(typeof(BaseAI), "HavePath", new Type[1] { typeof(Vector3) }, (Type[])null), (object)null, true);

	private static readonly List<Piece> nearbyPieces = new List<Piece>();

	private static bool IsUnreachable(MonsterAI ai, Character character, AiState state, Character target, float seconds)
	{
		//IL_003d: 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)
		if (seconds <= 0f || character.IsFlying())
		{
			return false;
		}
		if (Time.time >= state.NextPathCheck)
		{
			state.NextPathCheck = Time.time + 1f;
			if (havePath((BaseAI)(object)ai, ((Component)target).transform.position))
			{
				state.UnreachableSince = -1f;
			}
			else if (state.UnreachableSince < 0f)
			{
				state.UnreachableSince = Time.time;
			}
		}
		if (state.UnreachableSince < 0f || Time.time - state.UnreachableSince <= seconds)
		{
			return false;
		}
		if (ai.m_attackPlayerObjects)
		{
			return !NearPlayerBuildings(((Component)target).transform.position);
		}
		return true;
	}

	private static bool NearPlayerBuildings(Vector3 position)
	{
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		nearbyPieces.Clear();
		Piece.GetAllPiecesInRadius(position, 12f, nearbyPieces);
		foreach (Piece nearbyPiece in nearbyPieces)
		{
			if ((Object)(object)nearbyPiece != (Object)null && nearbyPiece.IsPlacedByPlayer())
			{
				return true;
			}
		}
		return false;
	}

	private static void Postfix(MonsterAI __instance, Character ___m_character, ref Character ___m_targetCreature, ref StaticTarget ___m_targetStatic, float ___m_timeSinceSensedTargetCreature, ref float ___m_timeSinceAttacking, ref float ___m_updateTargetTimer, float ___m_timeSinceHurt)
	{
		//IL_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_0112: Unknown result type (might be due to invalid IL or missing references)
		//IL_0118: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
		AiState aiState = AiState.Get(__instance);
		Character val = ___m_targetCreature;
		if ((Object)(object)val == (Object)null || !val.IsPlayer())
		{
			aiState.ChaseTarget = null;
			return;
		}
		if ((Object)(object)aiState.ChaseTarget != (Object)(object)val)
		{
			aiState.ChaseTarget = val;
			aiState.ChaseStart = ((Component)__instance).transform.position;
			aiState.Returning = false;
			aiState.UnreachableSince = -1f;
		}
		if (!Plugin.Affects(___m_character, aiState) || (((BaseAI)__instance).HuntPlayer() && !Plugin.AffectRaidMonsters.Value))
		{
			return;
		}
		CreatureSettings settings = aiState.Settings;
		float giveUpWhenNotSensed = settings.GiveUpWhenNotSensed;
		float giveUpWhenNoCombat = settings.GiveUpWhenNoCombat;
		float maxChaseDistance = settings.MaxChaseDistance;
		bool flag = ___m_timeSinceHurt <= 5f || ___m_timeSinceAttacking <= 5f;
		string text = null;
		if (giveUpWhenNotSensed > 0f && ___m_timeSinceSensedTargetCreature > giveUpWhenNotSensed)
		{
			text = "couldn't see or hear the target";
		}
		else if (giveUpWhenNoCombat > 0f && ___m_timeSinceAttacking > giveUpWhenNoCombat && ___m_timeSinceHurt > giveUpWhenNoCombat)
		{
			text = "no combat for " + giveUpWhenNoCombat + " s";
		}
		else if (!flag && maxChaseDistance > 0f && Vector3.Distance(((Component)__instance).transform.position, aiState.ChaseStart) > maxChaseDistance)
		{
			text = "chased farther than " + maxChaseDistance + " m";
		}
		else if (IsUnreachable(__instance, ___m_character, aiState, val, settings.GiveUpWhenUnreachable) && !flag)
		{
			text = "no path to the target";
		}
		if (text != null)
		{
			Plugin.Debug(aiState.PrefabName + " gave up the chase: " + text);
			setAlerted.Invoke(__instance, new object[1] { false });
			___m_targetCreature = null;
			___m_targetStatic = null;
			___m_timeSinceAttacking = 0f;
			___m_updateTargetTimer = Mathf.Max(settings.IgnoreAfterGiveUp, 0f);
			aiState.ChaseTarget = null;
			float num = Utils.DistanceXZ(((Component)__instance).transform.position, aiState.ChaseStart);
			if (settings.ReturnAfterGiveUp && num > 4f)
			{
				aiState.Returning = true;
				aiState.ReturnTarget = aiState.ChaseStart;
				aiState.ReturnBestDistance = num;
				aiState.ReturnProgressTime = Time.time;
			}
		}
	}
}
[HarmonyPatch(typeof(BaseAI), "IdleMovement")]
internal static class ReturnPatch
{
	internal const float ArriveDistance = 2f;

	private const float StuckTime = 4f;

	private const float Progress = 1f;

	private static readonly Func<BaseAI, float, Vector3, float, bool, bool> moveTo = AccessTools.MethodDelegate<Func<BaseAI, float, Vector3, float, bool, bool>>(AccessTools.Method(typeof(BaseAI), "MoveTo", new Type[4]
	{
		typeof(float),
		typeof(Vector3),
		typeof(float),
		typeof(bool)
	}, (Type[])null), (object)null, true);

	private static bool Prefix(BaseAI __instance, float __0)
	{
		//IL_0029: 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_0087: Unknown result type (might be due to invalid IL or missing references)
		MonsterAI val = (MonsterAI)(object)((__instance is MonsterAI) ? __instance : null);
		if ((Object)(object)val == (Object)null)
		{
			return true;
		}
		AiState aiState = AiState.Get(val);
		if (!aiState.Returning)
		{
			return true;
		}
		float num = Utils.DistanceXZ(((Component)val).transform.position, aiState.ReturnTarget);
		if (num < aiState.ReturnBestDistance - 1f)
		{
			aiState.ReturnBestDistance = num;
			aiState.ReturnProgressTime = Time.time;
		}
		if (num <= 2f || Time.time - aiState.ReturnProgressTime > 4f)
		{
			aiState.Returning = false;
			return true;
		}
		moveTo((BaseAI)(object)val, __0, aiState.ReturnTarget, 2f, arg5: false);
		return false;
	}
}
internal static class SleepPatch
{
	[HarmonyPatch(typeof(MonsterAI), "UpdateTarget")]
	private static class TrackUpdatingCreature
	{
		private static void Prefix(MonsterAI __instance)
		{
			updating = __instance;
		}

		private static void Postfix()
		{
			updating = null;
		}
	}

	[HarmonyPatch(typeof(Player), "OnTargeted")]
	private static class IgnoreTamingCreatures
	{
		private static bool Prefix()
		{
			if (!((Object)(object)updating == (Object)null) && Plugin.TamingDoesntBlockSleep.Value)
			{
				return !IsBeingTamed(updating);
			}
			return true;
		}
	}

	private static MonsterAI updating;

	private static readonly Func<Tameable, int> getTameness = AccessTools.MethodDelegate<Func<Tameable, int>>(AccessTools.Method(typeof(Tameable), "GetTameness", (Type[])null, (Type[])null), (object)null, true);

	private static bool IsBeingTamed(MonsterAI ai)
	{
		Tameable component = ((Component)ai).GetComponent<Tameable>();
		if ((Object)(object)component != (Object)null && !component.IsTamed() && !component.IsHungry())
		{
			return getTameness(component) > 0;
		}
		return false;
	}
}