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 Unfair Enemies Tweaks v1.0.0
BepInEx/plugins/UnfairMonsters.dll
Decompiled a year agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Photon.Pun; using UnityEngine; using UnityEngine.AI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp-firstpass")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: IgnoresAccessChecksTo("Autodesk.Fbx")] [assembly: IgnoresAccessChecksTo("Facepunch.Steamworks.Win64")] [assembly: IgnoresAccessChecksTo("FbxBuildTestAssets")] [assembly: IgnoresAccessChecksTo("Klattersynth")] [assembly: IgnoresAccessChecksTo("Photon3Unity3D")] [assembly: IgnoresAccessChecksTo("PhotonChat")] [assembly: IgnoresAccessChecksTo("PhotonRealtime")] [assembly: IgnoresAccessChecksTo("PhotonUnityNetworking")] [assembly: IgnoresAccessChecksTo("PhotonUnityNetworking.Utilities")] [assembly: IgnoresAccessChecksTo("PhotonVoice.API")] [assembly: IgnoresAccessChecksTo("PhotonVoice")] [assembly: IgnoresAccessChecksTo("PhotonVoice.PUN")] [assembly: IgnoresAccessChecksTo("SingularityGroup.HotReload.Runtime")] [assembly: IgnoresAccessChecksTo("SingularityGroup.HotReload.Runtime.Public")] [assembly: IgnoresAccessChecksTo("Sirenix.OdinInspector.Attributes")] [assembly: IgnoresAccessChecksTo("Sirenix.Serialization.Config")] [assembly: IgnoresAccessChecksTo("Sirenix.Serialization")] [assembly: IgnoresAccessChecksTo("Sirenix.Utilities")] [assembly: IgnoresAccessChecksTo("Unity.AI.Navigation")] [assembly: IgnoresAccessChecksTo("Unity.Formats.Fbx.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.InputSystem")] [assembly: IgnoresAccessChecksTo("Unity.InputSystem.ForUI")] [assembly: IgnoresAccessChecksTo("Unity.Postprocessing.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.ShaderLibrary")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary")] [assembly: IgnoresAccessChecksTo("Unity.TextMeshPro")] [assembly: IgnoresAccessChecksTo("Unity.Timeline")] [assembly: IgnoresAccessChecksTo("Unity.VisualScripting.Antlr3.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.VisualScripting.Core")] [assembly: IgnoresAccessChecksTo("Unity.VisualScripting.Flow")] [assembly: IgnoresAccessChecksTo("Unity.VisualScripting.State")] [assembly: IgnoresAccessChecksTo("UnityEngine.ARModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.NVIDIAModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.UI")] [assembly: IgnoresAccessChecksTo("websocket-sharp")] [assembly: AssemblyCompany("PlusBlankPlus")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("VisionMonsters")] [assembly: AssemblyTitle("VisionMonsters")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.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 VisionMonsters { public class EnemyAIHelper : MonoBehaviour { [CompilerGenerated] private sealed class <ScanLoop>d__13 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public EnemyAIHelper <>4__this; object? IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object? IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <ScanLoop>d__13(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Expected O, but got Unknown //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; if (Plugin.RadarScanInterval == null) { ManualLogSource? log = <>4__this._log; if (log != null) { log.LogError((object)"ScanLoop cannot start: RadarScanInterval config is null."); } return false; } if (<>4__this.scanWaitSeconds == null) { <>4__this.scanWaitSeconds = new WaitForSeconds(Plugin.RadarScanInterval.Value); } <>2__current = (object)new WaitForSeconds(Random.Range(1f, Plugin.RadarScanInterval.Value)); <>1__state = 1; return true; case 1: <>1__state = -1; break; case 2: <>1__state = -1; break; } if (((Behaviour)<>4__this).enabled && Plugin.EnableCheatingAI != null && Plugin.EnableCheatingAI.Value && SemiFunc.IsMasterClientOrSingleplayer() && (Object)(object)<>4__this.enemyRef != (Object)null && ((Behaviour)<>4__this.enemyRef).isActiveAndEnabled) { <>4__this.ScanForPlayers(); } <>2__current = <>4__this.scanWaitSeconds; <>1__state = 2; return true; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } internal Enemy? enemyRef; private Coroutine? _scanCoroutine; private ManualLogSource? _log; public Vector3 lastKnownPlayerPosition = Vector3.zero; public float timeSincePlayerKnown = float.MaxValue; public bool hasMemory = false; private WaitForSeconds? scanWaitSeconds; private void Awake() { //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Expected O, but got Unknown _log = Plugin.Log; enemyRef = ((Component)this).GetComponent<Enemy>(); if ((Object)(object)enemyRef == (Object)null) { ManualLogSource? log = _log; if (log != null) { log.LogError((object)("EnemyAIHelper could not find Enemy component on " + ((Object)((Component)this).gameObject).name)); } ((Behaviour)this).enabled = false; } else if (Plugin.RadarScanInterval == null || Plugin.MemoryDuration == null || Plugin.RadarScanRadius == null || Plugin.EnableCheatingAI == null) { ManualLogSource? log2 = _log; if (log2 != null) { log2.LogError((object)("EnemyAIHelper cannot run on " + ((Object)((Component)this).gameObject).name + ": required config entries are null.")); } ((Behaviour)this).enabled = false; } else { scanWaitSeconds = new WaitForSeconds(Plugin.RadarScanInterval.Value); timeSincePlayerKnown = float.MaxValue; } } private void OnEnable() { //IL_0011: 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) if (((Behaviour)this).enabled) { lastKnownPlayerPosition = Vector3.zero; timeSincePlayerKnown = float.MaxValue; hasMemory = false; if (_scanCoroutine == null && (Object)(object)enemyRef != (Object)null) { _scanCoroutine = ((MonoBehaviour)this).StartCoroutine(ScanLoop()); } } } private void OnDisable() { if (_scanCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_scanCoroutine); _scanCoroutine = null; } } private void Update() { if (!((Behaviour)this).enabled || (Object)(object)enemyRef == (Object)null || Plugin.EnableCheatingAI == null || !Plugin.EnableCheatingAI.Value || !SemiFunc.IsMasterClientOrSingleplayer()) { return; } if (hasMemory) { if (Plugin.MemoryDuration != null) { timeSincePlayerKnown += Time.deltaTime; if (timeSincePlayerKnown >= Plugin.MemoryDuration.Value) { hasMemory = false; } } else { hasMemory = false; } } if (HasFreshMemory()) { TryForceInvestigation(); } } public void UpdateMemory(Vector3 position) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) if (((Behaviour)this).enabled && Plugin.EnableCheatingAI != null && Plugin.EnableCheatingAI.Value) { lastKnownPlayerPosition = position; timeSincePlayerKnown = 0f; hasMemory = true; } } public bool HasFreshMemory() { if (!((Behaviour)this).enabled || Plugin.MemoryDuration == null) { return false; } return hasMemory && timeSincePlayerKnown < Plugin.MemoryDuration.Value; } [IteratorStateMachine(typeof(<ScanLoop>d__13))] private IEnumerator ScanLoop() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <ScanLoop>d__13(0) { <>4__this = this }; } private void ScanForPlayers() { //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_005c: 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_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_00e1: 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) if ((Object)(object)enemyRef == (Object)null || (Object)(object)enemyRef.CenterTransform == (Object)null || Plugin.RadarScanRadius == null) { return; } float value = Plugin.RadarScanRadius.Value; Vector3 position = enemyRef.CenterTransform.position; PlayerAvatar val = null; float num = float.MaxValue; Collider[] array = Physics.OverlapSphere(position, value, LayerMask.GetMask(new string[1] { "Player" }), (QueryTriggerInteraction)1); Collider[] array2 = array; foreach (Collider val2 in array2) { if (((Component)val2).transform.IsChildOf(((Component)enemyRef).transform)) { continue; } PlayerAvatar componentInParent = ((Component)val2).GetComponentInParent<PlayerAvatar>(); if ((Object)(object)componentInParent != (Object)null && !componentInParent.isDisabled) { Vector3 val3 = ((Component)componentInParent).transform.position - position; float sqrMagnitude = ((Vector3)(ref val3)).sqrMagnitude; if (sqrMagnitude < num) { num = sqrMagnitude; val = componentInParent; } } } if ((Object)(object)val != (Object)null) { UpdateMemory(((Component)val).transform.position); } } private void TryForceInvestigation() { //IL_0038: 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_005b: 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_0082: Invalid comparison between Unknown and I4 //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_015e: 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_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)enemyRef == (Object)null || (Object)(object)EnemyDirector.instance == (Object)null || !hasMemory || !CanInterruptForInvestigation(enemyRef.CurrentState)) { return; } float num = Vector3.Distance(((Component)enemyRef).transform.position, lastKnownPlayerPosition); if (!(num > 5f)) { return; } if ((int)enemyRef.CurrentState == 7) { EnemyNavMeshAgent navMeshAgent = enemyRef.NavMeshAgent; if (navMeshAgent != null && navMeshAgent.HasPath()) { if (!(Vector3.Distance(enemyRef.NavMeshAgent.Agent.destination, lastKnownPlayerPosition) > 3f)) { return; } if ((Object)(object)enemyRef.StateInvestigate != (Object)null) { enemyRef.StateInvestigate.Set(lastKnownPlayerPosition); return; } EnemyNavMeshAgent navMeshAgent2 = enemyRef.NavMeshAgent; if (navMeshAgent2 != null) { navMeshAgent2.SetDestination(lastKnownPlayerPosition); } return; } } if ((Object)(object)enemyRef.StateInvestigate != (Object)null) { enemyRef.StateInvestigate.Set(lastKnownPlayerPosition); } else { EnemyDirector.instance.SetInvestigate(lastKnownPlayerPosition, 10f); } } private bool CanInterruptForInvestigation(EnemyState currentState) { //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_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0004: 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_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected I4, but got Unknown switch (currentState - 1) { case 1: case 5: case 6: return true; case 0: case 2: case 3: case 4: case 8: case 9: case 10: return false; default: return true; } } } public enum Formation { Line, Triangle, Box, Circle, Loose } public class EnemyGroup { public Enemy? Leader { get; private set; } public List<GroupAIHelper> Followers { get; private set; } = new List<GroupAIHelper>(); public Formation CurrentFormation { get; set; } public Vector3 CurrentPatrolTarget { get; set; } public EnemyType GroupEnemyType { get; private set; } public bool IsValid { get; private set; } = true; public EnemyGroup(Enemy leader, Formation formation) { //IL_003a: 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) if ((Object)(object)leader == (Object)null) { throw new ArgumentNullException("leader"); } Leader = leader; GroupEnemyType = leader.Type; CurrentFormation = formation; CurrentPatrolTarget = ((Component)leader).transform.position; } public void AddFollower(GroupAIHelper follower) { if ((Object)(object)follower != (Object)null && (Object)(object)Leader != (Object)null && (Object)(object)follower.enemyRef != (Object)(object)Leader && !Followers.Contains(follower)) { Followers.Add(follower); follower.AssignToGroup(this, isLeader: false); } } public void RemoveMember(GroupAIHelper member) { if ((Object)(object)member != (Object)null && Followers.Remove(member)) { if (Plugin.MinGroupSize != null && Followers.Count < Plugin.MinGroupSize.Value - 1) { DisbandGroup(); } else { AssignFormationOffsets(); } } } public void DisbandGroup() { if (!IsValid) { return; } IsValid = false; Enemy? leader = Leader; if (leader != null) { ((Component)leader).GetComponent<GroupAIHelper>()?.LeaveGroup(); } Leader = null; foreach (GroupAIHelper item in Followers.ToList()) { item?.LeaveGroup(); } Followers.Clear(); } public void AssignFormationOffsets() { //IL_0066: 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_02e5: Unknown result type (might be due to invalid IL or missing references) if (!SemiFunc.IsMasterClientOrSingleplayer() || (Object)(object)Leader == (Object)null || !IsValid) { return; } int count = Followers.Count; float num = 2.5f; GroupAIHelper component = ((Component)Leader).GetComponent<GroupAIHelper>(); if ((Object)(object)component == (Object)null) { DisbandGroup(); return; } component.SetFormationOffset(Vector3.zero); component.AssignToGroup(this, isLeader: true); Formation formation = CurrentFormation; bool flag = count >= 2; bool flag2 = count >= 3; if (formation == Formation.Triangle && !flag) { formation = Formation.Line; } if (formation == Formation.Box && !flag2) { formation = Formation.Triangle; } if (formation == Formation.Triangle && !flag) { formation = Formation.Line; } for (int i = 0; i < count; i++) { Vector3 zero = Vector3.zero; switch (formation) { case Formation.Line: ((Vector3)(ref zero))..ctor(0f, 0f, (0f - num) * (float)(i + 1)); break; case Formation.Triangle: switch (i) { case 0: ((Vector3)(ref zero))..ctor((0f - num) * 0.8f, 0f, 0f - num); break; case 1: ((Vector3)(ref zero))..ctor(num * 0.8f, 0f, 0f - num); break; default: ((Vector3)(ref zero))..ctor(0f, 0f, (0f - num) * (1f + (float)(i - 1) * 0.8f)); break; } break; case Formation.Box: switch (i) { case 0: ((Vector3)(ref zero))..ctor(num, 0f, 0f); break; case 1: ((Vector3)(ref zero))..ctor(0f, 0f, 0f - num); break; case 2: ((Vector3)(ref zero))..ctor(num, 0f, 0f - num); break; default: ((Vector3)(ref zero))..ctor(num * 0.5f, 0f, (0f - num) * (1f + (float)(i - 2) * 0.8f)); break; } break; case Formation.Circle: { float num4 = 360f / (float)(count + 1); float num5 = num * 0.6f * (float)(count + 1) / MathF.PI; float num6 = (float)(i + 1) * num4 * (MathF.PI / 180f); ((Vector3)(ref zero))..ctor(Mathf.Sin(num6) * num5, 0f, Mathf.Cos(num6) * num5 - num5 * 0.5f); break; } default: { float num2 = Random.Range(-75f, 75f) * (MathF.PI / 180f); float num3 = num * (1f + (float)i * 0.3f) * Random.Range(0.7f, 1.3f); ((Vector3)(ref zero))..ctor(Mathf.Sin(num2) * num3 * 0.6f, 0f, (0f - Mathf.Cos(num2)) * num3); break; } } Followers[i].SetFormationOffset(zero); Followers[i].AssignToGroup(this, isLeader: false); } } } [RequireComponent(typeof(PhotonView))] public class GroupAIHelper : MonoBehaviourPun { internal Enemy? enemyRef; internal NavMeshAgent? agent; private ManualLogSource? _log; private PhotonView? pv; public EnemyGroup? CurrentGroup { get; private set; } public bool IsInGroup => CurrentGroup != null && CurrentGroup.IsValid; public bool IsLeader { get; private set; } = false; public Vector3 FormationOffset { get; private set; } = Vector3.zero; public Vector3 CurrentWorldTargetPosition { get; private set; } = Vector3.zero; private void Awake() { _log = Plugin.Log; enemyRef = ((Component)this).GetComponent<Enemy>(); agent = ((Component)this).GetComponent<NavMeshAgent>(); pv = ((Component)this).GetComponent<PhotonView>(); if ((Object)(object)enemyRef == (Object)null) { ManualLogSource? log = _log; if (log != null) { log.LogError((object)("GroupAIHelper missing Enemy on " + ((Object)((Component)this).gameObject).name)); } ((Behaviour)this).enabled = false; } else if ((Object)(object)pv == (Object)null) { ManualLogSource? log2 = _log; if (log2 != null) { log2.LogError((object)("GroupAIHelper missing PhotonView on " + ((Object)((Component)this).gameObject).name)); } ((Behaviour)this).enabled = false; } else if ((Object)(object)agent == (Object)null && enemyRef.HasNavMeshAgent) { agent = ((Component)this).GetComponentInChildren<NavMeshAgent>(); } } private void Start() { if (((Behaviour)this).enabled && SemiFunc.IsMasterClientOrSingleplayer()) { ConfigEntry<bool>? enableGrouping = Plugin.EnableGrouping; if (enableGrouping != null && enableGrouping.Value) { ((MonoBehaviour)this).Invoke("RegisterWithManager", Random.Range(0.5f, 1f)); } } } private void OnDestroy() { if (((Behaviour)this).enabled && SemiFunc.IsMasterClientOrSingleplayer() && (Object)(object)GroupPatrolManager.Instance != (Object)null) { ConfigEntry<bool>? enableGrouping = Plugin.EnableGrouping; if (enableGrouping != null && enableGrouping.Value) { GroupPatrolManager.Instance.UnregisterEnemy(this); } } LeaveGroupInternal(); } private void RegisterWithManager() { if ((Object)(object)GroupPatrolManager.Instance != (Object)null && (Object)(object)enemyRef != (Object)null) { GroupPatrolManager.Instance.RegisterEnemy(this); return; } ManualLogSource? log = _log; if (log != null) { Enemy? obj = enemyRef; log.LogError((object)("Failed to register " + ((obj != null) ? ((Object)obj).name : null) + ": GroupPatrolManager instance is null.")); } } public void AssignToGroup(EnemyGroup group, bool isLeader) { //IL_0085: Unknown result type (might be due to invalid IL or missing references) if (!SemiFunc.IsMasterClientOrSingleplayer() || (Object)(object)pv == (Object)null) { return; } CurrentGroup = group; IsLeader = isLeader; int num = (((Object)(object)group.Leader != (Object)null) ? ((MonoBehaviourPun)group.Leader).photonView.ViewID : (-1)); if (num != -1) { pv.RPC("AssignToGroupRPC", (RpcTarget)6, new object[3] { num, isLeader, FormationOffset }); return; } ManualLogSource? log = _log; if (log != null) { Enemy? obj = enemyRef; log.LogError((object)("HOST: Cannot send AssignToGroupRPC for " + ((obj != null) ? ((Object)obj).name : null) + " because leader is null!")); } } [PunRPC] public void AssignToGroupRPC(int leaderViewID, bool isLeader, Vector3 formationOffset) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) IsLeader = isLeader; FormationOffset = formationOffset; PhotonView val = PhotonView.Find(leaderViewID); Enemy val2 = ((val != null) ? ((Component)val).GetComponent<Enemy>() : null); if ((Object)(object)val2 != (Object)null) { CurrentGroup = null; return; } ManualLogSource? log = _log; if (log != null) { PhotonView? obj = pv; log.LogError((object)$"CLIENT {((obj != null) ? new int?(obj.ViewID) : null)}: Received AssignToGroupRPC but could not find leader with ViewID {leaderViewID}!"); } LeaveGroupInternal(); } public void SetFormationOffset(Vector3 offset) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) FormationOffset = offset; } public void LeaveGroup() { if (SemiFunc.IsMasterClientOrSingleplayer() && !((Object)(object)pv == (Object)null)) { if (CurrentGroup != null || IsLeader) { pv.RPC("LeaveGroupRPC", (RpcTarget)6, Array.Empty<object>()); } LeaveGroupInternal(); } } [PunRPC] public void LeaveGroupRPC() { LeaveGroupInternal(); } private void LeaveGroupInternal() { //IL_0012: 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) CurrentGroup = null; IsLeader = false; FormationOffset = Vector3.zero; CurrentWorldTargetPosition = Vector3.zero; } public bool TryGroupMovement() { //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_014d: 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_0157: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_015e: 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_0183: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01e3: Unknown result type (might be due to invalid IL or missing references) //IL_01ea: Expected O, but got Unknown //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_0201: Unknown result type (might be due to invalid IL or missing references) //IL_0207: Invalid comparison between Unknown and I4 //IL_0234: Unknown result type (might be due to invalid IL or missing references) //IL_0239: Unknown result type (might be due to invalid IL or missing references) //IL_021a: Unknown result type (might be due to invalid IL or missing references) //IL_0260: Unknown result type (might be due to invalid IL or missing references) //IL_026d: Unknown result type (might be due to invalid IL or missing references) //IL_0273: Invalid comparison between Unknown and I4 //IL_0285: Unknown result type (might be due to invalid IL or missing references) bool flag = !IsLeader && CurrentGroup != null && CurrentGroup.IsValid && (Object)(object)CurrentGroup.Leader != (Object)null; if (!SemiFunc.IsMasterClientOrSingleplayer()) { PhotonView val = PhotonView.Find((((Component)this).GetComponent<PhotonView>().InstantiationData != null) ? ((int)((Component)this).GetComponent<PhotonView>().InstantiationData[0]) : (-1)); flag = !IsLeader && (Object)(object)val != (Object)null; flag = !IsLeader && FormationOffset != Vector3.zero; } if (flag) { if (SemiFunc.IsMasterClientOrSingleplayer()) { if ((Object)(object)agent == (Object)null || !((Behaviour)agent).enabled || !agent.isOnNavMesh || CurrentGroup == null || (Object)(object)CurrentGroup.Leader == (Object)null || !((Behaviour)CurrentGroup.Leader).isActiveAndEnabled) { return false; } Vector3 position = ((Component)CurrentGroup.Leader).transform.position; Quaternion rotation = ((Component)CurrentGroup.Leader).transform.rotation; Vector3 val2 = position + rotation * FormationOffset; NavMeshHit val3 = default(NavMeshHit); if (NavMesh.SamplePosition(val2, ref val3, 1f, -1)) { CurrentWorldTargetPosition = ((NavMeshHit)(ref val3)).position; } else { NavMeshHit val4 = default(NavMeshHit); if (!NavMesh.SamplePosition(position, ref val4, 1f, -1)) { return false; } CurrentWorldTargetPosition = ((NavMeshHit)(ref val4)).position; } if (Vector3.Distance(((Component)this).transform.position, CurrentWorldTargetPosition) > agent.stoppingDistance + 0.5f) { NavMeshPath val5 = new NavMeshPath(); if (agent.CalculatePath(CurrentWorldTargetPosition, val5) && (int)val5.status == 0) { agent.SetDestination(CurrentWorldTargetPosition); return true; } if (Vector3.Distance(((Component)this).transform.position, position) > agent.stoppingDistance + 1f && agent.CalculatePath(position, val5) && (int)val5.status == 0) { agent.SetDestination(position); return true; } return false; } if (agent.hasPath) { agent.ResetPath(); } return false; } if ((Object)(object)agent != (Object)null && ((Behaviour)agent).enabled && agent.isOnNavMesh && agent.hasPath) { agent.ResetPath(); } return true; } return false; } public void BreakFormationTemporarily() { if (SemiFunc.IsMasterClientOrSingleplayer()) { if (IsInGroup) { CurrentGroup?.RemoveMember(this); } } else { LeaveGroupInternal(); } } } public class GroupPatrolManager : MonoBehaviour { private List<EnemyGroup> activeGroups = new List<EnemyGroup>(); private Dictionary<EnemyType, List<GroupAIHelper>> potentialGroupMembers = new Dictionary<EnemyType, List<GroupAIHelper>>(); private float groupCheckTimer = 0f; private ManualLogSource? _log; public static GroupPatrolManager? Instance { get; private set; } private void Awake() { if ((Object)(object)Instance != (Object)null && (Object)(object)Instance != (Object)(object)this) { Object.Destroy((Object)(object)((Component)this).gameObject); return; } Instance = this; _log = Plugin.Log; ManualLogSource? log = _log; if (log != null) { log.LogInfo((object)"GroupPatrolManager Initialized."); } if (Plugin.GroupCheckInterval == null) { ManualLogSource? log2 = _log; if (log2 != null) { log2.LogError((object)"GroupPatrolManager cannot run: GroupCheckInterval config is null."); } ((Behaviour)this).enabled = false; } else { groupCheckTimer = Plugin.GroupCheckInterval.Value; } } private void OnDestroy() { if (!((Object)(object)Instance == (Object)(object)this)) { return; } Instance = null; ManualLogSource? log = _log; if (log != null) { log.LogInfo((object)"GroupPatrolManager Cleaning Up Groups..."); } foreach (EnemyGroup item in activeGroups.ToList()) { item.DisbandGroup(); } activeGroups.Clear(); potentialGroupMembers.Clear(); ManualLogSource? log2 = _log; if (log2 != null) { log2.LogInfo((object)"GroupPatrolManager Destroyed."); } } private void Update() { if (!((Behaviour)this).enabled || !SemiFunc.IsMasterClientOrSingleplayer() || Plugin.EnableGrouping == null) { return; } if (!Plugin.EnableGrouping.Value) { if (activeGroups.Count <= 0) { return; } ManualLogSource? log = _log; if (log != null) { log.LogInfo((object)"Grouping disabled, disbanding..."); } foreach (EnemyGroup item in activeGroups.ToList()) { item.DisbandGroup(); } activeGroups.Clear(); potentialGroupMembers.Clear(); return; } groupCheckTimer -= Time.deltaTime; if (groupCheckTimer <= 0f) { TryFormGroups(); CleanupInvalidGroups(); if (Plugin.GroupCheckInterval != null) { groupCheckTimer = Plugin.GroupCheckInterval.Value + Random.Range(-1f, 1f); } else { groupCheckTimer = 10f; } } UpdateGroupPatrols(); } public void RegisterEnemy(GroupAIHelper helper) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: 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_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) if (((Behaviour)this).enabled && !((Object)(object)helper?.enemyRef == (Object)null)) { EnemyType type = helper.enemyRef.Type; if (!potentialGroupMembers.ContainsKey(type)) { potentialGroupMembers[type] = new List<GroupAIHelper>(); } if (!potentialGroupMembers[type].Contains(helper) && !helper.IsInGroup) { potentialGroupMembers[type].Add(helper); } } } public void UnregisterEnemy(GroupAIHelper helper) { //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: 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) if (!((Behaviour)this).enabled || (Object)(object)helper?.enemyRef == (Object)null) { return; } if (helper.IsInGroup && helper.CurrentGroup != null) { if (helper.IsLeader) { helper.CurrentGroup.DisbandGroup(); } else { helper.CurrentGroup.RemoveMember(helper); } } EnemyType type = helper.enemyRef.Type; if (potentialGroupMembers.ContainsKey(type)) { potentialGroupMembers[type].Remove(helper); } } private void TryFormGroups() { //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_0061: 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_0231: Unknown result type (might be due to invalid IL or missing references) if (Plugin.MinGroupSize == null || Plugin.MaxGroupSize == null) { return; } int value = Plugin.MinGroupSize.Value; int value2 = Plugin.MaxGroupSize.Value; List<EnemyType> list = potentialGroupMembers.Keys.ToList(); foreach (EnemyType item in list) { if (!potentialGroupMembers.ContainsKey(item)) { continue; } List<GroupAIHelper> list2 = potentialGroupMembers[item]; list2.RemoveAll((GroupAIHelper h) => (Object)(object)h == (Object)null || (Object)(object)h.enemyRef == (Object)null || !((Behaviour)h.enemyRef).isActiveAndEnabled || h.IsInGroup); while (list2.Count >= value) { int num = Mathf.Min(Random.Range(value, value2 + 1), list2.Count); List<GroupAIHelper> list3 = list2.Where((GroupAIHelper p) => (Object)(object)p != (Object)null && (Object)(object)p.enemyRef != (Object)null && ((Behaviour)p.enemyRef).isActiveAndEnabled && !p.IsInGroup).ToList(); if (list3.Count < value) { break; } num = Mathf.Min(num, list3.Count); List<GroupAIHelper> list4 = list3.Take(num).ToList(); GroupAIHelper groupAIHelper = list4[0]; if ((Object)(object)groupAIHelper.enemyRef == (Object)null) { ManualLogSource? log = _log; if (log != null) { log.LogError((object)"Selected leader has null enemyRef!"); } list2.Remove(groupAIHelper); continue; } Formation formation = ParseFormation(Plugin.FormationType?.Value ?? "Loose"); EnemyGroup enemyGroup = new EnemyGroup(groupAIHelper.enemyRef, formation); groupAIHelper.AssignToGroup(enemyGroup, isLeader: true); for (int i = 1; i < list4.Count; i++) { enemyGroup.AddFollower(list4[i]); } foreach (GroupAIHelper item2 in list4) { list2.Remove(item2); } activeGroups.Add(enemyGroup); ManualLogSource? log2 = _log; if (log2 != null) { log2.LogInfo((object)$"Formed group for {item} size {num}."); } } } } private void CleanupInvalidGroups() { activeGroups.RemoveAll((EnemyGroup group) => !group.IsValid || (Object)(object)group.Leader == (Object)null || !((Behaviour)group.Leader).isActiveAndEnabled); } private void UpdateGroupPatrols() { //IL_00bd: 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_00df: Expected O, but got Unknown //IL_00e7: 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_00fd: Invalid comparison between Unknown and I4 //IL_0111: 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) foreach (EnemyGroup activeGroup in activeGroups) { if (!activeGroup.IsValid || (Object)(object)activeGroup.Leader == (Object)null || !((Behaviour)activeGroup.Leader).isActiveAndEnabled) { continue; } NavMeshAgent val = activeGroup.Leader.NavMeshAgent?.Agent; if (!((Object)(object)val != (Object)null) || !((Behaviour)val).enabled || !val.isOnNavMesh || (val.hasPath && !(val.remainingDistance < val.stoppingDistance + 1.5f))) { continue; } LevelPoint val2 = FindNewPatrolPoint(((Component)activeGroup.Leader).transform.position); if ((Object)(object)val2 != (Object)null) { NavMeshPath val3 = new NavMeshPath(); if (val.CalculatePath(((Component)val2).transform.position, val3) && (int)val3.status == 0) { activeGroup.CurrentPatrolTarget = ((Component)val2).transform.position; val.SetDestination(activeGroup.CurrentPatrolTarget); } else if (val.hasPath) { val.ResetPath(); } } else if (val.hasPath) { val.ResetPath(); } } } private LevelPoint? FindNewPatrolPoint(Vector3 currentPos) { //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) if (LevelGenerator.Instance?.LevelPathPoints == null || LevelGenerator.Instance.LevelPathPoints.Count == 0) { return null; } List<LevelPoint> list = LevelGenerator.Instance.LevelPathPoints.Where((LevelPoint p) => (Object)(object)p != (Object)null && Vector3.Distance(((Component)p).transform.position, currentPos) > 10f).ToList(); if (list.Count > 0) { return list[Random.Range(0, list.Count)]; } return LevelGenerator.Instance.LevelPathPoints[Random.Range(0, LevelGenerator.Instance.LevelPathPoints.Count)]; } private Formation ParseFormation(string formationName) { if (Enum.TryParse<Formation>(formationName, ignoreCase: true, out var result)) { return result; } ManualLogSource? log = _log; if (log != null) { log.LogWarning((object)("Invalid FormationType '" + formationName + "'. Defaulting to Loose.")); } return Formation.Loose; } } [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("PlusBlankPlus.VisionMonsters", "Vision and Hearing Monsters", "1.3.3")] public class Plugin : BaseUnityPlugin { public const string PluginGuid = "PlusBlankPlus.VisionMonsters"; public const string PluginName = "Vision and Hearing Monsters"; public const string PluginVersion = "1.3.3"; public static ConfigEntry<float>? VisionDistanceMultiplier; public static ConfigEntry<float>? CloseVisionDistanceMultiplier; public static ConfigEntry<float>? CrouchCloseVisionDistanceMultiplier; public static ConfigEntry<float>? PhysObjectVisionRadiusMultiplier; public static ConfigEntry<float>? SoundSensitivityMultiplier; public static ConfigEntry<bool>? EnableCheatingAI; public static ConfigEntry<float>? RadarScanRadius; public static ConfigEntry<float>? RadarScanInterval; public static ConfigEntry<float>? MemoryDuration; public static ConfigEntry<bool>? EnableGrouping; public static ConfigEntry<int>? MinGroupSize; public static ConfigEntry<int>? MaxGroupSize; public static ConfigEntry<float>? GroupCheckInterval; public static ConfigEntry<string>? FormationType; internal static ManualLogSource? Log; internal static bool firstPatchLogDone; private void Awake() { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Expected O, but got Unknown //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Expected O, but got Unknown //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Expected O, but got Unknown //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Expected O, but got Unknown //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Expected O, but got Unknown //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Expected O, but got Unknown //IL_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Expected O, but got Unknown //IL_0231: Unknown result type (might be due to invalid IL or missing references) //IL_023b: Expected O, but got Unknown //IL_0283: Unknown result type (might be due to invalid IL or missing references) //IL_028d: Expected O, but got Unknown //IL_02b5: Unknown result type (might be due to invalid IL or missing references) //IL_02bf: Expected O, but got Unknown //IL_02f2: Unknown result type (might be due to invalid IL or missing references) //IL_02fc: Expected O, but got Unknown //IL_0353: Unknown result type (might be due to invalid IL or missing references) //IL_035d: Expected O, but got Unknown //IL_037d: Unknown result type (might be due to invalid IL or missing references) //IL_0383: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; ManualLogSource? log = Log; if (log != null) { log.LogInfo((object)"Loading Vision and Hearing Monsters v1.3.3..."); } ManualLogSource? log2 = Log; if (log2 != null) { log2.LogInfo((object)"Binding configuration..."); } VisionDistanceMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("1. Vision", "VisionDistanceMultiplier", 1f, new ConfigDescription("Multiplier for base vision distance.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 20f), Array.Empty<object>())); CloseVisionDistanceMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("1. Vision", "CloseVisionDistanceMultiplier", 1f, new ConfigDescription("Multiplier for close standing vision.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 10f), Array.Empty<object>())); CrouchCloseVisionDistanceMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("1. Vision", "CrouchCloseVisionDistanceMultiplier", 1f, new ConfigDescription("Multiplier for close crouched vision.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 10f), Array.Empty<object>())); PhysObjectVisionRadiusMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("1. Vision", "PhysObjectVisionRadiusMultiplier", 1f, new ConfigDescription("Multiplier for physics object vision radius.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 10f), Array.Empty<object>())); SoundSensitivityMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("2. Hearing", "SoundSensitivityMultiplier", 1f, new ConfigDescription("Multiplier for sound investigation radius.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 10f), Array.Empty<object>())); EnableCheatingAI = ((BaseUnityPlugin)this).Config.Bind<bool>("3. Cheating AI", "EnableCheatingAI", false, "Enables unfair AI enhancements (Radar, Memory, Targeted Patrols)."); RadarScanRadius = ((BaseUnityPlugin)this).Config.Bind<float>("3. Cheating AI", "RadarScanRadius", 25f, new ConfigDescription("Radius (meters) for enemy 'radar' scans (ignores walls).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 100f), Array.Empty<object>())); RadarScanInterval = ((BaseUnityPlugin)this).Config.Bind<float>("3. Cheating AI", "RadarScanInterval", 5f, new ConfigDescription("How often (seconds) each enemy performs a radar scan.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 30f), Array.Empty<object>())); MemoryDuration = ((BaseUnityPlugin)this).Config.Bind<float>("3. Cheating AI", "MemoryDuration", 60f, new ConfigDescription("How long (seconds) enemy remembers player's last known location.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 300f), Array.Empty<object>())); EnableGrouping = ((BaseUnityPlugin)this).Config.Bind<bool>("4. Group Patrolling", "EnableGrouping", false, "Enables enemies of the same type to form patrol groups."); MinGroupSize = ((BaseUnityPlugin)this).Config.Bind<int>("4. Group Patrolling", "MinGroupSize", 2, new ConfigDescription("Minimum number of same-type enemies needed to form a group.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(2, 10), Array.Empty<object>())); MaxGroupSize = ((BaseUnityPlugin)this).Config.Bind<int>("4. Group Patrolling", "MaxGroupSize", 4, new ConfigDescription("Maximum number of enemies in a single patrol group.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(2, 10), Array.Empty<object>())); GroupCheckInterval = ((BaseUnityPlugin)this).Config.Bind<float>("4. Group Patrolling", "GroupCheckInterval", 10f, new ConfigDescription("How often (seconds) the manager tries to form new groups.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 60f), Array.Empty<object>())); FormationType = ((BaseUnityPlugin)this).Config.Bind<string>("4. Group Patrolling", "FormationType", "Loose", new ConfigDescription("How grouped enemies attempt to arrange themselves while patrolling.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[5] { "Line", "Triangle", "Box", "Circle", "Loose" }), Array.Empty<object>())); ManualLogSource? log3 = Log; if (log3 != null) { log3.LogInfo((object)"Applying patches for PlusBlankPlus.VisionMonsters..."); } Harmony val = new Harmony("PlusBlankPlus.VisionMonsters"); try { val.PatchAll(Assembly.GetExecutingAssembly()); ManualLogSource? log4 = Log; if (log4 != null) { log4.LogInfo((object)"Patches applied successfully!"); } } catch (Exception ex) { ManualLogSource? log5 = Log; if (log5 != null) { log5.LogError((object)("Error applying Harmony patches: " + ex.Message)); } ManualLogSource? log6 = Log; if (log6 != null) { log6.LogError((object)("Stack trace: " + ex.StackTrace)); } if (ex is ReflectionTypeLoadException ex2 && ex2.LoaderExceptions != null) { Exception[] loaderExceptions = ex2.LoaderExceptions; foreach (Exception ex3 in loaderExceptions) { if (ex3 != null) { ManualLogSource? log7 = Log; if (log7 != null) { log7.LogError((object)("LoaderException: " + ex3.Message)); } } } } } ManualLogSource? log8 = Log; if (log8 != null) { log8.LogInfo((object)"Plugin Vision and Hearing Monsters (PlusBlankPlus.VisionMonsters) version 1.3.3 is loaded!"); } } } [HarmonyPatch(typeof(GameDirector))] internal static class GameDirectorStartPatch { private static bool managerChecked; [HarmonyPatch("Start")] [HarmonyPostfix] private static void CreateGroupManagerIfNeeded() { //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Expected O, but got Unknown if (managerChecked) { return; } managerChecked = true; ManualLogSource? log = Plugin.Log; if (log != null) { log.LogInfo((object)"GameDirector.Start Postfix: Checking if Group Manager needs creation..."); } ConfigEntry<bool>? enableGrouping = Plugin.EnableGrouping; if (enableGrouping != null && enableGrouping.Value && SemiFunc.IsMasterClientOrSingleplayer()) { if ((Object)(object)GroupPatrolManager.Instance == (Object)null) { ManualLogSource? log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)"HOST: Creating GroupPatrolManager GameObject via GameDirector.Start patch..."); } GameObject val = new GameObject("GroupPatrolManager_MOD"); if ((Object)(object)val.AddComponent<GroupPatrolManager>() != (Object)null) { Object.DontDestroyOnLoad((Object)(object)val); ManualLogSource? log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)"GroupPatrolManager created successfully."); } } else { ManualLogSource? log4 = Plugin.Log; if (log4 != null) { log4.LogError((object)"Failed to add GroupPatrolManager component!"); } Object.Destroy((Object)(object)val); } } else { ManualLogSource? log5 = Plugin.Log; if (log5 != null) { log5.LogInfo((object)"GroupPatrolManager instance already exists."); } } return; } ConfigEntry<bool>? enableGrouping2 = Plugin.EnableGrouping; if (enableGrouping2 != null && enableGrouping2.Value) { ManualLogSource? log6 = Plugin.Log; if (log6 != null) { log6.LogInfo((object)"CLIENT: Grouping enabled, manager runs on host."); } } else { ManualLogSource? log7 = Plugin.Log; if (log7 != null) { log7.LogInfo((object)"Grouping disabled, manager not created."); } } } } [HarmonyPatch(typeof(EnemyVision))] internal static class EnemyVisionPatches { [HarmonyPatch("Awake")] [HarmonyPostfix] private static void ModifyVisionRanges(EnemyVision __instance) { if ((Object)(object)__instance == (Object)null || Plugin.Log == null || Plugin.VisionDistanceMultiplier == null || Plugin.CloseVisionDistanceMultiplier == null || Plugin.CrouchCloseVisionDistanceMultiplier == null || Plugin.PhysObjectVisionRadiusMultiplier == null) { return; } try { float visionDistance = __instance.VisionDistance; __instance.VisionDistance *= Plugin.VisionDistanceMultiplier.Value; __instance.VisionDistanceClose *= Plugin.CloseVisionDistanceMultiplier.Value; __instance.VisionDistanceCloseCrouch *= Plugin.CrouchCloseVisionDistanceMultiplier.Value; __instance.PhysObjectVisionRadius *= Plugin.PhysObjectVisionRadiusMultiplier.Value; if (__instance.PhysObjectVisionRadiusOverride > 0f) { __instance.PhysObjectVisionRadiusOverride *= Plugin.PhysObjectVisionRadiusMultiplier.Value; } if (!Plugin.firstPatchLogDone && (Object)(object)((Component)__instance).gameObject != (Object)null) { Plugin.Log.LogInfo((object)string.Format("[{0}] Modifying EnemyVision on {1}: Vision={2}->{3}", "Vision and Hearing Monsters", ((Object)((Component)__instance).gameObject).name, visionDistance, __instance.VisionDistance)); } } catch (Exception arg) { ManualLogSource? log = Plugin.Log; if (log != null) { log.LogError((object)$"Error in ModifyVisionRanges patch: {arg}"); } } } } [HarmonyPatch(typeof(EnemyDirector))] internal static class EnemyDirectorPatches { [HarmonyPatch("SetInvestigate")] [HarmonyPrefix] private static void ModifyInvestigationRadius(ref float radius) { if (Plugin.Log == null || Plugin.SoundSensitivityMultiplier == null) { return; } try { radius *= Plugin.SoundSensitivityMultiplier.Value; if (!Plugin.firstPatchLogDone) { Plugin.Log.LogInfo((object)string.Format("[{0}] Sound Sensitivity Multiplier Applied: {1}.", "Vision and Hearing Monsters", Plugin.SoundSensitivityMultiplier.Value)); Plugin.firstPatchLogDone = true; } } catch (Exception arg) { ManualLogSource? log = Plugin.Log; if (log != null) { log.LogError((object)$"Error in ModifyInvestigationRadius patch: {arg}"); } } } } [HarmonyPatch(typeof(Enemy))] internal static class EnemyAwakePatch { [HarmonyPatch("Awake")] [HarmonyPostfix] private static void AddAIHelperComponents(Enemy __instance) { if ((Object)(object)((__instance != null) ? ((Component)__instance).gameObject : null) == (Object)null) { ManualLogSource? log = Plugin.Log; if (log != null) { log.LogWarning((object)"AddAIHelperComponents patch __instance null."); } return; } if ((Object)(object)((Component)__instance).gameObject.GetComponent<EnemyAIHelper>() == (Object)null) { ((Component)__instance).gameObject.AddComponent<EnemyAIHelper>(); } if ((Object)(object)((Component)__instance).gameObject.GetComponent<GroupAIHelper>() == (Object)null) { ((Component)__instance).gameObject.AddComponent<GroupAIHelper>(); } } } [HarmonyPatch(typeof(EnemyHealth))] internal static class EnemyHealthPatch { [HarmonyPatch("DeathImpulseRPC")] [HarmonyPrefix] private static void UnregisterOnDeathConfirmed(EnemyHealth __instance) { ConfigEntry<bool>? enableGrouping = Plugin.EnableGrouping; if (enableGrouping == null || !enableGrouping.Value || !SemiFunc.IsMasterClientOrSingleplayer()) { return; } if ((Object)(object)__instance != (Object)null && (Object)(object)GroupPatrolManager.Instance != (Object)null) { GroupAIHelper component = ((Component)__instance).GetComponent<GroupAIHelper>(); if ((Object)(object)component != (Object)null) { GroupPatrolManager.Instance.UnregisterEnemy(component); } } else if ((Object)(object)__instance == (Object)null) { ManualLogSource? log = Plugin.Log; if (log != null) { log.LogWarning((object)"UnregisterOnDeathConfirmed patch __instance null."); } } } } [HarmonyPatch(typeof(EnemyVision))] internal static class EnemyVisionTriggerPatch { [HarmonyPatch("VisionTrigger")] [HarmonyPrefix] private static void UpdateMemoryAndBreakFormationOnVision(EnemyVision __instance, PlayerAvatar player) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)__instance == (Object)null) && !((Object)(object)player == (Object)null)) { ConfigEntry<bool>? enableCheatingAI = Plugin.EnableCheatingAI; if (enableCheatingAI != null && enableCheatingAI.Value) { ((Component)__instance).GetComponent<EnemyAIHelper>()?.UpdateMemory(((Component)player).transform.position); } ConfigEntry<bool>? enableGrouping = Plugin.EnableGrouping; if (enableGrouping != null && enableGrouping.Value) { ((Component)__instance).GetComponent<GroupAIHelper>()?.BreakFormationTemporarily(); } } } } [HarmonyPatch(typeof(EnemyStateInvestigate))] internal static class EnemyInvestigatePatch { [HarmonyPatch("Set")] [HarmonyPrefix] private static void UpdateMemoryAndBreakFormationOnInvestigate(EnemyStateInvestigate __instance, Vector3 position) { //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_005e: 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_007e: 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_00bf: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)__instance == (Object)null)) { EnemyAIHelper component = ((Component)__instance).GetComponent<EnemyAIHelper>(); GroupAIHelper component2 = ((Component)__instance).GetComponent<GroupAIHelper>(); ConfigEntry<bool>? enableCheatingAI = Plugin.EnableCheatingAI; if (enableCheatingAI != null && enableCheatingAI.Value && (Object)(object)component != (Object)null && (!component.HasFreshMemory() || Vector3.Distance(((Component)__instance).transform.position, position) < Vector3.Distance(((Component)__instance).transform.position, component.lastKnownPlayerPosition))) { component.UpdateMemory(position); } ConfigEntry<bool>? enableGrouping = Plugin.EnableGrouping; if (enableGrouping != null && enableGrouping.Value && (Object)(object)component2 != (Object)null && component2.IsInGroup && component2.CurrentGroup != null && Vector3.Distance(position, component2.CurrentGroup.CurrentPatrolTarget) > 5f) { component2.BreakFormationTemporarily(); } } } } [HarmonyPatch(typeof(EnemyStateRoaming))] internal static class EnemyRoamingPatch { [HarmonyPatch("Update")] [HarmonyPrefix] private static bool OverrideRoamingMovement(EnemyStateRoaming __instance) { if (Plugin.EnableGrouping == null || !Plugin.EnableGrouping.Value) { return true; } GroupAIHelper groupAIHelper = ((__instance != null) ? ((Component)__instance).GetComponent<GroupAIHelper>() : null); if ((Object)(object)groupAIHelper != (Object)null && groupAIHelper.TryGroupMovement()) { return false; } return true; } } [HarmonyPatch(typeof(EnemyStateChaseBegin))] internal static class EnemyStateChaseBeginPatch { [HarmonyPatch("Update")] [HarmonyPrefix] private static void BreakFormationOnChaseBegin(EnemyStateChaseBegin __instance) { if (Plugin.EnableGrouping != null && Plugin.EnableGrouping.Value && __instance != null) { ((Component)__instance).GetComponent<GroupAIHelper>()?.BreakFormationTemporarily(); } } } [HarmonyPatch(typeof(EnemyStateChase))] internal static class EnemyStateChasePatch { [HarmonyPatch("Update")] [HarmonyPrefix] private static void BreakFormationOnChase(EnemyStateChase __instance) { if (Plugin.EnableGrouping != null && Plugin.EnableGrouping.Value && __instance != null) { ((Component)__instance).GetComponent<GroupAIHelper>()?.BreakFormationTemporarily(); } } } }