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 FairGiantsFixed v0.2.1
plugins/FairGiantsFixed.dll
Decompiled 2 hours agousing 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<float> VisionDivisor; internal static ConfigEntry<bool> AffectFoggy; internal static ConfigEntry<bool> AffectSnowy; internal static ConfigEntry<string> SnowWeatherNames; internal static ConfigEntry<StealthDecayMode> StealthDecaysWhen; internal static ConfigEntry<float> PassiveStealthDecay; internal static ConfigEntry<bool> EnhancedAntiCamp; internal static ConfigEntry<bool> RandomWander; internal static ConfigEntry<float> ShipCampRadius; internal static ConfigEntry<float> ShipEscapeExtraDistance; internal static ConfigEntry<float> ShipEscapeReassertInterval; internal static ConfigEntry<float> ShipEscapeStuckTime; internal static ConfigEntry<float> 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<float>("Vision", "VisionDivisor", 3f, "Divides Forest Giant vision range during affected weather."); AffectFoggy = ((BaseUnityPlugin)this).Config.Bind<bool>("Vision", "AffectFoggy", true, "Reduce Forest Giant vision during foggy weather."); AffectSnowy = ((BaseUnityPlugin)this).Config.Bind<bool>("Vision", "AffectSnowy", true, "Reduce Forest Giant vision during snowy weather."); SnowWeatherNames = ((BaseUnityPlugin)this).Config.Bind<string>("Vision", "SnowWeatherNames", "snow,snowy,blizzard", "Comma-separated weather names treated as snowy."); StealthDecaysWhen = ((BaseUnityPlugin)this).Config.Bind<StealthDecayMode>("Aggro", "StealthDecaysWhen", StealthDecayMode.Solo, "When stealth meters should passively decay while the giant sees nobody."); PassiveStealthDecay = ((BaseUnityPlugin)this).Config.Bind<float>("Aggro", "PassiveStealthDecay", 0.2f, "Stealth memory lost per second while no players are visible."); EnhancedAntiCamp = ((BaseUnityPlugin)this).Config.Bind<bool>("Ship", "EnhancedAntiCamp", true, "Makes giants commit to leaving the ship area after losing a player nearby."); RandomWander = ((BaseUnityPlugin)this).Config.Bind<bool>("Ship", "RandomWander", true, "Choose randomly from distant AI nodes instead of always using the farthest node."); ShipCampRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Ship", "ShipCampRadius", 40f, "Distance from the ship within which anti-camp can activate."); ShipEscapeExtraDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Ship", "ShipEscapeExtraDistance", 20f, "Additional distance beyond ShipCampRadius the giant must reach."); ShipEscapeReassertInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Ship", "ShipEscapeReassertInterval", 0.2f, "How often the mod forces the escape destination while anti-camp is active."); ShipEscapeStuckTime = ((BaseUnityPlugin)this).Config.Bind<float>("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<float>("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 "<no TimeOfDay>"; } object obj = TimeOfDay.Instance.currentLevelWeather; if (obj == null) { return "<null weather>"; } return obj.ToString(); } catch { return "<weather error>"; } } 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<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Expected O, but got Unknown List<CodeInstruction> list = new List<CodeInstruction>(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<CodeInstruction> 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<float[]>(__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<float[]>(__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<bool>(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<int, EscapeState> ActiveEscapes = new Dictionary<int, EscapeState>(); 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<bool>(giant, "lostPlayerInChase"); bool fieldValue2 = ReflectionUtil.GetFieldValue<bool>(giant, "chasingPlayerInLOS"); int fieldValue3 = ReflectionUtil.GetFieldValue<int>(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<Transform> 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<Transform> GetAINodes(ForestGiantAI giant) { List<Transform> list = new List<Transform>(); 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<Transform> 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<T>(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; } }