Decompiled source of FiendGetConfigured v1.0.0

plugins/FiendGetConfigured/FiendGetConfigured.dll

Decompiled 5 hours ago
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using HarmonyLib;
using UnityEngine;
using UnityEngine.AI;

[assembly: CompilationRelaxations(8)]
[assembly: AssemblyFileVersion("1.0.0")]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyTitle("FiendGetConfigured")]
[assembly: AssemblyDescription("Configurable behavior for AuroraBytes The Fiend 1.1.6")]
[assembly: AssemblyCompany("TranslationLookasideBufferModding")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace FiendGetConfigured;

internal static class Patches
{
	private static bool strict = true;

	internal static void Install(Harmony harmony, bool requireKnownCounts)
	{
		MethodInfo[] array = Validate(requireKnownCounts);
		MethodInfo methodInfo = array[0];
		MethodInfo methodInfo2 = array[1];
		MethodInfo methodInfo3 = array[2];
		MethodInfo methodInfo4 = array[3];
		MethodInfo methodInfo5 = array[4];
		MethodInfo methodInfo6 = array[5];
		harmony.Patch((MethodBase)methodInfo5, (HarmonyMethod)null, H("Prepare"), H("ModernInstructions"), (HarmonyMethod)null, (HarmonyMethod)null);
		harmony.Patch((MethodBase)methodInfo6, (HarmonyMethod)null, H("Prepare"), H("ModernInstructions"), (HarmonyMethod)null, (HarmonyMethod)null);
		harmony.Patch((MethodBase)methodInfo, H("Prepare"), H("AfterAi"), H("AiInstructions"), (HarmonyMethod)null, (HarmonyMethod)null);
		harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, (HarmonyMethod)null, H("GrabInstructions"), (HarmonyMethod)null, (HarmonyMethod)null);
		harmony.Patch((MethodBase)methodInfo3, H("Contact"), (HarmonyMethod)null, H("ContactInstructions"), (HarmonyMethod)null, (HarmonyMethod)null);
		harmony.Patch((MethodBase)methodInfo4, (HarmonyMethod)null, (HarmonyMethod)null, H("RpcInstructions"), (HarmonyMethod)null, (HarmonyMethod)null);
		harmony.Patch((MethodBase)Runtime.NeedMethod(Runtime.FiendType, "TeleportServerRpc", 0), H("Teleport"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		harmony.Patch((MethodBase)Runtime.NeedMethod(Runtime.FiendType, "SetLungLightServerRpc", 0), H("Apparatus"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		harmony.Patch((MethodBase)Runtime.NeedMethod(Runtime.FiendType, "BreakDoorServerRpc", 0), H("Doors"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		harmony.Patch((MethodBase)Runtime.NeedMethod(Runtime.FiendType, "BreakerBoxBreakServerRpc", 0), H("Breaker"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		harmony.Patch((MethodBase)Runtime.NeedMethod(Runtime.FiendType, "FearedServerRpc", 2), H("Fear"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
	}

	internal static MethodInfo[] Validate(bool requireKnownCounts)
	{
		strict = requireKnownCounts;
		MethodInfo methodInfo = Runtime.NeedMethod(Runtime.FiendType, "DoAIInterval", 0);
		Type type = Runtime.FiendType.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic).Single((Type t) => t.Name.StartsWith("<Grabbing>"));
		MethodInfo methodInfo2 = Runtime.NeedMethod(type, "MoveNext", 0);
		MethodInfo methodInfo3 = Runtime.NeedMethod(Runtime.FiendType, "OnTriggerStay", 1);
		MethodInfo methodInfo4 = Runtime.NeedMethod(Runtime.FiendType, "GrabServerRpc", 1);
		MethodInfo methodInfo5 = Runtime.NeedMethod(Runtime.FiendType, "Awake", 0);
		MethodInfo methodInfo6 = Runtime.NeedMethod(Runtime.FiendType, "OnNetworkSpawn", 0);
		AiInstructions(PatchProcessor.GetOriginalInstructions((MethodBase)methodInfo, (ILGenerator)null)).ToList();
		GrabInstructions(PatchProcessor.GetOriginalInstructions((MethodBase)methodInfo2, (ILGenerator)null)).ToList();
		ContactInstructions(PatchProcessor.GetOriginalInstructions((MethodBase)methodInfo3, (ILGenerator)null)).ToList();
		RpcInstructions(PatchProcessor.GetOriginalInstructions((MethodBase)methodInfo4, (ILGenerator)null)).ToList();
		ModernInstructions(PatchProcessor.GetOriginalInstructions((MethodBase)methodInfo5, (ILGenerator)null)).ToList();
		ModernInstructions(PatchProcessor.GetOriginalInstructions((MethodBase)methodInfo6, (ILGenerator)null)).ToList();
		return new MethodInfo[6] { methodInfo, methodInfo2, methodInfo3, methodInfo4, methodInfo5, methodInfo6 };
	}

	private static HarmonyMethod H(string name)
	{
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Expected O, but got Unknown
		return new HarmonyMethod(typeof(Patches), name, (Type[])null);
	}

	private static MethodInfo Own(string name)
	{
		return AccessTools.Method(typeof(Patches), name, (Type[])null, (Type[])null);
	}

	private static MethodInfo Run(string name)
	{
		return AccessTools.Method(typeof(Runtime), name, (Type[])null, (Type[])null);
	}

	private static void Prepare(object __instance)
	{
		Runtime.Prepare(__instance);
	}

	private static void AfterAi(object __instance)
	{
		Runtime.AfterAi(__instance);
	}

	private static bool Contact(object __instance, Collider __0)
	{
		return Runtime.ContactPrefix(__instance, __0);
	}

	private static bool Teleport()
	{
		return Runtime.Current.TeleportEnabled;
	}

	private static bool Apparatus()
	{
		return Runtime.Current.ApparatusRageEnabled;
	}

	private static bool Doors()
	{
		return Runtime.Current.BreakDoorsEnabled;
	}

	private static bool Breaker()
	{
		return Runtime.Current.BreakerBoxDestructionEnabled;
	}

	private static void Fear(ref bool __0)
	{
		if (!Runtime.Current.TemporaryRageEnabled)
		{
			__0 = false;
		}
	}

	private static int Damage()
	{
		return Runtime.Current.Damage;
	}

	private static int ExecutionThreshold()
	{
		return Runtime.Current.ExecutionHealthThreshold;
	}

	private static float AttackDistance()
	{
		return Runtime.Current.AttackDistance;
	}

	private static float Cooldown()
	{
		return Runtime.Current.AttackCooldownSeconds;
	}

	private static float Knockback()
	{
		return Runtime.Current.KnockbackForce;
	}

	private static bool ApparatusRage()
	{
		return Runtime.Current.ApparatusRageEnabled;
	}

	private static int LateHour()
	{
		if (!Runtime.Current.LateGameBoostEnabled)
		{
			return int.MaxValue;
		}
		return Runtime.Current.LateGameStartHour;
	}

	private static int LateMultiplier()
	{
		return Runtime.Current.LateGameMultiplier;
	}

	private static bool IsCall(CodeInstruction code, string name)
	{
		MethodInfo methodInfo = code.operand as MethodInfo;
		if ((code.opcode == OpCodes.Call || code.opcode == OpCodes.Callvirt) && methodInfo != null)
		{
			return methodInfo.Name == name;
		}
		return false;
	}

	private static bool IsField(CodeInstruction code, string name)
	{
		FieldInfo fieldInfo = code.operand as FieldInfo;
		if (fieldInfo != null)
		{
			return fieldInfo.Name == name;
		}
		return false;
	}

	private static bool Integer(CodeInstruction code, int number)
	{
		if (code.opcode == OpCodes.Ldc_I4 || code.opcode == OpCodes.Ldc_I4_S)
		{
			return Convert.ToInt32(code.operand) == number;
		}
		if (number == 2)
		{
			return code.opcode == OpCodes.Ldc_I4_2;
		}
		return false;
	}

	private static bool Single(CodeInstruction code, float number)
	{
		if (code.opcode == OpCodes.Ldc_R4)
		{
			return (float)code.operand == number;
		}
		return false;
	}

	private static void Call(CodeInstruction instruction, MethodInfo method)
	{
		instruction.opcode = OpCodes.Call;
		instruction.operand = method;
	}

	private static void Count(string label, int actual, int expected)
	{
		if ((strict && actual != expected) || (!strict && actual < 1))
		{
			throw new InvalidOperationException(label + ": expected " + (strict ? expected.ToString() : "at least 1") + ", found " + actual + ". Refusing a partial patch.");
		}
	}

	private static List<CodeInstruction> Copy(IEnumerable<CodeInstruction> instructions)
	{
		List<CodeInstruction> list = instructions.Select((Func<CodeInstruction, CodeInstruction>)((CodeInstruction i) => new CodeInstruction(i))).ToList();
		Dictionary<OpCode, OpCode> dictionary = new Dictionary<OpCode, OpCode>();
		dictionary.Add(OpCodes.Br_S, OpCodes.Br);
		dictionary.Add(OpCodes.Brfalse_S, OpCodes.Brfalse);
		dictionary.Add(OpCodes.Brtrue_S, OpCodes.Brtrue);
		dictionary.Add(OpCodes.Beq_S, OpCodes.Beq);
		dictionary.Add(OpCodes.Bne_Un_S, OpCodes.Bne_Un);
		dictionary.Add(OpCodes.Bge_S, OpCodes.Bge);
		dictionary.Add(OpCodes.Bge_Un_S, OpCodes.Bge_Un);
		dictionary.Add(OpCodes.Bgt_S, OpCodes.Bgt);
		dictionary.Add(OpCodes.Bgt_Un_S, OpCodes.Bgt_Un);
		dictionary.Add(OpCodes.Ble_S, OpCodes.Ble);
		dictionary.Add(OpCodes.Ble_Un_S, OpCodes.Ble_Un);
		dictionary.Add(OpCodes.Blt_S, OpCodes.Blt);
		dictionary.Add(OpCodes.Blt_Un_S, OpCodes.Blt_Un);
		dictionary.Add(OpCodes.Leave_S, OpCodes.Leave);
		Dictionary<OpCode, OpCode> dictionary2 = dictionary;
		foreach (CodeInstruction item in list)
		{
			if (dictionary2.TryGetValue(item.opcode, out var value))
			{
				item.opcode = value;
			}
		}
		return list;
	}

	private static IEnumerable<CodeInstruction> ModernInstructions(IEnumerable<CodeInstruction> instructions)
	{
		List<CodeInstruction> list = Copy(instructions);
		int num = 0;
		foreach (CodeInstruction item in list)
		{
			if (item.opcode == OpCodes.Ldsfld && IsField(item, "EnableLegacyBehaviourConfig"))
			{
				item.opcode = OpCodes.Ldc_I4_0;
				item.operand = null;
				num++;
			}
		}
		Count("modern behavior", num, 1);
		return list;
	}

	private static IEnumerable<CodeInstruction> AiInstructions(IEnumerable<CodeInstruction> instructions)
	{
		//IL_0093: Unknown result type (might be due to invalid IL or missing references)
		//IL_009a: Expected O, but got Unknown
		List<CodeInstruction> list = Copy(instructions);
		int num = 0;
		int num2 = 0;
		int num3 = 0;
		int num4 = 0;
		int num5 = 0;
		int num6 = 0;
		List<CodeInstruction> list2 = new List<CodeInstruction>();
		for (int i = 0; i < list.Count; i++)
		{
			CodeInstruction val = list[i];
			if (IsCall(val, "TargetClosestPlayer"))
			{
				Call(val, Run("ChooseTarget"));
				num++;
			}
			else if (IsCall(val, "SetDestinationToPosition"))
			{
				Call(val, Run("Navigate"));
				num3++;
			}
			else if (IsCall(val, "set_speed"))
			{
				CodeInstruction val2 = new CodeInstruction(OpCodes.Ldarg_0, (object)null);
				val2.labels.AddRange(val.labels);
				val.labels.Clear();
				val2.blocks.AddRange(val.blocks);
				val.blocks.Clear();
				list2.Add(val2);
				Call(val, Run("SetSpeed"));
				num2++;
			}
			else if (Integer(val, 15) && i > 0 && IsField(list[i - 1], "hour"))
			{
				Call(val, Own("LateHour"));
				num4++;
			}
			else if (Integer(val, 2) && i > 0 && IsField(list[i - 1], "Funky") && i + 1 < list.Count && IsCall(list[i + 1], "set_Value"))
			{
				Call(val, Own("LateMultiplier"));
				num5++;
			}
			else if (val.opcode == OpCodes.Ldsfld && IsField(val, "WillRageAfterApparatusConfig"))
			{
				Call(val, Own("ApparatusRage"));
				num6++;
			}
			list2.Add(val);
		}
		Count("target acquisition", num, 5);
		Count("movement", num2, 9);
		Count("last-seen navigation", num3, 6);
		Count("late-game hour", num4, 2);
		Count("late-game factor", num5, 2);
		Count("apparatus rage", num6, 2);
		return list2;
	}

	private static IEnumerable<CodeInstruction> ContactInstructions(IEnumerable<CodeInstruction> instructions)
	{
		List<CodeInstruction> list = Copy(instructions);
		int num = 0;
		for (int i = 1; i < list.Count; i++)
		{
			if (Single(list[i], 4f) && IsCall(list[i - 1], "Distance"))
			{
				Call(list[i], Own("AttackDistance"));
				num++;
			}
		}
		Count("contact distance", num, 1);
		return list;
	}

	private static IEnumerable<CodeInstruction> GrabInstructions(IEnumerable<CodeInstruction> instructions)
	{
		List<CodeInstruction> list = Copy(instructions);
		int num = 0;
		int num2 = 0;
		int num3 = 0;
		int num4 = 0;
		for (int i = 1; i < list.Count; i++)
		{
			if (Integer(list[i], 50) && IsField(list[i - 1], "health"))
			{
				Call(list[i], Own("ExecutionThreshold"));
				num++;
			}
			if (IsCall(list[i], "StartCooldown"))
			{
				int num5 = i - 1;
				while (num5 >= Math.Max(0, i - 4) && !Single(list[num5], 1f))
				{
					num5--;
				}
				if (num5 < Math.Max(0, i - 4))
				{
					throw new InvalidOperationException("Original hit cooldown literal not found.");
				}
				Call(list[num5], Own("Cooldown"));
				num3++;
			}
			if (!IsCall(list[i], "DamagePlayer"))
			{
				continue;
			}
			int num6 = i - 1;
			while (num6 >= Math.Max(0, i - 16) && !Integer(list[num6], 50))
			{
				num6--;
			}
			if (num6 < Math.Max(0, i - 16))
			{
				throw new InvalidOperationException("Original Fiend damage literal not found.");
			}
			Call(list[num6], Own("Damage"));
			num2++;
			for (num6 = i + 1; num6 < Math.Min(list.Count - 1, i + 40); num6++)
			{
				if (Single(list[num6], 30f) && IsCall(list[num6 + 1], "op_Multiply"))
				{
					Call(list[num6], Own("Knockback"));
					num4++;
					break;
				}
			}
		}
		Count("execution threshold", num, 1);
		Count("normal damage", num2, 1);
		Count("hit cooldown", num3, 1);
		Count("normal-hit knockback", num4, 1);
		return list;
	}

	private static IEnumerable<CodeInstruction> RpcInstructions(IEnumerable<CodeInstruction> instructions)
	{
		//IL_0063: Unknown result type (might be due to invalid IL or missing references)
		//IL_006a: Expected O, but got Unknown
		List<CodeInstruction> list = new List<CodeInstruction>();
		int num = 0;
		foreach (CodeInstruction item in Copy(instructions))
		{
			if (IsCall(item, "GrabClientRpc"))
			{
				Type parameterType = ((MethodInfo)item.operand).GetParameters()[0].ParameterType;
				if (!parameterType.IsValueType)
				{
					throw new InvalidOperationException("Expected network reference struct.");
				}
				CodeInstruction val = new CodeInstruction(OpCodes.Box, (object)parameterType);
				val.labels.AddRange(item.labels);
				item.labels.Clear();
				val.blocks.AddRange(item.blocks);
				item.blocks.Clear();
				list.Add(val);
				Call(item, Run("SendGrab"));
				num++;
			}
			list.Add(item);
		}
		Count("server attack gate", num, 1);
		return list;
	}
}
[BepInPlugin("TranslationLookasideBufferModding.FiendGetConfigured", "FiendGetConfigured", "1.0.0")]
[BepInDependency("com.TheFiend", "1.1.6")]
public sealed class Plugin : BaseUnityPlugin
{
	public const string Id = "TranslationLookasideBufferModding.FiendGetConfigured";

	private Harmony harmony;

	private void Awake()
	{
		//IL_0408: Unknown result type (might be due to invalid IL or missing references)
		//IL_0412: Expected O, but got Unknown
		bool value = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable FiendGetConfigured. Restart the game after changing any setting. Install the same version and config on ALL players.").Value;
		Settings s = new Settings();
		s.WalkSpeed = Float("Movement", "WalkSpeed", 3f, 0f, 100f, "Movement speed while roaming or walking.");
		s.StalkSpeed = Float("Movement", "StalkSpeed", 1f, 0f, 100f, "Movement speed in the Fiend's seeking/stalking mode.");
		s.ChaseSpeed = Float("Movement", "ChaseSpeed", 5.5f, 0f, 100f, "Movement speed during a normal chase.");
		s.RageSpeed = Float("Movement", "RageSpeed", 7f, 0f, 100f, "Movement speed during a rage chase.");
		s.Damage = Integer("Combat", "Damage", 25, 0, 1000, "Damage of a normal hit. The execution threshold is configured separately.");
		s.ExecutionHealthThreshold = Integer("Combat", "ExecutionHealthThreshold", 25, -1, 100, "Use the original lethal grab when the victim has at most this many HP BEFORE a hit. -1 disables the execution grab; normal damage can still kill.");
		s.AttackDistance = Float("Combat", "AttackDistance", 2f, 0.1f, 10f, "Maximum center-to-center contact attack distance in meters. The original contact collider is still required; increasing this does not enlarge it.");
		s.AttackCooldownSeconds = Float("Combat", "AttackCooldownSeconds", 2f, 0.1f, 60f, "Minimum seconds between accepted attacks. Also replaces the original normal-hit cooldown. An execution animation may take longer.");
		s.KnockbackForce = Float("Combat", "KnockbackForce", 15f, 0f, 100f, "Knockback of a normal hit. Does not change death-body effects.");
		s.RequireLineOfSight = ((BaseUnityPlugin)this).Config.Bind<bool>("Perception", "RequireLineOfSight", true, "Require visibility when acquiring a player and before a contact attack. The Fiend searches the LAST seen position during target memory.").Value;
		s.DetectionRange = Float("Perception", "DetectionRange", 30f, 0.5f, 40f, "Maximum distance for acquiring a player in meters. The v81 base game's target selector has a 40-meter visibility limit.");
		s.FieldOfViewDegrees = Float("Perception", "FieldOfViewDegrees", 140f, 10f, 360f, "Full field of view used for player acquisition.");
		s.TargetMemorySeconds = Float("Perception", "TargetMemorySeconds", 8f, 0f, 120f, "How long to search the last seen position after losing visibility. 0 forgets immediately. Dead players and players leaving the valid area are forgotten.");
		s.TeleportEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Abilities", "TeleportEnabled", false, "Allow the original random player teleport ability.").Value;
		s.ApparatusRageEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Abilities", "ApparatusRageEnabled", false, "Allow rage when the apparatus is removed. Overrides the original Fiend setting while this addon is enabled.").Value;
		s.TemporaryRageEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Abilities", "TemporaryRageEnabled", false, "Allow temporary rage triggered through the original feared/seeking behavior. Flashlight fear/stun itself is preserved.").Value;
		s.BreakDoorsEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Abilities", "BreakDoorsEnabled", true, "Allow the original door-breaking ability. When false, closed doors can obstruct the Fiend.").Value;
		s.BreakerBoxDestructionEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Abilities", "BreakerBoxDestructionEnabled", true, "Allow targeting and destroying the breaker box.").Value;
		s.LateGameBoostEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("LateGame", "Enabled", false, "Allow the original late-game speed AND random-ability frequency multiplier.").Value;
		s.LateGameStartHour = Integer("LateGame", "StartHour", 15, 0, 24, "Original TimeOfDay.hour value that activates the boost. This is the mod's original game-time field.");
		s.LateGameMultiplier = Integer("LateGame", "Multiplier", 2, 1, 5, "Multiplier applied to the configured movement speeds and original random-ability frequency while boosted.");
		if (!value)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)"FiendGetConfigured is disabled.");
			return;
		}
		harmony = new Harmony("TranslationLookasideBufferModding.FiendGetConfigured");
		try
		{
			if (!Chainloader.PluginInfos.TryGetValue("com.TheFiend", out var value2) || value2.Metadata.Version.ToString() != "1.1.6")
			{
				throw new NotSupportedException("This release supports AuroraBytes-The_Fiend 1.1.6 exactly. No patches were applied.");
			}
			Runtime.SettingsProvider = () => s;
			Runtime.Warning = delegate(string message)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)message);
			};
			Runtime.Initialize(AccessTools.TypeByName("TheFiendAI"));
			Patches.Install(harmony, requireKnownCounts: true);
			((BaseUnityPlugin)this).Logger.LogInfo((object)("FiendGetConfigured 1.0.0 active for The Fiend 1.1.6: modern behavior, damage=" + s.Damage + ", chase=" + s.ChaseSpeed + ", sight=" + s.RequireLineOfSight + ". Same config required on all players; restart to change settings."));
		}
		catch (Exception ex)
		{
			harmony.UnpatchSelf();
			((BaseUnityPlugin)this).Logger.LogError((object)("FiendGetConfigured disabled because compatibility checks failed. The original Fiend remains active: " + ex));
		}
	}

	private float Float(string section, string key, float value, float min, float max, string description)
	{
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Expected O, but got Unknown
		float value2 = ((BaseUnityPlugin)this).Config.Bind<float>(section, key, value, new ConfigDescription(description + " Restart required.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(min, max), new object[0])).Value;
		if (!float.IsNaN(value2) && !float.IsInfinity(value2))
		{
			return value2;
		}
		return value;
	}

	private int Integer(string section, string key, int value, int min, int max, string description)
	{
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Expected O, but got Unknown
		return ((BaseUnityPlugin)this).Config.Bind<int>(section, key, value, new ConfigDescription(description + " Restart required.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(min, max), new object[0])).Value;
	}

	private void OnDestroy()
	{
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
	}
}
internal sealed class TrackingState
{
	internal Component LastTarget;

	internal Vector3 LastPosition;

	internal float LastSeen;

	internal bool UsingMemory;

	internal float NextAttack = float.NegativeInfinity;
}
internal static class Runtime
{
	internal static Func<Settings> SettingsProvider = () => new Settings();

	internal static Func<float> Clock = () => Time.time;

	internal static Action<string> Warning;

	internal static Type FiendType;

	internal static Type PlayerType;

	internal static MethodInfo ChooseOriginal;

	internal static MethodInfo NavigateOriginal;

	internal static MethodInfo VisibilityOriginal;

	internal static MethodInfo TargetableOriginal;

	internal static MethodInfo GrabClient;

	internal static MethodInfo ResolveReference;

	internal static FieldInfo Target;

	internal static FieldInfo Agent;

	internal static FieldInfo Legacy;

	internal static FieldInfo State;

	internal static FieldInfo Seeking;

	internal static FieldInfo Rage;

	internal static FieldInfo Funky;

	internal static FieldInfo Apparatus;

	internal static FieldInfo Look;

	internal static FieldInfo Breaker;

	internal static FieldInfo Dead;

	internal static FieldInfo Controlled;

	private static PropertyInfo isServer;

	private static ConditionalWeakTable<object, TrackingState> tracking = new ConditionalWeakTable<object, TrackingState>();

	internal static Settings Current => SettingsProvider();

	internal static void Initialize(Type type)
	{
		if (type == null)
		{
			throw new MissingMemberException("TheFiendAI was not found.");
		}
		FiendType = type;
		Target = NeedField(type, "targetPlayer");
		PlayerType = Target.FieldType;
		Agent = NeedField(type, "agent");
		Legacy = NeedField(type, "enableLegacy");
		State = NeedField(type, "StateOfMind");
		Seeking = NeedField(type, "Seeking");
		Rage = NeedField(type, "RageMode");
		Funky = NeedField(type, "Funky");
		Apparatus = NeedField(type, "LungApparatusWillRage");
		Look = NeedField(type, "TargetLook");
		Breaker = NeedField(type, "breakerBox");
		Dead = NeedField(PlayerType, "isPlayerDead");
		Controlled = NeedField(PlayerType, "isPlayerControlled");
		ChooseOriginal = NeedMethod(type, "TargetClosestPlayer", 6);
		NavigateOriginal = NeedMethod(type, "SetDestinationToPosition", 2);
		VisibilityOriginal = NeedMethod(type, "CheckLineOfSightForPosition", 5);
		TargetableOriginal = NeedMethod(type, "PlayerIsTargetable", 4);
		GrabClient = NeedMethod(type, "GrabClientRpc", 1);
		ResolveReference = NeedMethod(GrabClient.GetParameters()[0].ParameterType, "TryGet", 2);
		isServer = AccessTools.Property(type, "IsServer");
		if (isServer == null)
		{
			throw new MissingMemberException("IsServer is missing.");
		}
		tracking = new ConditionalWeakTable<object, TrackingState>();
	}

	internal static FieldInfo NeedField(Type type, string name)
	{
		FieldInfo fieldInfo = AccessTools.Field(type, name);
		if (fieldInfo == null)
		{
			throw new MissingFieldException(type.FullName, name);
		}
		return fieldInfo;
	}

	internal static MethodInfo NeedMethod(Type type, string name, int parameters)
	{
		Type type2 = type;
		while (type2 != null)
		{
			MethodInfo[] methods = type2.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo methodInfo in methods)
			{
				if (methodInfo.Name == name && methodInfo.GetParameters().Length == parameters && !methodInfo.IsGenericMethodDefinition)
				{
					return methodInfo;
				}
			}
			type2 = type2.BaseType;
		}
		throw new MissingMethodException(type.FullName, name);
	}

	internal static object NetworkValue(FieldInfo field, object instance)
	{
		object value = field.GetValue(instance);
		return value?.GetType().GetProperty("Value").GetValue(value, null);
	}

	internal static void SetNetworkValue(FieldInfo field, object instance, object value)
	{
		object value2 = field.GetValue(instance);
		value2?.GetType().GetProperty("Value").SetValue(value2, value, null);
	}

	internal static bool Server(object instance)
	{
		return (bool)isServer.GetValue(instance, null);
	}

	internal static void Prepare(object instance)
	{
		Legacy.SetValue(instance, false);
		if (!Current.BreakerBoxDestructionEnabled)
		{
			Breaker.SetValue(instance, null);
		}
		if (Server(instance))
		{
			bool apparatusRageEnabled = Current.ApparatusRageEnabled;
			if (!object.Equals(NetworkValue(Apparatus, instance), apparatusRageEnabled))
			{
				SetNetworkValue(Apparatus, instance, apparatusRageEnabled);
			}
		}
	}

	private static bool Valid(object instance, Component player)
	{
		if ((Object)(object)player != (Object)null && player.gameObject.activeInHierarchy && !(bool)Dead.GetValue(player) && (bool)Controlled.GetValue(player))
		{
			return (bool)TargetableOriginal.Invoke(instance, new object[4] { player, false, false, true });
		}
		return false;
	}

	internal static bool ChooseTarget(object instance, float buffer, bool sight, float width, bool ground, bool path, bool mineshaft)
	{
		//IL_009d: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a4: Expected O, but got Unknown
		//IL_00b6: 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_00cc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
		Settings current = Current;
		TrackingState orCreateValue = tracking.GetOrCreateValue(instance);
		bool flag = (bool)ChooseOriginal.Invoke(instance, new object[6]
		{
			current.RequireLineOfSight ? 0f : buffer,
			current.RequireLineOfSight,
			current.FieldOfViewDegrees / 2f,
			ground,
			path,
			mineshaft
		});
		object? value = Target.GetValue(instance);
		Component val = (Component)((value is Component) ? value : null);
		Component val2 = (Component)instance;
		if (flag && Valid(instance, val))
		{
			Vector3 val3 = val.transform.position - val2.transform.position;
			if (Policy.IsInRange(((Vector3)(ref val3)).sqrMagnitude, current.DetectionRange))
			{
				orCreateValue.LastTarget = val;
				orCreateValue.LastPosition = val.transform.position;
				orCreateValue.LastSeen = Clock();
				orCreateValue.UsingMemory = false;
				return true;
			}
		}
		if (current.RequireLineOfSight && Valid(instance, orCreateValue.LastTarget) && Policy.CanRemember(Clock(), orCreateValue.LastSeen, current.TargetMemorySeconds))
		{
			Target.SetValue(instance, orCreateValue.LastTarget);
			orCreateValue.UsingMemory = true;
			return true;
		}
		orCreateValue.LastTarget = null;
		orCreateValue.UsingMemory = false;
		Target.SetValue(instance, null);
		return false;
	}

	internal static Vector3 Destination(object instance, Vector3 requested)
	{
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		//IL_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_003b: 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)
		if (tracking.TryGetValue(instance, out var value) && value.UsingMemory && (Object)(object)value.LastTarget != (Object)null)
		{
			Vector3 val = requested - value.LastTarget.transform.position;
			if (((Vector3)(ref val)).sqrMagnitude < 0.0001f)
			{
				return value.LastPosition;
			}
		}
		return requested;
	}

	internal static bool Navigate(object instance, Vector3 destination, bool checkPath)
	{
		//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 (bool)NavigateOriginal.Invoke(instance, new object[2]
		{
			Destination(instance, destination),
			checkPath
		});
	}

	internal static void AfterAi(object instance)
	{
		if (tracking.TryGetValue(instance, out var value) && value.UsingMemory)
		{
			Look.SetValue(instance, null);
		}
	}

	internal static void SetSpeed(NavMeshAgent agent, float requested, object instance)
	{
		agent.speed = Policy.Speed(Current, requested, (int)NetworkValue(State, instance), (bool)NetworkValue(Seeking, instance), (bool)NetworkValue(Rage, instance), (int)NetworkValue(Funky, instance));
	}

	internal static bool CanContact(object instance, Component player)
	{
		//IL_0017: 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_0027: Unknown result type (might be due to invalid IL or missing references)
		//IL_002c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0031: 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_00a7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_008b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
		if (!Valid(instance, player))
		{
			return false;
		}
		Settings current = Current;
		Vector3 val = player.transform.position - ((Component)instance).transform.position;
		if (!Policy.IsInRange(((Vector3)(ref val)).sqrMagnitude, current.AttackDistance))
		{
			return false;
		}
		if (!current.RequireLineOfSight)
		{
			return true;
		}
		FieldInfo fieldInfo = AccessTools.Field(PlayerType, "gameplayCamera");
		Camera val2 = (Camera)((fieldInfo == null) ? null : /*isinst with value type is only supported in some contexts*/);
		Vector3 val3 = (((Object)(object)val2 == (Object)null) ? (player.transform.position + Vector3.up * 1.5f) : ((Component)val2).transform.position);
		return (bool)VisibilityOriginal.Invoke(instance, new object[5]
		{
			val3,
			180f,
			Math.Max(1, Mathf.CeilToInt(current.AttackDistance + 3f)),
			-1f,
			null
		});
	}

	internal static bool ContactPrefix(object instance, Collider collider)
	{
		if ((Object)(object)collider == (Object)null)
		{
			return false;
		}
		Legacy.SetValue(instance, false);
		Component component = ((Component)collider).gameObject.GetComponent(PlayerType);
		if (!((Object)(object)component == (Object)null))
		{
			return CanContact(instance, component);
		}
		return true;
	}

	internal static void SendGrab(object instance, object reference)
	{
		object[] array = new object[2];
		object[] array2 = array;
		if (!(bool)ResolveReference.Invoke(reference, array2))
		{
			return;
		}
		object obj = array2[0];
		Component val = (Component)((obj is Component) ? obj : null);
		Component player = (((Object)(object)val == (Object)null) ? null : val.GetComponent(PlayerType));
		if (Server(instance) && CanContact(instance, player))
		{
			TrackingState orCreateValue = tracking.GetOrCreateValue(instance);
			if (Policy.CanStartAttack(Clock(), ref orCreateValue.NextAttack, Current.AttackCooldownSeconds))
			{
				GrabClient.Invoke(instance, new object[1] { reference });
			}
		}
	}
}
internal sealed class Settings
{
	internal float WalkSpeed = 3f;

	internal float StalkSpeed = 1f;

	internal float ChaseSpeed = 5.5f;

	internal float RageSpeed = 7f;

	internal int Damage = 25;

	internal int ExecutionHealthThreshold = 25;

	internal float AttackDistance = 2f;

	internal float AttackCooldownSeconds = 2f;

	internal float KnockbackForce = 15f;

	internal bool RequireLineOfSight = true;

	internal float DetectionRange = 30f;

	internal float FieldOfViewDegrees = 140f;

	internal float TargetMemorySeconds = 8f;

	internal bool TeleportEnabled;

	internal bool ApparatusRageEnabled;

	internal bool TemporaryRageEnabled;

	internal bool BreakDoorsEnabled = true;

	internal bool BreakerBoxDestructionEnabled = true;

	internal bool LateGameBoostEnabled;

	internal int LateGameStartHour = 15;

	internal int LateGameMultiplier = 2;
}
internal static class Policy
{
	internal static bool CanRemember(float now, float lastSeen, float duration)
	{
		if (duration > 0f && now >= lastSeen)
		{
			return now - lastSeen <= duration;
		}
		return false;
	}

	internal static bool IsInRange(float squaredDistance, float range)
	{
		if (!float.IsNaN(squaredDistance) && squaredDistance >= 0f)
		{
			return squaredDistance <= range * range;
		}
		return false;
	}

	internal static bool CanStartAttack(float now, ref float nextAttack, float cooldown)
	{
		if (now < nextAttack)
		{
			return false;
		}
		nextAttack = now + cooldown;
		return true;
	}

	internal static float Speed(Settings s, float requested, int state, bool stalking, bool rage, int factor)
	{
		if (requested <= 0f)
		{
			return requested;
		}
		float num = (stalking ? s.StalkSpeed : ((state != 2) ? s.WalkSpeed : (rage ? s.RageSpeed : s.ChaseSpeed)));
		return num * (float)((!s.LateGameBoostEnabled) ? 1 : Math.Max(1, factor));
	}
}
internal static class VersionInfo
{
	internal const string Version = "1.0.0";
}