using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using Unity.Netcode; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("FairGiantsFixed")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("FairGiantsFixed")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("b4551337-cf5e-4171-8774-cc6f7476b59a")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("1.0.0.0")] namespace FairGiantsFixed; public enum StealthDecayMode { Never, Solo, Always } [BepInPlugin("taylor.fairgiantsfixed", "Fair Giants Fixed", "0.2.1")] public sealed class Plugin : BaseUnityPlugin { internal static ManualLogSource Log; internal static ConfigEntry VisionDivisor; internal static ConfigEntry AffectFoggy; internal static ConfigEntry AffectSnowy; internal static ConfigEntry SnowWeatherNames; internal static ConfigEntry StealthDecaysWhen; internal static ConfigEntry PassiveStealthDecay; internal static ConfigEntry EnhancedAntiCamp; internal static ConfigEntry RandomWander; internal static ConfigEntry ShipCampRadius; internal static ConfigEntry ShipEscapeExtraDistance; internal static ConfigEntry ShipEscapeReassertInterval; internal static ConfigEntry ShipEscapeStuckTime; internal static ConfigEntry ShipEscapeMaxTime; private Harmony _harmony; private void Awake() { //IL_02f0: Unknown result type (might be due to invalid IL or missing references) //IL_02fa: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; VisionDivisor = ((BaseUnityPlugin)this).Config.Bind("Vision", "VisionDivisor", 3f, "Divides Forest Giant vision range during affected weather."); AffectFoggy = ((BaseUnityPlugin)this).Config.Bind("Vision", "AffectFoggy", true, "Reduce Forest Giant vision during foggy weather."); AffectSnowy = ((BaseUnityPlugin)this).Config.Bind("Vision", "AffectSnowy", true, "Reduce Forest Giant vision during snowy weather."); SnowWeatherNames = ((BaseUnityPlugin)this).Config.Bind("Vision", "SnowWeatherNames", "snow,snowy,blizzard", "Comma-separated weather names treated as snowy."); StealthDecaysWhen = ((BaseUnityPlugin)this).Config.Bind("Aggro", "StealthDecaysWhen", StealthDecayMode.Solo, "When stealth meters should passively decay while the giant sees nobody."); PassiveStealthDecay = ((BaseUnityPlugin)this).Config.Bind("Aggro", "PassiveStealthDecay", 0.2f, "Stealth memory lost per second while no players are visible."); EnhancedAntiCamp = ((BaseUnityPlugin)this).Config.Bind("Ship", "EnhancedAntiCamp", true, "Makes giants commit to leaving the ship area after losing a player nearby."); RandomWander = ((BaseUnityPlugin)this).Config.Bind("Ship", "RandomWander", true, "Choose randomly from distant AI nodes instead of always using the farthest node."); ShipCampRadius = ((BaseUnityPlugin)this).Config.Bind("Ship", "ShipCampRadius", 40f, "Distance from the ship within which anti-camp can activate."); ShipEscapeExtraDistance = ((BaseUnityPlugin)this).Config.Bind("Ship", "ShipEscapeExtraDistance", 20f, "Additional distance beyond ShipCampRadius the giant must reach."); ShipEscapeReassertInterval = ((BaseUnityPlugin)this).Config.Bind("Ship", "ShipEscapeReassertInterval", 0.2f, "How often the mod forces the escape destination while anti-camp is active."); ShipEscapeStuckTime = ((BaseUnityPlugin)this).Config.Bind("Ship", "ShipEscapeStuckTime", 4f, "How long the giant may fail to make meaningful progress before a new escape node is chosen."); ShipEscapeMaxTime = ((BaseUnityPlugin)this).Config.Bind("Ship", "ShipEscapeMaxTime", 30f, "Emergency maximum duration of an anti-camp escape."); ((BaseUnityPlugin)this).Logger.LogInfo((object)"======================================"); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Fair Giants Fixed 0.2.1 LOADED"); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Vision + Aggro + AUTHORITATIVE Ship Anti-Camp"); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Vision divisor: " + VisionDivisor.Value)); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Stealth decay mode: " + StealthDecaysWhen.Value)); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Passive stealth decay: " + PassiveStealthDecay.Value)); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Enhanced anti-camp: " + EnhancedAntiCamp.Value)); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Ship escape target distance: " + (ShipCampRadius.Value + ShipEscapeExtraDistance.Value))); ((BaseUnityPlugin)this).Logger.LogInfo((object)"======================================"); try { _harmony = new Harmony("taylor.fairgiantsfixed.harmony"); _harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"[FGF] Harmony patches applied."); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)"[FGF] Harmony patching failed:"); ((BaseUnityPlugin)this).Logger.LogError((object)ex); } } public static int GetGiantVisionRange(int vanillaRange) { try { string currentWeatherName = GetCurrentWeatherName(); if (!ShouldReduceVision(currentWeatherName)) { VisionLogging.LogNormal(currentWeatherName, vanillaRange); return vanillaRange; } float num = VisionDivisor.Value; if (num < 1f) { num = 1f; } int num2 = Mathf.Max(1, Mathf.RoundToInt((float)vanillaRange / num)); VisionLogging.LogReduced(currentWeatherName, vanillaRange, num, num2); return num2; } catch (Exception ex) { Log.LogWarning((object)("[FGF] Vision calculation failed. Using vanilla range. " + ex.Message)); return vanillaRange; } } private static string GetCurrentWeatherName() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)TimeOfDay.Instance == (Object)null) { return ""; } object obj = TimeOfDay.Instance.currentLevelWeather; if (obj == null) { return ""; } return obj.ToString(); } catch { return ""; } } private static bool ShouldReduceVision(string weatherName) { if (string.IsNullOrWhiteSpace(weatherName)) { return false; } string text = weatherName.Trim().ToLowerInvariant(); if (AffectFoggy.Value && text.Contains("fog")) { return true; } if (AffectSnowy.Value && MatchesConfiguredSnowWeather(text)) { return true; } return false; } private static bool MatchesConfiguredSnowWeather(string normalizedWeather) { string value = SnowWeatherNames.Value; if (string.IsNullOrWhiteSpace(value)) { return false; } string[] array = value.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = array; foreach (string text in array2) { string text2 = text.Trim().ToLowerInvariant(); if (text2.Length > 0 && normalizedWeather.Contains(text2)) { return true; } } return false; } } internal static class VisionLogging { private static float _nextNormalLogTime; private static float _nextReducedLogTime; internal static void LogNormal(string weather, int range) { if (!(Time.realtimeSinceStartup < _nextNormalLogTime)) { _nextNormalLogTime = Time.realtimeSinceStartup + 10f; Plugin.Log.LogInfo((object)("[FGF] Giant vision | Weather=" + weather + " | Range=" + range + " (vanilla)")); } } internal static void LogReduced(string weather, int vanillaRange, float divisor, int reducedRange) { if (!(Time.realtimeSinceStartup < _nextReducedLogTime)) { _nextReducedLogTime = Time.realtimeSinceStartup + 5f; Plugin.Log.LogInfo((object)"[FGF] FOREST GIANT VISION REDUCED"); Plugin.Log.LogInfo((object)("[FGF] Weather=" + weather + " | Vanilla=" + vanillaRange + " | Effective=" + reducedRange + " | Divisor=" + divisor.ToString("0.00"))); } } } [HarmonyPatch(typeof(ForestGiantAI), "LookForPlayers")] internal static class ForestGiantVisionPatch { [HarmonyTranspiler] private static IEnumerable Transpiler(IEnumerable instructions) { //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Expected O, but got Unknown List list = new List(instructions); try { MethodInfo methodInfo = AccessTools.Method(typeof(EnemyAI), "GetAllPlayersInLineOfSightNonAlloc", new Type[5] { typeof(float), typeof(int), typeof(Transform), typeof(float), typeof(int) }, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(Plugin), "GetGiantVisionRange", (Type[])null, (Type[])null); if (methodInfo == null || methodInfo2 == null) { Plugin.Log.LogError((object)"[FGF] Vision dependencies could not be found."); return list; } int num = FindVisionRangeInstruction(list, methodInfo); if (num < 0) { Plugin.Log.LogError((object)"[FGF] VISION PATCH NOT APPLIED."); return list; } if (num + 1 < list.Count && list[num + 1].opcode == OpCodes.Call && object.Equals(list[num + 1].operand, methodInfo2)) { return list; } list.Insert(num + 1, new CodeInstruction(OpCodes.Call, (object)methodInfo2)); Plugin.Log.LogInfo((object)"[FGF] FOREST GIANT VISION PATCH APPLIED"); Plugin.Log.LogInfo((object)("[FGF] Matched modern LOS range at IL index " + num + ".")); } catch (Exception ex) { Plugin.Log.LogError((object)"[FGF] Vision transpiler failed:"); Plugin.Log.LogError((object)ex); } return list; } private static int FindVisionRangeInstruction(List codes, MethodInfo losMethod) { FieldInfo fieldInfo = AccessTools.Field(typeof(EnemyAI), "eye"); if (fieldInfo == null) { return -1; } for (int i = 1; i < codes.Count - 4; i++) { if (!LoadsInteger(codes[i], 70) || !LoadsFloat(codes[i - 1], 50f) || codes[i + 1].opcode != OpCodes.Ldarg_0 || codes[i + 2].opcode != OpCodes.Ldfld || !object.Equals(codes[i + 2].operand, fieldInfo) || !LoadsFloat(codes[i + 3], 3f)) { continue; } int num = Math.Min(codes.Count, i + 12); for (int j = i + 4; j < num; j++) { if ((codes[j].opcode == OpCodes.Call || codes[j].opcode == OpCodes.Callvirt) && object.Equals(codes[j].operand, losMethod)) { return i; } } } return -1; } private static bool LoadsInteger(CodeInstruction code, int value) { if (code.opcode != OpCodes.Ldc_I4 && code.opcode != OpCodes.Ldc_I4_S) { return false; } try { return Convert.ToInt32(code.operand) == value; } catch { return false; } } private static bool LoadsFloat(CodeInstruction code, float value) { if (code.opcode != OpCodes.Ldc_R4 || code.operand == null) { return false; } try { return Math.Abs(Convert.ToSingle(code.operand) - value) < 0.001f; } catch { return false; } } } [HarmonyPatch(typeof(ForestGiantAI), "LookForPlayers")] internal static class ForestGiantAggroPatch { internal sealed class AggroState { internal float[] Before; } private static bool _loggedWorking; private static bool _losFallbackLogged; private static float _nextDecayLogTime; [HarmonyPrefix] private static void Prefix(ForestGiantAI __instance, ref AggroState __state) { __state = new AggroState(); if ((Object)(object)__instance == (Object)null || !ShouldUsePassiveDecay()) { return; } try { float[] fieldValue = ReflectionUtil.GetFieldValue(__instance, "playerStealthMeters"); if (fieldValue != null) { __state.Before = new float[fieldValue.Length]; Array.Copy(fieldValue, __state.Before, fieldValue.Length); } } catch { } } [HarmonyPostfix] private static void Postfix(ForestGiantAI __instance, AggroState __state) { if ((Object)(object)__instance == (Object)null) { return; } try { ShipAntiCamp.Update(__instance); if (__state == null || __state.Before == null || !ShouldUsePassiveDecay() || GiantCanCurrentlySeeAnyPlayer(__instance)) { return; } float[] fieldValue = ReflectionUtil.GetFieldValue(__instance, "playerStealthMeters"); if (fieldValue != null) { float num = Mathf.Max(0f, Plugin.PassiveStealthDecay.Value) * Time.deltaTime; int num2 = Math.Min(fieldValue.Length, __state.Before.Length); for (int i = 0; i < num2; i++) { fieldValue[i] = Mathf.Clamp01(__state.Before[i] - num); } if (!_loggedWorking) { _loggedWorking = true; Plugin.Log.LogInfo((object)"[FGF] AGGRO DECAY ACTIVE"); Plugin.Log.LogInfo((object)"[FGF] Stealth memory now decays when no players are visible."); } if (Time.realtimeSinceStartup >= _nextDecayLogTime) { _nextDecayLogTime = Time.realtimeSinceStartup + 10f; Plugin.Log.LogInfo((object)("[FGF] Passive stealth decay running at " + Plugin.PassiveStealthDecay.Value + "/second.")); } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[FGF] Aggro patch error: " + ex.Message)); } } private static bool ShouldUsePassiveDecay() { switch (Plugin.StealthDecaysWhen.Value) { case StealthDecayMode.Never: return false; case StealthDecayMode.Always: return true; default: try { if ((Object)(object)StartOfRound.Instance == (Object)null || StartOfRound.Instance.allPlayerScripts == null) { return false; } int num = 0; PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; foreach (PlayerControllerB val in allPlayerScripts) { if ((Object)(object)val != (Object)null && val.isPlayerControlled) { num++; } } return num <= 1; } catch { return false; } } } private static bool GiantCanCurrentlySeeAnyPlayer(ForestGiantAI giant) { try { MethodInfo methodInfo = AccessTools.Method(typeof(EnemyAI), "GetAllPlayersInLineOfSightNonAlloc", new Type[5] { typeof(float), typeof(int), typeof(Transform), typeof(float), typeof(int) }, (Type[])null); FieldInfo fieldInfo = AccessTools.Field(typeof(EnemyAI), "eye"); FieldInfo fieldInfo2 = AccessTools.Field(typeof(StartOfRound), "collidersRoomDefaultAndFoliage"); if (methodInfo != null && fieldInfo != null && fieldInfo2 != null && (Object)(object)StartOfRound.Instance != (Object)null) { object? value = fieldInfo.GetValue(giant); Transform val = (Transform)((value is Transform) ? value : null); int num = Convert.ToInt32(fieldInfo2.GetValue(StartOfRound.Instance)); int giantVisionRange = Plugin.GetGiantVisionRange(70); object obj = methodInfo.Invoke(giant, new object[5] { 50f, giantVisionRange, val, 3f, num }); if (obj != null) { return Convert.ToInt32(obj) > 0; } } } catch { } if (!_losFallbackLogged) { _losFallbackLogged = true; Plugin.Log.LogWarning((object)"[FGF] Aggro LOS helper unavailable; using chasingPlayerInLOS fallback."); } return ReflectionUtil.GetFieldValue(giant, "chasingPlayerInLOS"); } } internal static class ShipAntiCamp { internal sealed class EscapeState { internal Vector3 Target; internal float Started; internal float NextReassert; internal float LastProgressTime; internal Vector3 LastPosition; internal float LastDistanceFromShip; internal int RetargetCount; } private static readonly Dictionary ActiveEscapes = new Dictionary(); private static bool _nodeFailureLogged; private static bool _destinationFailureLogged; internal static void Update(ForestGiantAI giant) { if (!Plugin.EnhancedAntiCamp.Value || (Object)(object)giant == (Object)null) { return; } try { if (!((NetworkBehaviour)giant).IsServer) { return; } Transform shipTransform = GetShipTransform(); if (!((Object)(object)shipTransform == (Object)null)) { int instanceID = ((Object)giant).GetInstanceID(); if (ActiveEscapes.TryGetValue(instanceID, out var value)) { ContinueEscape(giant, shipTransform, instanceID, value); } else { TryStartEscape(giant, shipTransform, instanceID); } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[FGF] Anti-camp error: " + ex.Message)); } } private unsafe static void TryStartEscape(ForestGiantAI giant, Transform ship, int id) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) bool fieldValue = ReflectionUtil.GetFieldValue(giant, "lostPlayerInChase"); bool fieldValue2 = ReflectionUtil.GetFieldValue(giant, "chasingPlayerInLOS"); int fieldValue3 = ReflectionUtil.GetFieldValue(giant, "currentBehaviourStateIndex"); if (!(!fieldValue || fieldValue2) && fieldValue3 == 1) { float num = Vector3.Distance(((Component)giant).transform.position, ship.position); if (!(num > Plugin.ShipCampRadius.Value) && TryChooseEscapeNode(giant, ship.position, out var result)) { float realtimeSinceStartup = Time.realtimeSinceStartup; EscapeState escapeState = new EscapeState(); escapeState.Target = result; escapeState.Started = realtimeSinceStartup; escapeState.NextReassert = 0f; escapeState.LastProgressTime = realtimeSinceStartup; escapeState.LastDistanceFromShip = num; escapeState.LastPosition = ((Component)giant).transform.position; escapeState.RetargetCount = 0; ActiveEscapes[id] = escapeState; Plugin.Log.LogInfo((object)"[FGF] ======================================"); Plugin.Log.LogInfo((object)"[FGF] SHIP ANTI-CAMP TRIGGERED"); Plugin.Log.LogInfo((object)("[FGF] Giant lost player " + num.ToString("0.0") + " units from ship.")); ManualLogSource log = Plugin.Log; Vector3 val = result; log.LogInfo((object)("[FGF] Escape destination: " + ((object)(*(Vector3*)(&val))/*cast due to .constrained prefix*/).ToString())); Plugin.Log.LogInfo((object)("[FGF] Escape will remain authoritative until giant reaches " + (Plugin.ShipCampRadius.Value + Plugin.ShipEscapeExtraDistance.Value).ToString("0.0") + " units.")); Plugin.Log.LogInfo((object)"[FGF] ======================================"); ForceEscapeState(giant, escapeState); } } } private unsafe static void ContinueEscape(ForestGiantAI giant, Transform ship, int id, EscapeState state) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: 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_013f: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: 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_01c6: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_0222: Unknown result type (might be due to invalid IL or missing references) //IL_0224: Unknown result type (might be due to invalid IL or missing references) float realtimeSinceStartup = Time.realtimeSinceStartup; float num = Vector3.Distance(((Component)giant).transform.position, ship.position); float num2 = Plugin.ShipCampRadius.Value + Plugin.ShipEscapeExtraDistance.Value; if (num >= num2) { ActiveEscapes.Remove(id); Plugin.Log.LogInfo((object)"[FGF] ======================================"); Plugin.Log.LogInfo((object)"[FGF] GIANT ANTI-CAMP ESCAPE SUCCESS"); Plugin.Log.LogInfo((object)("[FGF] Distance from ship=" + num.ToString("0.0"))); Plugin.Log.LogInfo((object)"[FGF] ======================================"); return; } if (realtimeSinceStartup - state.Started > Plugin.ShipEscapeMaxTime.Value) { ActiveEscapes.Remove(id); Plugin.Log.LogWarning((object)"[FGF] ANTI-CAMP ESCAPE FAILED / TIMED OUT"); Plugin.Log.LogWarning((object)("[FGF] Giant still only " + num.ToString("0.0") + " units from ship after " + Plugin.ShipEscapeMaxTime.Value + " seconds.")); return; } float num3 = Vector3.Distance(((Component)giant).transform.position, state.LastPosition); bool flag = num > state.LastDistanceFromShip + 0.75f; if (num3 > 1f || flag) { state.LastProgressTime = realtimeSinceStartup; state.LastPosition = ((Component)giant).transform.position; state.LastDistanceFromShip = num; } if (realtimeSinceStartup - state.LastProgressTime >= Plugin.ShipEscapeStuckTime.Value && TryChooseEscapeNode(giant, ship.position, out var result)) { state.Target = result; state.LastProgressTime = realtimeSinceStartup; state.LastPosition = ((Component)giant).transform.position; state.LastDistanceFromShip = num; state.RetargetCount++; Plugin.Log.LogWarning((object)"[FGF] Giant made insufficient escape progress."); ManualLogSource log = Plugin.Log; string text = state.RetargetCount.ToString(); Vector3 val = result; log.LogInfo((object)("[FGF] Retargeting anti-camp destination #" + text + " -> " + ((object)(*(Vector3*)(&val))/*cast due to .constrained prefix*/).ToString())); } if (realtimeSinceStartup >= state.NextReassert) { state.NextReassert = realtimeSinceStartup + Mathf.Max(0.05f, Plugin.ShipEscapeReassertInterval.Value); ForceEscapeState(giant, state); } ActiveEscapes[id] = state; } private static void ForceEscapeState(ForestGiantAI giant, EscapeState state) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) ReflectionUtil.TryInvokeIntMethod(giant, "SwitchToBehaviourState", 0); ReflectionUtil.TrySetField(giant, "lostPlayerInChase", false); ReflectionUtil.TrySetField(giant, "chasingPlayerInLOS", false); ReflectionUtil.TrySetField(giant, "movingTowardsTargetPlayer", false); SetDestination(giant, state.Target); } private static Transform GetShipTransform() { try { if ((Object)(object)StartOfRound.Instance != (Object)null && (Object)(object)StartOfRound.Instance.elevatorTransform != (Object)null) { return StartOfRound.Instance.elevatorTransform; } } catch { } return null; } private static bool TryChooseEscapeNode(ForestGiantAI giant, Vector3 shipPosition, out Vector3 result) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) result = Vector3.zero; List aINodes = GetAINodes(giant); if (aINodes.Count == 0) { if (!_nodeFailureLogged) { _nodeFailureLogged = true; Plugin.Log.LogWarning((object)"[FGF] Anti-camp could not locate outdoor AI nodes."); } return false; } aINodes.Sort(delegate(Transform a, Transform b) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) float value = Vector3.SqrMagnitude(a.position - shipPosition); return Vector3.SqrMagnitude(b.position - shipPosition).CompareTo(value); }); if (!Plugin.RandomWander.Value) { result = aINodes[0].position; return true; } int num = Mathf.Clamp(aINodes.Count / 4, 1, Mathf.Min(8, aINodes.Count)); int index = Random.Range(0, num); result = aINodes[index].position; return true; } private static List GetAINodes(ForestGiantAI giant) { List list = new List(); object fieldObject = ReflectionUtil.GetFieldObject(giant, "allAINodes"); AddNodes(list, fieldObject); if (list.Count > 0) { return list; } try { GameObject[] array = GameObject.FindGameObjectsWithTag("OutsideAINode"); if (array != null) { GameObject[] array2 = array; foreach (GameObject val in array2) { if ((Object)(object)val != (Object)null) { list.Add(val.transform); } } } } catch { } return list; } private static void AddNodes(List target, object value) { if (value == null || !(value is IEnumerable enumerable)) { return; } foreach (object item in enumerable) { GameObject val = (GameObject)((item is GameObject) ? item : null); if ((Object)(object)val != (Object)null) { target.Add(val.transform); continue; } Transform val2 = (Transform)((item is Transform) ? item : null); if ((Object)(object)val2 != (Object)null) { target.Add(val2); } } } private static void SetDestination(ForestGiantAI giant, Vector3 position) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) if (!ReflectionUtil.TrySetDestination(giant, position) && !_destinationFailureLogged) { _destinationFailureLogged = true; Plugin.Log.LogWarning((object)"[FGF] Could not invoke EnemyAI.SetDestinationToPosition."); } } } internal static class ReflectionUtil { internal static T GetFieldValue(object instance, string fieldName) { object fieldObject = GetFieldObject(instance, fieldName); if (fieldObject == null) { return default(T); } try { return (T)fieldObject; } catch { return default(T); } } internal static object GetFieldObject(object instance, string fieldName) { if (instance == null) { return null; } FieldInfo fieldInfo = FindField(instance.GetType(), fieldName); if (fieldInfo == null) { return null; } try { return fieldInfo.GetValue(instance); } catch { return null; } } internal static bool TrySetField(object instance, string fieldName, object value) { if (instance == null) { return false; } FieldInfo fieldInfo = FindField(instance.GetType(), fieldName); if (fieldInfo == null) { return false; } try { fieldInfo.SetValue(instance, value); return true; } catch { return false; } } private static FieldInfo FindField(Type type, string fieldName) { Type type2 = type; while (type2 != null && type2 != typeof(object)) { FieldInfo field = type2.GetField(fieldName, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { return field; } type2 = type2.BaseType; } return null; } internal static bool TryInvokeIntMethod(object instance, string methodName, int value) { if (instance == null) { return false; } Type type = instance.GetType(); while (type != null && type != typeof(object)) { MethodInfo[] methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); MethodInfo[] array = methods; foreach (MethodInfo methodInfo in array) { if (methodInfo.Name != methodName) { continue; } ParameterInfo[] parameters = methodInfo.GetParameters(); if (parameters.Length == 1 && parameters[0].ParameterType == typeof(int)) { try { methodInfo.Invoke(instance, new object[1] { value }); return true; } catch { return false; } } } type = type.BaseType; } return false; } internal static bool TrySetDestination(object instance, Vector3 position) { //IL_009d: Unknown result type (might be due to invalid IL or missing references) if (instance == null) { return false; } Type type = instance.GetType(); while (type != null && type != typeof(object)) { MethodInfo[] methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); MethodInfo[] array = methods; foreach (MethodInfo methodInfo in array) { if (methodInfo.Name != "SetDestinationToPosition") { continue; } ParameterInfo[] parameters = methodInfo.GetParameters(); if (parameters.Length < 1 || parameters[0].ParameterType != typeof(Vector3)) { continue; } object[] array2 = new object[parameters.Length]; array2[0] = position; for (int j = 1; j < parameters.Length; j++) { if (parameters[j].ParameterType == typeof(bool)) { array2[j] = true; } else if (parameters[j].HasDefaultValue) { array2[j] = parameters[j].DefaultValue; } else if (parameters[j].ParameterType.IsValueType) { array2[j] = Activator.CreateInstance(parameters[j].ParameterType); } else { array2[j] = null; } } try { methodInfo.Invoke(instance, array2); return true; } catch { return false; } } type = type.BaseType; } return false; } }