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 TameAI v1.1.6
BepInEx/plugins/Goblinheim - TameAI/NicoMods.TameAI.dll
Decompiled a day agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("NicoMods.TameAI")] [assembly: AssemblyDescription("Lightweight local navigation safeguards for tamed Valheim creatures")] [assembly: AssemblyCompany("NicoMods")] [assembly: AssemblyProduct("NicoMods.TameAI")] [assembly: AssemblyCopyright("Copyright © 2026 NicoMods")] [assembly: ComVisible(false)] [assembly: AssemblyFileVersion("1.1.6.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("1.1.6.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace NicoMods.TameAI { internal static class Authority { private const string CompanionAlpha = "GoblinHeim.Companion.Alpha"; private const string CompanionWounded = "GoblinHeim.Companion.Wounded"; private const string CompanionHealing = "GoblinHeim.Companion.Healing"; private const string CompanionRecovery = "GoblinHeim.Companion.RecoveryPending"; internal static bool IsCompanionAlpha(Character character) { if ((Object)(object)character == (Object)null) { return false; } ZNetView component = ((Component)character).GetComponent<ZNetView>(); ZDO val = (((Object)(object)component != (Object)null && component.IsValid()) ? component.GetZDO() : null); if (val != null) { return val.GetBool("GoblinHeim.Companion.Alpha", false); } return false; } internal static bool IsLocalParkedTame(BaseAI ai, out Character character, out MonsterAI monster) { if (!IsLocalTame(ai, out character, out monster)) { return false; } if ((Object)(object)monster.GetFollowTarget() != (Object)null) { return false; } return true; } internal static bool IsLocalFollowingTame(BaseAI ai, GameObject expectedTarget, out Character character, out MonsterAI monster) { if (!IsLocalTame(ai, out character, out monster)) { return false; } if ((Object)(object)expectedTarget != (Object)null) { return (Object)(object)monster.GetFollowTarget() == (Object)(object)expectedTarget; } return false; } private static bool IsLocalTame(BaseAI ai, out Character character, out MonsterAI monster) { character = null; monster = (MonsterAI)(object)((ai is MonsterAI) ? ai : null); if (!TameAIPlugin.Enabled.Value || (Object)(object)ai == (Object)null || (Object)(object)monster == (Object)null) { return false; } character = ((Component)ai).GetComponent<Character>(); ZNetView component = ((Component)ai).GetComponent<ZNetView>(); if ((Object)(object)character == (Object)null || (Object)(object)component == (Object)null || !component.IsValid() || !component.IsOwner() || character.IsDead() || !character.IsTamed()) { return false; } if (character.HaveRider() || character.IsAttached()) { return false; } ZDO zDO = component.GetZDO(); if (zDO != null && zDO.GetBool("GoblinHeim.Companion.Alpha", false) && (zDO.GetBool("GoblinHeim.Companion.Wounded", false) || zDO.GetBool("GoblinHeim.Companion.Healing", false) || zDO.GetBool("GoblinHeim.Companion.RecoveryPending", false))) { return false; } return true; } } internal enum RefusalReason { None, Water, Lava, Fire, Smoke, Trap, Structure, NarrowOpening, Occupied, NoPath } internal static class HazardScanner { private static readonly Collider[] Hits = (Collider[])(object)new Collider[48]; private static readonly RaycastHit[] CastHits = (RaycastHit[])(object)new RaycastHit[24]; private static int solidMask = -1; private static int smokeMask = -1; private static int SolidMask { get { if (solidMask < 0) { solidMask = LayerMask.GetMask(new string[5] { "Default", "Default_small", "static_solid", "piece", "vehicle" }); } return solidMask; } } private static int SmokeMask { get { if (smokeMask < 0) { smokeMask = LayerMask.GetMask(new string[1] { "smoke" }); } return smokeMask; } } internal static RefusalReason Judge(BaseAI ai, Character character, Vector3 point, bool allowOccupied = false) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: 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_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) if (TameAIPlugin.AvoidLava.Value && (Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.IsLava(point, false)) { return RefusalReason.Lava; } if (TameAIPlugin.AvoidFire.Value && Object.op_Implicit((Object)(object)EffectArea.IsPointInsideArea(point, (Type)2, 0f))) { return RefusalReason.Fire; } if (TameAIPlugin.AvoidSmoke.Value && Physics.CheckSphere(point + Vector3.up * 0.5f, 0.5f, SmokeMask, (QueryTriggerInteraction)2)) { return RefusalReason.Smoke; } if (TameAIPlugin.AvoidTraps.Value && HarmfulAoe(point, 1.5f)) { return RefusalReason.Trap; } BodySize(character, out var radius, out var height); if (TameAIPlugin.AvoidSwimming.Value && IsWaterDestination(character, point, height)) { return RefusalReason.Water; } if (TameAIPlugin.StructureAwareness.Value) { if (BlockedAt(character, point, radius, height)) { return RefusalReason.Structure; } if (BlockedBetween(character, ((Component)character).transform.position, point, radius, height)) { return RefusalReason.NarrowOpening; } if ((Object)(object)Pathfinding.instance != (Object)null && !Pathfinding.instance.HavePath(((Component)ai).transform.position, point, ai.m_pathAgentType)) { return RefusalReason.NoPath; } } if (!allowOccupied && TameAIPlugin.AnimalSeparation.Value && Occupied(character, point, radius)) { return RefusalReason.Occupied; } return RefusalReason.None; } internal static RefusalReason JudgeRestingArea(Character character, Vector3 point) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)character == (Object)null) { return RefusalReason.None; } if (TameAIPlugin.StructureAwareness.Value && !ClearOfBuiltPieces(character, point, TameAIPlugin.RestingClearance.Value)) { return RefusalReason.Structure; } if (TameAIPlugin.AnimalSeparation.Value && !ClearOfOtherTames(character, point)) { return RefusalReason.Occupied; } return RefusalReason.None; } internal static bool NearbyConstructedHazard(Character character, float searchRadius, out Vector3 origin, out float keepAway, out string name) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_001c: 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) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: 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_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) origin = Vector3.zero; keepAway = 0f; name = null; int num = Physics.OverlapSphereNonAlloc(((Component)character).transform.position, searchRadius, Hits, -1, (QueryTriggerInteraction)2); float num2 = float.MaxValue; for (int i = 0; i < num; i++) { Collider val = Hits[i]; if ((Object)(object)val == (Object)null || ((Component)val).transform.IsChildOf(((Component)character).transform)) { continue; } Aoe componentInParent = ((Component)val).GetComponentInParent<Aoe>(); if ((Object)(object)componentInParent == (Object)null || !componentInParent.m_hitCharacters || !componentInParent.m_hitFriendly || ((DamageTypes)(ref componentInParent.m_damage)).GetTotalDamage() <= 0f) { continue; } Piece componentInParent2 = ((Component)componentInParent).GetComponentInParent<Piece>(); if (!((Object)(object)componentInParent2 == (Object)null)) { Vector3 val2 = val.ClosestPoint(((Component)character).transform.position); float num3 = Vector3.Distance(((Component)character).transform.position, val2); float num4 = Mathf.Max(1.5f, componentInParent.m_radius + 1.25f); if (!(num3 > num4) && !(num3 >= num2)) { num2 = num3; origin = ((Component)componentInParent).transform.position; keepAway = num4; name = Utils.GetPrefabName(((Component)componentInParent2).gameObject); } } } return num2 < float.MaxValue; } internal static void ArmVanilla(BaseAI ai) { if (TameAIPlugin.AvoidFire.Value) { ai.m_avoidFire = true; } if (TameAIPlugin.AvoidLava.Value) { ai.m_avoidLava = true; } if (TameAIPlugin.AvoidSwimming.Value) { ai.m_avoidWater = true; } } internal static bool DangerAt(Character character, Vector3 point, bool allowWater = false) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: 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_00b4: Unknown result type (might be due to invalid IL or missing references) if (TameAIPlugin.AvoidLava.Value && (Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.IsLava(point, false)) { return true; } if (TameAIPlugin.AvoidFire.Value && Object.op_Implicit((Object)(object)EffectArea.IsPointInsideArea(point, (Type)2, 0f))) { return true; } if (TameAIPlugin.AvoidSmoke.Value && Physics.CheckSphere(point + Vector3.up * 0.5f, 0.5f, SmokeMask, (QueryTriggerInteraction)2)) { return true; } if (TameAIPlugin.AvoidTraps.Value && HarmfulAoe(point, 1.25f)) { return true; } BodySize(character, out var _, out var height); if (!allowWater && TameAIPlugin.AvoidSwimming.Value) { return IsWaterDestination(character, point, height); } return false; } internal static bool SafeFollowSegment(Character character, Vector3 from, Vector3 to, bool allowWater = false) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_001e: 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_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) int num = Mathf.Clamp(Mathf.CeilToInt(Vector3.Distance(from, to) / 2f), 1, 8); float y = default(float); for (int i = 1; i <= num; i++) { Vector3 val = Vector3.Lerp(from, to, (float)i / (float)num); if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetSolidHeight(val, ref y, 1000)) { val.y = y; } if (DangerAt(character, val, allowWater)) { return false; } } return true; } private static bool IsWaterDestination(Character character, Vector3 point, float height) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) if (character.IsFlying() || (Object)(object)ZoneSystem.instance == (Object)null) { return false; } float num = default(float); if (!ZoneSystem.instance.GetSolidHeight(point, ref num, 1000)) { return true; } float waterLevel = ZoneSystem.instance.m_waterLevel; float num2 = Mathf.Max(0.25f, height * 0.22f); if (!character.IsSwimming()) { return num < waterLevel - num2; } float num3 = default(float); if (ZoneSystem.instance.GetSolidHeight(((Component)character).transform.position, ref num3, 1000)) { return num < num3 - 0.15f; } return false; } private static bool HarmfulAoe(Vector3 point, float margin) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) int num = Physics.OverlapSphereNonAlloc(point, margin, Hits, -1, (QueryTriggerInteraction)2); for (int i = 0; i < num; i++) { Collider val = Hits[i]; if (!((Object)(object)val == (Object)null)) { Aoe componentInParent = ((Component)val).GetComponentInParent<Aoe>(); if ((Object)(object)componentInParent != (Object)null && componentInParent.m_hitCharacters && componentInParent.m_hitFriendly && ((DamageTypes)(ref componentInParent.m_damage)).GetTotalDamage() > 0f && Vector3.Distance(point, ((Component)componentInParent).transform.position) <= componentInParent.m_radius + margin) { return true; } } } return false; } private static bool BlockedAt(Character self, Vector3 point, float radius, float height) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: 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) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0020: 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_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) Vector3 val = point + Vector3.up * radius; Vector3 val2 = point + Vector3.up * Mathf.Max(radius, height - radius); int num = Physics.OverlapCapsuleNonAlloc(val, val2, radius, Hits, SolidMask, (QueryTriggerInteraction)1); for (int i = 0; i < num; i++) { Collider val3 = Hits[i]; if (Foreign(self, val3)) { Bounds bounds = val3.bounds; if (((Bounds)(ref bounds)).max.y > point.y + 0.15f) { return true; } } } return false; } private static bool BlockedBetween(Character self, Vector3 from, Vector3 to, float radius, float height) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0023: 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) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0046: 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) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) Vector3 val = from + Vector3.up * Mathf.Clamp(height * 0.45f, radius, height - radius); Vector3 val2 = to + Vector3.up * Mathf.Clamp(height * 0.45f, radius, height - radius) - val; float magnitude = ((Vector3)(ref val2)).magnitude; if (magnitude < 0.1f) { return false; } int num = Physics.SphereCastNonAlloc(val, radius, val2 / magnitude, CastHits, magnitude, SolidMask, (QueryTriggerInteraction)1); for (int i = 0; i < num; i++) { if (Foreign(self, ((RaycastHit)(ref CastHits[i])).collider)) { return true; } } return false; } private static bool Occupied(Character self, Vector3 point, float radius) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) int num = Physics.OverlapSphereNonAlloc(point + Vector3.up * radius, radius * 1.35f + TameAIPlugin.SeparationMargin.Value, Hits, -1, (QueryTriggerInteraction)1); for (int i = 0; i < num; i++) { Collider val = Hits[i]; if (!((Object)(object)val == (Object)null) && !((Component)val).transform.IsChildOf(((Component)self).transform)) { Character componentInParent = ((Component)val).GetComponentInParent<Character>(); if ((Object)(object)componentInParent != (Object)null && (Object)(object)componentInParent != (Object)(object)self && !componentInParent.IsDead()) { return true; } } } return false; } private static bool ClearOfBuiltPieces(Character self, Vector3 point, float clearance) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0086: 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_008f: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009c: 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_00a2: Unknown result type (might be due to invalid IL or missing references) Vector3 val = point + Vector3.up * 0.75f; int num = Physics.OverlapSphereNonAlloc(val, clearance, Hits, SolidMask, (QueryTriggerInteraction)1); for (int i = 0; i < num; i++) { Collider val2 = Hits[i]; if ((Object)(object)val2 == (Object)null || ((Component)val2).transform.IsChildOf(((Component)self).transform) || (Object)(object)((Component)val2).GetComponentInParent<Piece>() == (Object)null) { continue; } Bounds bounds = val2.bounds; if (!(((Bounds)(ref bounds)).max.y <= point.y + 0.25f)) { Vector3 val3 = val2.ClosestPoint(val); val3.y = val.y; Vector3 val4 = val3 - val; val4.y = 0f; if (((Vector3)(ref val4)).sqrMagnitude < clearance * clearance) { return false; } } } return true; } private static bool ClearOfOtherTames(Character self, Vector3 point) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Max(0.25f, self.GetRadius()); List<Character> allCharacters = Character.GetAllCharacters(); for (int i = 0; i < allCharacters.Count; i++) { Character val = allCharacters[i]; if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)self) && !val.IsDead() && val.IsTamed()) { Vector3 val2 = ((Component)val).transform.position - point; val2.y = 0f; float num2 = num + Mathf.Max(0.25f, val.GetRadius()) + TameAIPlugin.SeparationMargin.Value; if (((Vector3)(ref val2)).sqrMagnitude < num2 * num2) { return false; } } } return true; } private static bool Foreign(Character self, Collider hit) { if ((Object)(object)hit == (Object)null || hit.isTrigger || ((Component)hit).transform.IsChildOf(((Component)self).transform)) { return false; } return (Object)(object)((Component)hit).GetComponentInParent<Character>() == (Object)null; } private static void BodySize(Character character, out float radius, out float height) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) CapsuleCollider component = ((Component)character).GetComponent<CapsuleCollider>(); if ((Object)(object)component != (Object)null) { Vector3 lossyScale = ((Component)component).transform.lossyScale; radius = component.radius * Mathf.Max(Mathf.Abs(lossyScale.x), Mathf.Abs(lossyScale.z)); height = component.height * Mathf.Abs(lossyScale.y); } else { Collider component2 = ((Component)character).GetComponent<Collider>(); if ((Object)(object)component2 != (Object)null) { Bounds bounds = component2.bounds; radius = Mathf.Max(((Bounds)(ref bounds)).extents.x, ((Bounds)(ref bounds)).extents.z); height = ((Bounds)(ref bounds)).size.y; } else { radius = 0.45f; height = 1.5f; } } radius = Mathf.Clamp(radius + TameAIPlugin.StructureMargin.Value, 0.3f, 2.5f); height = Mathf.Max(height, radius * 2f + 0.05f); } } internal static class LocalBehaviour { private enum Mode { None, HazardEscape, SocialApproach, SocialPause, PlayerApproach } private sealed class State { internal Mode Current; internal Vector3 Target; internal Vector3 HarmDirection; internal float Until; internal float NextHazardScan; internal float NextSocial; internal float NextPlayerScan; internal float NextPathCheck; internal int SocialPartner; internal int DetectedPlayer; internal string HazardName; } private static readonly Dictionary<int, State> States = new Dictionary<int, State>(); private static readonly Func<BaseAI, float, Vector3, float, bool, bool> MoveTo = AccessTools.MethodDelegate<Func<BaseAI, float, Vector3, float, bool, bool>>(AccessTools.Method(typeof(BaseAI), "MoveTo", new Type[4] { typeof(float), typeof(Vector3), typeof(float), typeof(bool) }, (Type[])null), (object)null, true); internal static void Clear() { States.Clear(); } internal static void Forget(BaseAI ai) { if ((Object)(object)ai != (Object)null) { States.Remove(((Object)ai).GetInstanceID()); } } internal static bool HasNearbyPlayer(Vector3 point) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZoneSystem.instance == (Object)null) { return false; } float num = Mathf.Max(80f, ZoneSystem.instance.m_zoneSize * 1.5f); return (Object)(object)Player.GetClosestPlayer(point, num) != (Object)null; } internal static void EnvironmentalDamage(Character character, HitData hit) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) if (!TameAIPlugin.CalmHazardEscape.Value || (Object)(object)character == (Object)null || hit == null || !((DamageTypes)(ref hit.m_damage)).HaveDamage() || !HasNearbyPlayer(((Component)character).transform.position)) { return; } BaseAI baseAI = character.GetBaseAI(); if (!Authority.IsLocalParkedTame(baseAI, out var character2, out var _) || Authority.IsCompanionAlpha(character2)) { return; } Character attacker = hit.GetAttacker(); if (!((Object)(object)attacker != (Object)null) || attacker is Player) { State state = Get(baseAI); Vector3 val = hit.m_dir; val.y = 0f; if (((Vector3)(ref val)).sqrMagnitude < 0.01f && hit.m_point != Vector3.zero) { val = ((Component)character).transform.position - hit.m_point; val.y = 0f; } if (((Vector3)(ref val)).sqrMagnitude < 0.01f) { val = ((Component)character).transform.forward; } state.HarmDirection = ((Vector3)(ref val)).normalized; state.Current = Mode.HazardEscape; state.Until = Time.time + 1.5f; PickEscape(baseAI, character, state, ((Component)character).transform.position - state.HarmDirection, 4f); } } internal static bool BeforeIdle(BaseAI ai, float dt) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0143: 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_00a5: 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_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_0362: Unknown result type (might be due to invalid IL or missing references) //IL_036d: Unknown result type (might be due to invalid IL or missing references) //IL_0372: Unknown result type (might be due to invalid IL or missing references) //IL_0377: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_0396: Unknown result type (might be due to invalid IL or missing references) //IL_025b: Unknown result type (might be due to invalid IL or missing references) //IL_0266: Unknown result type (might be due to invalid IL or missing references) //IL_026b: Unknown result type (might be due to invalid IL or missing references) //IL_0270: Unknown result type (might be due to invalid IL or missing references) //IL_02cc: Unknown result type (might be due to invalid IL or missing references) //IL_02d3: Unknown result type (might be due to invalid IL or missing references) //IL_02f6: Unknown result type (might be due to invalid IL or missing references) //IL_02fb: Unknown result type (might be due to invalid IL or missing references) //IL_0300: Unknown result type (might be due to invalid IL or missing references) //IL_0304: Unknown result type (might be due to invalid IL or missing references) //IL_02b4: Unknown result type (might be due to invalid IL or missing references) //IL_0319: Unknown result type (might be due to invalid IL or missing references) //IL_031b: Unknown result type (might be due to invalid IL or missing references) //IL_0328: Unknown result type (might be due to invalid IL or missing references) if (!Authority.IsLocalParkedTame(ai, out var character, out var _)) { Forget(ai); return true; } if (Authority.IsCompanionAlpha(character)) { Forget(ai); return true; } if (!HasNearbyPlayer(((Component)character).transform.position)) { Forget(ai); return true; } State state = Get(ai); float time = Time.time; if (state.NextHazardScan <= time) { state.NextHazardScan = time + 0.75f + StableFraction(((Object)ai).GetInstanceID(), 0.55f); if (TameAIPlugin.CalmHazardEscape.Value && HazardScanner.NearbyConstructedHazard(character, 8f, out var origin, out var keepAway, out var name)) { Vector3 val = ((Component)character).transform.position - origin; val.y = 0f; if (((Vector3)(ref val)).sqrMagnitude < 0.01f) { val = ((Component)character).transform.forward; } state.HarmDirection = ((Vector3)(ref val)).normalized; state.Current = Mode.HazardEscape; state.Until = time + 1.5f; state.HazardName = name; PickEscape(ai, character, state, origin, Mathf.Clamp(keepAway + 1f, 3f, 7f)); } } if (state.Current == Mode.HazardEscape) { if (time > state.Until) { state.Current = Mode.None; ai.ResetRandomMovement(); return false; } if (!SafeTarget(ai, character, state.Target)) { PickEscape(ai, character, state, ((Component)character).transform.position - state.HarmDirection, 4f); } if (MoveTo(ai, dt, state.Target, 0.6f, arg5: false)) { state.Current = Mode.None; ai.ResetRandomMovement(); } return false; } if (state.Current == Mode.PlayerApproach) { if (time > state.Until || (time >= state.NextPathCheck && !SafeTarget(ai, character, state.Target, allowOccupied: true))) { state.Current = Mode.None; ai.ResetRandomMovement(); return false; } if (time >= state.NextPathCheck) { state.NextPathCheck = time + 1f; } if (MoveTo(ai, dt, state.Target, 1f, arg5: false)) { state.Current = Mode.None; ai.ResetRandomMovement(); } return false; } if (state.Current == Mode.SocialApproach) { Character val2 = FindByInstance(state.SocialPartner); if ((Object)(object)val2 == (Object)null || val2.IsDead() || time > state.Until) { EndSocial(ai, state, time); return false; } Vector3 val3 = ((Component)val2).transform.position - ((Component)character).transform.position; val3.y = 0f; if (((Vector3)(ref val3)).sqrMagnitude <= 5.75f) { state.Current = Mode.SocialPause; state.Until = time + 2f + StableFraction(((Object)ai).GetInstanceID(), 2.5f); character.SetLookDir(((Vector3)(ref val3)).normalized, 0f); return false; } Vector3 target = ((Component)val2).transform.position - ((Vector3)(ref val3)).normalized * Mathf.Max(1.2f, character.GetRadius() + val2.GetRadius() + 0.35f); if (!SafeTarget(ai, character, target, allowOccupied: true)) { EndSocial(ai, state, time); return false; } state.Target = target; MoveTo(ai, dt, state.Target, 1f, arg5: false); return false; } if (state.Current == Mode.SocialPause) { Character val4 = FindByInstance(state.SocialPartner); if ((Object)(object)val4 != (Object)null) { Vector3 val5 = ((Component)val4).transform.position - ((Component)character).transform.position; val5.y = 0f; if (((Vector3)(ref val5)).sqrMagnitude > 0.01f) { character.SetLookDir(((Vector3)(ref val5)).normalized, 0f); } } if (time >= state.Until) { EndSocial(ai, state, time); } return false; } if (TameAIPlugin.PlayerCuriosity.Value && time >= state.NextPlayerScan) { state.NextPlayerScan = time + 4f + StableFraction(((Object)ai).GetInstanceID() + Mathf.FloorToInt(time / 4f), 3f); TryPlayerCuriosity(ai, character, state, time); if (state.Current != Mode.None) { return false; } } if (!TameAIPlugin.SocialVisits.Value) { return true; } if (state.NextSocial <= 0f) { state.NextSocial = time + 35f + StableFraction(((Object)ai).GetInstanceID(), 70f); } if (time >= state.NextSocial) { TryStartSocial(ai, character, state, time); } return state.Current == Mode.None; } private static void TryPlayerCuriosity(BaseAI ai, Character self, State state, float now) { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_01da: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Min(Mathf.Max(8f, ai.m_viewRange), TameAIPlugin.CuriosityMaxRange.Value); Player val = FindPlayerByInstance(state.DetectedPlayer); if ((Object)(object)val != (Object)null && !((Character)val).IsDead() && Vector3.Distance(((Component)self).transform.position, ((Component)val).transform.position) <= num + 5f) { return; } state.DetectedPlayer = 0; List<Player> allPlayers = Player.GetAllPlayers(); Player val2 = null; float num2 = num * num; for (int i = 0; i < allPlayers.Count; i++) { Player val3 = allPlayers[i]; if (!((Object)(object)val3 == (Object)null) && !((Character)val3).IsDead()) { Vector3 val4 = ((Component)val3).transform.position - ((Component)self).transform.position; float sqrMagnitude = ((Vector3)(ref val4)).sqrMagnitude; if (!(sqrMagnitude >= num2) && ai.CanSeeTarget((Character)(object)val3)) { num2 = sqrMagnitude; val2 = val3; } } } if ((Object)(object)val2 == (Object)null) { return; } state.DetectedPlayer = ((Object)val2).GetInstanceID(); if (StableFraction(((Object)ai).GetInstanceID() ^ ((Object)val2).GetInstanceID() ^ Mathf.FloorToInt(now), 100f) >= TameAIPlugin.CuriosityChance.Value) { return; } Vector3 val5 = ((Component)val2).transform.position - ((Component)self).transform.position; val5.y = 0f; if (!(((Vector3)(ref val5)).sqrMagnitude < 9f)) { Vector3 val6 = ((Component)val2).transform.position - ((Vector3)(ref val5)).normalized * Mathf.Max(1.75f, self.GetRadius() + ((Character)val2).GetRadius() + 0.75f); float y = default(float); if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetSolidHeight(val6, ref y, 1000)) { val6.y = y; } if (SafeTarget(ai, self, val6, allowOccupied: true)) { state.Current = Mode.PlayerApproach; state.Target = val6; state.Until = now + 6f; state.NextPathCheck = now + 1f; } } } private static void TryStartSocial(BaseAI ai, Character self, State state, float now) { //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) state.NextSocial = now + 55f + StableFraction(((Object)ai).GetInstanceID() + Mathf.FloorToInt(now), 85f); List<Character> allCharacters = Character.GetAllCharacters(); Character val = null; float num = 144f; string prefabName = Utils.GetPrefabName(((Component)self).gameObject); for (int i = 0; i < allCharacters.Count; i++) { Character val2 = allCharacters[i]; if ((Object)(object)val2 == (Object)null || (Object)(object)val2 == (Object)(object)self || val2.IsDead() || !val2.IsTamed() || Utils.GetPrefabName(((Component)val2).gameObject) != prefabName) { continue; } BaseAI baseAI = val2.GetBaseAI(); if (!((Object)(object)baseAI == (Object)null) && (!(baseAI is MonsterAI) || !((Object)(object)((MonsterAI)baseAI).GetFollowTarget() != (Object)null))) { Vector3 val3 = ((Component)val2).transform.position - ((Component)self).transform.position; float sqrMagnitude = ((Vector3)(ref val3)).sqrMagnitude; if (!(sqrMagnitude < 9f) && !(sqrMagnitude > num)) { num = sqrMagnitude; val = val2; } } } if (!((Object)(object)val == (Object)null)) { Vector3 val4 = ((Component)val).transform.position - ((Component)self).transform.position; val4.y = 0f; Vector3 target = ((Component)val).transform.position - ((Vector3)(ref val4)).normalized * Mathf.Max(1.2f, self.GetRadius() + val.GetRadius() + 0.35f); if (SafeTarget(ai, self, target, allowOccupied: true)) { state.Current = Mode.SocialApproach; state.SocialPartner = ((Object)val).GetInstanceID(); state.Target = target; state.Until = now + 8f; } } } private static void EndSocial(BaseAI ai, State state, float now) { state.Current = Mode.None; state.SocialPartner = 0; state.NextSocial = now + 55f + StableFraction(((Object)ai).GetInstanceID() + Mathf.FloorToInt(now), 85f); ai.ResetRandomMovement(); } private static void PickEscape(BaseAI ai, Character character, State state, Vector3 danger, float distance) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000c: 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) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: 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_00d6: Unknown result type (might be due to invalid IL or missing references) Vector3 val = ((Component)character).transform.position - danger; val.y = 0f; if (((Vector3)(ref val)).sqrMagnitude < 0.01f) { val = ((((Vector3)(ref state.HarmDirection)).sqrMagnitude > 0.01f) ? state.HarmDirection : ((Component)character).transform.forward); } ((Vector3)(ref val)).Normalize(); float[] array = new float[8] { 0f, 35f, -35f, 70f, -70f, 110f, -110f, 180f }; float y = default(float); for (int i = 0; i < array.Length; i++) { Vector3 val2 = ((Component)character).transform.position + Quaternion.Euler(0f, array[i], 0f) * val * distance; if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetSolidHeight(val2, ref y, 1000)) { val2.y = y; } if (SafeTarget(ai, character, val2)) { state.Target = val2; return; } } state.Target = ((Component)character).transform.position; } private static bool SafeTarget(BaseAI ai, Character character, Vector3 target, bool allowOccupied = false) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: 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) //IL_0022: Unknown result type (might be due to invalid IL or missing references) Vector3 val = target - ((Component)character).transform.position; if (((Vector3)(ref val)).sqrMagnitude > 0.25f) { return HazardScanner.Judge(ai, character, target, allowOccupied) == RefusalReason.None; } return false; } private static Character FindByInstance(int id) { List<Character> allCharacters = Character.GetAllCharacters(); for (int i = 0; i < allCharacters.Count; i++) { if ((Object)(object)allCharacters[i] != (Object)null && ((Object)allCharacters[i]).GetInstanceID() == id) { return allCharacters[i]; } } return null; } private static Player FindPlayerByInstance(int id) { if (id == 0) { return null; } List<Player> allPlayers = Player.GetAllPlayers(); for (int i = 0; i < allPlayers.Count; i++) { if ((Object)(object)allPlayers[i] != (Object)null && ((Object)allPlayers[i]).GetInstanceID() == id) { return allPlayers[i]; } } return null; } private static State Get(BaseAI ai) { int instanceID = ((Object)ai).GetInstanceID(); if (!States.TryGetValue(instanceID, out var value)) { value = (States[instanceID] = new State()); } return value; } private static float StableFraction(int seed, float range) { int num = seed ^ (seed << 13); int num2 = num ^ (num >>> 17); return (float)(uint)((num2 ^ (num2 << 5)) & 0xFFFF) / 65535f * range; } } [HarmonyPatch(typeof(BaseAI), "IdleMovement")] internal static class LocalIdleBehaviourPatch { private static bool Prefix(BaseAI __instance, float dt) { return LocalBehaviour.BeforeIdle(__instance, dt); } } [HarmonyPatch(typeof(BaseAI), "RandomMovement")] internal static class WanderAmplitudePatch { private static void Prefix(BaseAI __instance, ref float __state) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) __state = __instance.m_randomMoveRange; if (Authority.IsLocalParkedTame(__instance, out var character, out var _) && !Authority.IsCompanionAlpha(character) && LocalBehaviour.HasNearbyPlayer(((Component)character).transform.position)) { __instance.m_randomMoveRange = __state * TameAIPlugin.WanderAmplitude.Value; } } private static void Postfix(BaseAI __instance, float __state) { __instance.m_randomMoveRange = __state; } } [HarmonyPatch(typeof(Character), "Damage")] internal static class EnvironmentalDamageResponsePatch { private static void Prefix(Character __instance, HitData hit) { LocalBehaviour.EnvironmentalDamage(__instance, hit); } } internal static class PackFollowBehaviour { private sealed class State { internal Vector3 Target; internal Vector3 HarmDirection; internal float NextPlan; internal float NextSeparation; internal float NextHazardScan; internal float EscapeUntil; internal bool HasTarget; internal Vector3 Repulsion; } private static readonly Dictionary<int, State> States = new Dictionary<int, State>(); private static readonly Func<BaseAI, float, Vector3, float, bool, bool> MoveTo = AccessTools.MethodDelegate<Func<BaseAI, float, Vector3, float, bool, bool>>(AccessTools.Method(typeof(BaseAI), "MoveTo", new Type[4] { typeof(float), typeof(Vector3), typeof(float), typeof(bool) }, (Type[])null), (object)null, true); private static readonly Action<BaseAI> StopMoving = AccessTools.MethodDelegate<Action<BaseAI>>(AccessTools.Method(typeof(BaseAI), "StopMoving", (Type[])null, (Type[])null), (object)null, true); internal static void Clear() { States.Clear(); } internal static void Forget(BaseAI ai) { if ((Object)(object)ai != (Object)null) { States.Remove(((Object)ai).GetInstanceID()); } } internal static bool Follow(BaseAI ai, GameObject follow, float dt) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: 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_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_0204: Unknown result type (might be due to invalid IL or missing references) //IL_0209: Unknown result type (might be due to invalid IL or missing references) //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: 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_0270: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_02ad: Unknown result type (might be due to invalid IL or missing references) if (!TameAIPlugin.PackFollow.Value || !((Object)(object)follow != (Object)null) || !((Object)(object)follow.GetComponent<Player>() != (Object)null)) { return false; } if (!Authority.IsLocalFollowingTame(ai, follow, out var character, out var _)) { Forget(ai); return false; } State state = Get(ai); float time = Time.time; Vector3 position = ((Component)character).transform.position; Vector3 position2 = follow.transform.position; if (state.NextHazardScan <= time) { state.NextHazardScan = time + 0.7f + Fraction(((Object)ai).GetInstanceID(), 0.45f); if (HazardScanner.NearbyConstructedHazard(character, 7f, out var origin, out var keepAway, out var _) || HazardScanner.DangerAt(character, position, allowWater: true)) { Vector3 val = position - ((origin == Vector3.zero) ? position2 : origin); val.y = 0f; if (((Vector3)(ref val)).sqrMagnitude < 0.01f) { val = ((Component)character).transform.forward; } state.HarmDirection = ((Vector3)(ref val)).normalized; state.EscapeUntil = time + 1.5f; state.HasTarget = PickSafeTarget(ai, character, position, state.HarmDirection, Mathf.Clamp(keepAway + 1f, 4f, 8f), out state.Target); } } if (time < state.EscapeUntil) { if (!state.HasTarget) { StopMoving(ai); return true; } MoveTo(ai, dt, state.Target, 0.8f, arg5: false); return true; } float num = Mathf.Min(TameAIPlugin.PackInnerRadius.Value, TameAIPlugin.PackOuterRadius.Value - 1f); float num2 = Mathf.Max(num + 1f, TameAIPlugin.PackOuterRadius.Value); float num3 = HorizontalDistance(position, position2); float desired = character.GetRadius() + TameAIPlugin.PackSpacing.Value; if (time >= state.NextSeparation) { state.NextSeparation = time + 0.35f + Fraction(((Object)ai).GetInstanceID(), 0.25f); state.Repulsion = Separation(character, follow, desired); } Vector3 repulsion = state.Repulsion; bool flag = ((Vector3)(ref repulsion)).sqrMagnitude > 0.01f; if (num3 <= num2 && !flag) { state.HasTarget = false; StopMoving(ai); return true; } if (time >= state.NextPlan || !state.HasTarget) { state.NextPlan = time + 0.45f + Fraction(((Object)ai).GetInstanceID(), 0.25f); state.HasTarget = PlanFormationTarget(ai, character, follow, num, num2, repulsion, out state.Target); } if (!state.HasTarget) { StopMoving(ai); return true; } bool arg = num3 > num2 + 7f; if (MoveTo(ai, dt, state.Target, 1.1f, arg)) { state.HasTarget = false; } return true; } internal static void Damage(Character character, HitData hit) { //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: 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_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) if (!TameAIPlugin.PackFollow.Value || !TameAIPlugin.CalmHazardEscape.Value || (Object)(object)character == (Object)null || hit == null || !((DamageTypes)(ref hit.m_damage)).HaveDamage()) { return; } BaseAI baseAI = character.GetBaseAI(); MonsterAI val = (MonsterAI)(object)((baseAI is MonsterAI) ? baseAI : null); if ((Object)(object)val == (Object)null || (Object)(object)val.GetFollowTarget() == (Object)null || !Authority.IsLocalFollowingTame(baseAI, val.GetFollowTarget(), out var _, out var _)) { return; } Character attacker = hit.GetAttacker(); if (!((Object)(object)attacker != (Object)null) || attacker is Player) { State state = Get(baseAI); Vector3 val2 = hit.m_dir; val2.y = 0f; if (((Vector3)(ref val2)).sqrMagnitude < 0.01f && hit.m_point != Vector3.zero) { val2 = ((Component)character).transform.position - hit.m_point; } if (((Vector3)(ref val2)).sqrMagnitude < 0.01f) { val2 = ((Component)character).transform.forward; } state.HarmDirection = ((Vector3)(ref val2)).normalized; state.EscapeUntil = Time.time + 1.5f; state.HasTarget = PickSafeTarget(baseAI, character, ((Component)character).transform.position, state.HarmDirection, 4f, out state.Target); } } private static bool PlanFormationTarget(BaseAI ai, Character character, GameObject follow, float inner, float outer, Vector3 repulsion, out Vector3 target) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001a: 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_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: 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_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) Vector3 position = ((Component)character).transform.position; Vector3 position2 = follow.transform.position; Vector3 val = position - position2; val.y = 0f; if (((Vector3)(ref val)).sqrMagnitude < 0.04f) { float num = Fraction(((Object)ai).GetInstanceID(), 360f); val = Quaternion.Euler(0f, num, 0f) * Vector3.forward; } ((Vector3)(ref val)).Normalize(); float num2 = Mathf.Lerp(inner, outer, Fraction(((Object)ai).GetInstanceID() * 31, 1f)); Vector3 val2 = ((((Vector3)(ref repulsion)).sqrMagnitude > 0.01f) ? (((Vector3)(ref repulsion)).normalized * Mathf.Min(3.5f, ((Vector3)(ref repulsion)).magnitude)) : Vector3.zero); float[] array = new float[7] { 0f, 28f, -28f, 55f, -55f, 85f, -85f }; for (int i = 0; i < array.Length; i++) { Vector3 val3 = Quaternion.Euler(0f, array[i], 0f) * val; Vector3 grounded = position2 + val3 * num2 + val2; if (TryGround(grounded, out grounded) && FormationSafe(character, position, grounded)) { target = grounded; return true; } } target = position; return false; } private static bool PickSafeTarget(BaseAI ai, Character character, Vector3 origin, Vector3 direction, float distance, out Vector3 target) { //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0073: 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) float[] array = new float[8] { 0f, 35f, -35f, 70f, -70f, 110f, -110f, 180f }; for (int i = 0; i < 2; i++) { float num = ((i == 0) ? distance : Mathf.Min(9f, distance + 3f)); for (int j = 0; j < array.Length; j++) { Vector3 grounded = origin + Quaternion.Euler(0f, array[j], 0f) * direction * num; if (TryGround(grounded, out grounded) && Safe(ai, character, origin, grounded)) { target = grounded; return true; } } } target = origin; return false; } private static Vector3 Separation(Character self, GameObject follow, float desired) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0086: 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_0090: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) Vector3 val = Vector3.zero; List<Character> allCharacters = Character.GetAllCharacters(); for (int i = 0; i < allCharacters.Count; i++) { Character val2 = allCharacters[i]; if ((Object)(object)val2 == (Object)null || (Object)(object)val2 == (Object)(object)self || val2.IsDead() || !val2.IsTamed()) { continue; } BaseAI baseAI = val2.GetBaseAI(); MonsterAI val3 = (MonsterAI)(object)((baseAI is MonsterAI) ? baseAI : null); if ((Object)(object)val3 == (Object)null || (Object)(object)val3.GetFollowTarget() != (Object)(object)follow) { continue; } Vector3 val4 = ((Component)self).transform.position - ((Component)val2).transform.position; val4.y = 0f; float num = desired + val2.GetRadius(); float num2 = ((Vector3)(ref val4)).magnitude; if (!(num2 >= num)) { if (num2 < 0.05f) { float num3 = ((((Object)self).GetInstanceID() < ((Object)val2).GetInstanceID()) ? (-1f) : 1f); val4 = Quaternion.Euler(0f, 90f * num3, 0f) * ((Component)self).transform.forward; num2 = 0.05f; } val += ((Vector3)(ref val4)).normalized * (num - num2); } } return val; } private static bool Safe(BaseAI ai, Character character, Vector3 from, Vector3 target) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000c: 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) if (HazardScanner.Judge(ai, character, target, allowOccupied: true) == RefusalReason.None) { return HazardScanner.SafeFollowSegment(character, from, target); } return false; } private static bool FormationSafe(Character character, Vector3 from, Vector3 target) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) if (!HazardScanner.DangerAt(character, target, allowWater: true)) { return HazardScanner.SafeFollowSegment(character, from, target, allowWater: true); } return false; } private static bool TryGround(Vector3 point, out Vector3 grounded) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: 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) grounded = point; float y = default(float); if ((Object)(object)ZoneSystem.instance == (Object)null || !ZoneSystem.instance.GetSolidHeight(point, ref y, 1000)) { return false; } grounded.y = y; return true; } private static float HorizontalDistance(Vector3 a, Vector3 b) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: 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_0013: Unknown result type (might be due to invalid IL or missing references) float num = a.x - b.x; float num2 = a.z - b.z; return Mathf.Sqrt(num * num + num2 * num2); } private static State Get(BaseAI ai) { int instanceID = ((Object)ai).GetInstanceID(); if (!States.TryGetValue(instanceID, out var value)) { value = (States[instanceID] = new State()); } return value; } private static float Fraction(int seed, float range) { int num = seed ^ (seed << 13); int num2 = num ^ (num >>> 17); return (float)(uint)((num2 ^ (num2 << 5)) & 0xFFFF) / 65535f * range; } } [HarmonyPatch(typeof(BaseAI), "Follow")] internal static class PackFollowPatch { private static bool Prefix(BaseAI __instance, GameObject go, float dt) { return !PackFollowBehaviour.Follow(__instance, go, dt); } } [HarmonyPatch(typeof(Character), "Damage")] internal static class PackFollowDamagePatch { private static void Prefix(Character __instance, HitData hit) { PackFollowBehaviour.Damage(__instance, hit); } } [HarmonyPatch(typeof(MonsterAI), "SetFollowTarget")] internal static class ResetPackFollowTargetPatch { private static void Postfix(MonsterAI __instance) { PackFollowBehaviour.Forget((BaseAI)(object)__instance); if (!((Object)(object)__instance == (Object)null) && TameAIPlugin.AvoidSwimming.Value) { ((BaseAI)__instance).m_avoidWater = (Object)(object)__instance.GetFollowTarget() == (Object)null; } } } [BepInPlugin("nicomods.tameai", "NicoMods TameAI", "1.1.6")] public sealed class TameAIPlugin : BaseUnityPlugin { public const string Guid = "nicomods.tameai"; public const string Name = "NicoMods TameAI"; public const string Version = "1.1.6"; internal static TameAIPlugin Instance; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<bool> ImproveWandering; internal static ConfigEntry<bool> StuckRecovery; internal static ConfigEntry<bool> AnimalSeparation; internal static ConfigEntry<bool> StructureAwareness; internal static ConfigEntry<bool> AvoidSwimming; internal static ConfigEntry<bool> AvoidFire; internal static ConfigEntry<bool> AvoidLava; internal static ConfigEntry<bool> AvoidSmoke; internal static ConfigEntry<bool> AvoidTraps; internal static ConfigEntry<bool> Diagnostics; internal static ConfigEntry<float> StructureMargin; internal static ConfigEntry<float> SeparationMargin; internal static ConfigEntry<float> RestingClearance; internal static ConfigEntry<float> StuckSeconds; internal static ConfigEntry<float> WanderAmplitude; internal static ConfigEntry<bool> CalmHazardEscape; internal static ConfigEntry<bool> SocialVisits; internal static ConfigEntry<bool> PlayerCuriosity; internal static ConfigEntry<float> CuriosityChance; internal static ConfigEntry<float> CuriosityMaxRange; internal static ConfigEntry<bool> PackFollow; internal static ConfigEntry<float> PackInnerRadius; internal static ConfigEntry<float> PackOuterRadius; internal static ConfigEntry<float> PackSpacing; private Harmony harmony; private void Awake() { //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Expected O, but got Unknown //IL_01d1: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Expected O, but got Unknown //IL_020e: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Expected O, but got Unknown //IL_024b: Unknown result type (might be due to invalid IL or missing references) //IL_0255: Expected O, but got Unknown //IL_0288: Unknown result type (might be due to invalid IL or missing references) //IL_0292: Expected O, but got Unknown //IL_0325: Unknown result type (might be due to invalid IL or missing references) //IL_032f: Expected O, but got Unknown //IL_0362: Unknown result type (might be due to invalid IL or missing references) //IL_036c: Expected O, but got Unknown //IL_03bf: Unknown result type (might be due to invalid IL or missing references) //IL_03c9: Expected O, but got Unknown //IL_03fc: Unknown result type (might be due to invalid IL or missing references) //IL_0406: Expected O, but got Unknown //IL_0439: Unknown result type (might be due to invalid IL or missing references) //IL_0443: Expected O, but got Unknown //IL_044e: Unknown result type (might be due to invalid IL or missing references) //IL_0458: Expected O, but got Unknown Instance = this; Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master switch. Off leaves vanilla behaviour untouched."); ImproveWandering = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ImproveWandering", true, "Reject unsafe or unreachable ordinary wander destinations."); StuckRecovery = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "StuckRecovery", true, "Abandon an ordinary wander destination after sustained lack of progress."); AnimalSeparation = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "AnimalSeparation", true, "Avoid choosing a resting destination already occupied by another creature."); StructureAwareness = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "StructureAwareness", true, "Reject wander paths blocked by walls, fences or openings too small for the creature."); AvoidSwimming = ((BaseUnityPlugin)this).Config.Bind<bool>("Water", "AvoidSwimming", true, "Land creatures do not deliberately choose an ordinary wander destination in deep water."); AvoidFire = ((BaseUnityPlugin)this).Config.Bind<bool>("Hazards", "AvoidFire", true, "Enable vanilla fire avoidance flags for parked tames and reject fire destinations."); AvoidLava = ((BaseUnityPlugin)this).Config.Bind<bool>("Hazards", "AvoidLava", true, "Enable vanilla lava avoidance and reject lava destinations."); AvoidSmoke = ((BaseUnityPlugin)this).Config.Bind<bool>("Hazards", "AvoidSmoke", true, "Reject ordinary wander destinations inside smoke."); AvoidTraps = ((BaseUnityPlugin)this).Config.Bind<bool>("Hazards", "AvoidTraps", true, "Reject destinations near an AOE that damages friendly characters."); Diagnostics = ((BaseUnityPlugin)this).Config.Bind<bool>("Advanced", "Diagnostics", false, "Log deduplicated refusals and stuck recoveries for testing."); StructureMargin = ((BaseUnityPlugin)this).Config.Bind<float>("Advanced", "StructureMargin", 0.2f, new ConfigDescription("Extra clearance around the creature for structure checks, metres.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1.5f), Array.Empty<object>())); SeparationMargin = ((BaseUnityPlugin)this).Config.Bind<float>("Advanced", "SeparationMargin", 0.35f, new ConfigDescription("Extra personal space used only while choosing wander destinations, metres.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>())); RestingClearance = ((BaseUnityPlugin)this).Config.Bind<float>("Advanced", "RestingClearance", 2f, new ConfigDescription("Minimum horizontal clearance from vertical player-built pieces for an idle resting destination, metres. Floors below the animal are allowed.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 5f), Array.Empty<object>())); StuckSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Advanced", "StuckSeconds", 3f, new ConfigDescription("Seconds with negligible progress before an ordinary wander is abandoned.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f), Array.Empty<object>())); WanderAmplitude = ((BaseUnityPlugin)this).Config.Bind<float>("Advanced", "WanderAmplitude", 1.15f, new ConfigDescription("Small multiplier applied only to ordinary tame wandering.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 1.5f), Array.Empty<object>())); CalmHazardEscape = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "CalmHazardEscape", true, "Walk a short safe distance away from damaging environment and constructed defences."); SocialVisits = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "SocialVisits", true, "Occasionally let an idle tame approach a nearby tame of the same species."); PlayerCuriosity = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "PlayerCuriosity", true, "Give an idle tame one small chance to walk toward a player it has just detected; never creates Follow."); CuriosityChance = ((BaseUnityPlugin)this).Config.Bind<float>("General", "CuriosityChance", 10f, new ConfigDescription("Chance per newly detected player, percent. No retry until that player leaves detection range.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 25f), Array.Empty<object>())); CuriosityMaxRange = ((BaseUnityPlugin)this).Config.Bind<float>("Advanced", "CuriosityMaxRange", 20f, new ConfigDescription("Maximum range used for calm player curiosity, metres.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(8f, 40f), Array.Empty<object>())); PackFollow = ((BaseUnityPlugin)this).Config.Bind<bool>("Follow", "PackFollow", true, "Replace direct-to-player tame Follow with a spaced, hazard-aware area formation."); PackInnerRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Follow", "PackInnerRadius", 5f, new ConfigDescription("Preferred minimum distance from the followed player, metres.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(3f, 12f), Array.Empty<object>())); PackOuterRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Follow", "PackOuterRadius", 11f, new ConfigDescription("Preferred outer formation radius around the followed player, metres.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(6f, 20f), Array.Empty<object>())); PackSpacing = ((BaseUnityPlugin)this).Config.Bind<float>("Follow", "PackSpacing", 2.5f, new ConfigDescription("Extra spacing sought between following tames, metres.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 6f), Array.Empty<object>())); harmony = new Harmony("nicomods.tameai"); harmony.PatchAll(typeof(TameAIPlugin).Assembly); ((BaseUnityPlugin)this).Logger.LogInfo((object)"NicoMods TameAI 1.1.6 loaded; local-owner navigation safeguards active."); } private void OnDestroy() { if (harmony != null) { harmony.UnpatchSelf(); } WanderSafety.Clear(); LocalBehaviour.Clear(); PackFollowBehaviour.Clear(); Instance = null; } internal static void DebugLog(string text) { if ((Object)(object)Instance != (Object)null && Diagnostics != null && Diagnostics.Value) { ((BaseUnityPlugin)Instance).Logger.LogInfo((object)("[TameAI] " + text)); } } } internal static class WanderSafety { private sealed class State { internal Vector3 Destination; internal Vector3 LastPosition; internal float LastDistance; internal float StalledFor; internal float LastLog; internal RefusalReason LastReason; } private static readonly Dictionary<int, State> States = new Dictionary<int, State>(); private static readonly FieldInfo RandomTarget = AccessTools.Field(typeof(BaseAI), "m_randomMoveTarget"); private static readonly FieldInfo Reached = AccessTools.Field(typeof(BaseAI), "m_reachedRandomMoveTarget"); internal static void Clear() { States.Clear(); } internal static bool IsCurrentWander(BaseAI ai, Vector3 point) { //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_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) if (RandomTarget == null || Reached == null) { return false; } Vector3 val = (Vector3)RandomTarget.GetValue(ai); if (!(bool)Reached.GetValue(ai)) { Vector3 val2 = val - point; return ((Vector3)(ref val2)).sqrMagnitude < 0.04f; } return false; } internal static void Observe(BaseAI ai, Character character, Vector3 destination, float dt, bool arrived) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0097: 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_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) if (!TameAIPlugin.StuckRecovery.Value || arrived) { Forget(ai); return; } int instanceID = ((Object)ai).GetInstanceID(); if (States.TryGetValue(instanceID, out var value)) { Vector3 val = value.Destination - destination; if (!(((Vector3)(ref val)).sqrMagnitude > 0.25f)) { float num = DistanceXZ(value.LastPosition, ((Component)character).transform.position); float num2 = DistanceXZ(((Component)character).transform.position, destination); float num3 = value.LastDistance - num2; if (num < 0.05f && num3 < 0.03f) { value.StalledFor += Mathf.Max(0f, dt); } else { value.StalledFor = Mathf.Max(0f, value.StalledFor - dt * 2f); } value.LastPosition = ((Component)character).transform.position; value.LastDistance = num2; if (!(value.StalledFor < TameAIPlugin.StuckSeconds.Value)) { ai.ResetRandomMovement(); States.Remove(instanceID); TameAIPlugin.DebugLog(character.m_name + " abandoned a blocked wander destination after " + TameAIPlugin.StuckSeconds.Value.ToString("0.0") + " s."); } return; } } States[instanceID] = new State { Destination = destination, LastPosition = ((Component)character).transform.position, LastDistance = DistanceXZ(((Component)character).transform.position, destination) }; } internal static void Refused(BaseAI ai, Character character, RefusalReason reason) { int instanceID = ((Object)ai).GetInstanceID(); if (!States.TryGetValue(instanceID, out var value)) { value = (States[instanceID] = new State()); } float time = Time.time; if (value.LastReason != reason || time - value.LastLog >= 10f) { value.LastReason = reason; value.LastLog = time; TameAIPlugin.DebugLog(character.m_name + " wander destination refused: " + reason.ToString() + "."); } } internal static void Forget(BaseAI ai) { if ((Object)(object)ai != (Object)null) { States.Remove(((Object)ai).GetInstanceID()); } } private static float DistanceXZ(Vector3 a, Vector3 b) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: 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_0013: Unknown result type (might be due to invalid IL or missing references) float num = a.x - b.x; float num2 = a.z - b.z; return Mathf.Sqrt(num * num + num2 * num2); } } [HarmonyPatch(typeof(BaseAI), "IsValidRandomMovePoint")] internal static class ValidateWanderDestinationPatch { private static void Postfix(BaseAI __instance, Vector3 point, ref bool __result) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) if (__result && TameAIPlugin.ImproveWandering.Value && Authority.IsLocalParkedTame(__instance, out var character, out var _)) { HazardScanner.ArmVanilla(__instance); RefusalReason refusalReason = HazardScanner.Judge(__instance, character, point); if (refusalReason == RefusalReason.None) { refusalReason = HazardScanner.JudgeRestingArea(character, point); } if (refusalReason != RefusalReason.None) { __result = false; WanderSafety.Refused(__instance, character, refusalReason); } } } } [HarmonyPatch(typeof(BaseAI), "MoveTo")] internal static class WanderProgressPatch { private static void Postfix(BaseAI __instance, float dt, Vector3 point, bool __result) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) if (!WanderSafety.IsCurrentWander(__instance, point)) { return; } if (!Authority.IsLocalParkedTame(__instance, out var character, out var _)) { WanderSafety.Forget(__instance); return; } if (__result) { RefusalReason refusalReason = HazardScanner.JudgeRestingArea(character, point); if (refusalReason != RefusalReason.None) { __instance.ResetRandomMovement(); WanderSafety.Refused(__instance, character, refusalReason); return; } } WanderSafety.Observe(__instance, character, point, dt, __result); } } [HarmonyPatch(typeof(BaseAI), "OnDestroy")] internal static class ForgetDestroyedAiPatch { private static void Prefix(BaseAI __instance) { WanderSafety.Forget(__instance); LocalBehaviour.Forget(__instance); PackFollowBehaviour.Forget(__instance); } } [HarmonyPatch(typeof(BaseAI), "Awake")] internal static class ArmExistingTamePatch { private static void Postfix(BaseAI __instance) { if (Authority.IsLocalParkedTame(__instance, out var _, out var _)) { HazardScanner.ArmVanilla(__instance); } } } [HarmonyPatch(typeof(Character), "SetTamed")] internal static class ArmNewTamePatch { private static void Postfix(Character __instance, bool tamed) { if (tamed && !((Object)(object)__instance == (Object)null)) { BaseAI baseAI = __instance.GetBaseAI(); if (Authority.IsLocalParkedTame(baseAI, out var _, out var _)) { HazardScanner.ArmVanilla(baseAI); } } } } }