Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of FiendGetConfigured v1.0.0
plugins/FiendGetConfigured/FiendGetConfigured.dll
Decompiled 5 hours agousing 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"; }