using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using AIGraph; using Agents; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using BepInEx.Unity.IL2CPP.Hook; using BepInEx.Unity.IL2CPP.Utils.Collections; using CullingSystem; using Enemies; using EnemyAnimationFix.API; using EnemyAnimationFix.NativePatches; using EnemyAnimationFix.NativePatches.KernelTools; using EnemyAnimationFix.Networking.Foam; using EnemyAnimationFix.Networking.Notify; using EnemyAnimationFix.Patches; using EnemyAnimationFix.Utils.Extensions; using GTFO.API; using GTFO.API.Utilities; using HarmonyLib; using Iced.Intel; using Il2CppInterop.Runtime; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppInterop.Runtime.Runtime; using Il2CppInterop.Runtime.Runtime.VersionSpecific.Class; using Il2CppInterop.Runtime.Runtime.VersionSpecific.MethodInfo; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using Microsoft.CodeAnalysis; using Player; using SNetwork; using StateMachines; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("EnemyAnimationFix")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+31bb79c6aadaed69b7ad1b1c19bde68687b9ebe0")] [assembly: AssemblyProduct("EnemyAnimationFix")] [assembly: AssemblyTitle("EnemyAnimationFix")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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] [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; } } } namespace EnemyAnimationFix { public static class Configuration { private static readonly ConfigEntry _syncLerpTime; private static readonly ConfigEntry _minWaveSleepTime; private static readonly ConfigEntry _maxWaveSleepTime; private static readonly ConfigEntry _disableCullNear; private static readonly ConfigEntry _disableCorpseHitbox; private static readonly ConfigFile configFile; public static float SyncLerpTime => _syncLerpTime.Value; public static float MinWaveSleepTime => _minWaveSleepTime.Value; public static float MaxWaveSleepTime => _maxWaveSleepTime.Value; public static bool DisableNearCull => _disableCullNear.Value; public static bool DisableCorpseHitbox => _disableCorpseHitbox.Value; static Configuration() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown configFile = new ConfigFile(Path.Combine(Paths.ConfigPath, "EnemyAnimationFix.cfg"), true); string text = "Sync Settings"; string text2 = "Amount of time the enemy position is smoothed over to match host position when they stop moving to attack/scream/etc."; _syncLerpTime = configFile.Bind(text, "Sync Position Time", 0.25f, text2); text = "Wave Settings"; text2 = "Minimum amount of time in seconds before enemies become active after spawning."; _minWaveSleepTime = configFile.Bind(text, "Minimum Wave Inactive Time", 0f, text2); text2 = "Maximum amount of time in seconds before enemies become active after spawning."; _maxWaveSleepTime = configFile.Bind(text, "Maximum Wave Inactive Time", 6f, text2); text = "Cull Settings"; text2 = "Prevents nearby or attacking enemies from culling.\nThis fixes enemies not on screen failing to play footstep sounds or move when attacking."; _disableCullNear = configFile.Bind(text, "Disable Culling Nearby Enemies", true, text2); text = "Corpse Settings"; text2 = "Prevent melees from hitting corpses."; _disableCorpseHitbox = configFile.Bind(text, "Disable Corpse Melee Hitbox", true, text2); } internal static void Init() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown LiveEdit.CreateListener(Paths.ConfigPath, "EnemyAnimationFix.cfg", false).FileChanged += new LiveEditEventHandler(OnFileChanged); } private static void OnFileChanged(LiveEditEventArgs _) { configFile.Reload(); if (LayerManager.MASK_ENEMY_DEAD != 0) { if (DisableCorpseHitbox) { LayerManager.MASK_MELEE_ATTACK_TARGETS_WITH_STATIC &= ~LayerManager.MASK_ENEMY_DEAD; } else { LayerManager.MASK_MELEE_ATTACK_TARGETS_WITH_STATIC |= LayerManager.MASK_ENEMY_DEAD; } } } } internal static class DinoLogger { private static ManualLogSource logger = Logger.CreateLogSource("EnemyAnimationFix"); public static void Log(string format, params object[] args) { Log(string.Format(format, args)); } public static void Log(string str) { if (logger != null) { logger.Log((LogLevel)8, (object)str); } } public static void Warning(string format, params object[] args) { Warning(string.Format(format, args)); } public static void Warning(string str) { if (logger != null) { logger.Log((LogLevel)4, (object)str); } } public static void Error(string format, params object[] args) { Error(string.Format(format, args)); } public static void Error(string str) { if (logger != null) { logger.Log((LogLevel)2, (object)str); } } public static void Debug(string format, params object[] args) { Debug(string.Format(format, args)); } public static void Debug(string str) { if (logger != null) { logger.Log((LogLevel)32, (object)str); } } } [BepInPlugin("Dinorush.EnemyAnimationFix", "EnemyAnimationFix", "1.4.8")] [BepInDependency(/*Could not decode attribute arguments.*/)] internal sealed class EntryPoint : BasePlugin { public const string MODNAME = "EnemyAnimationFix"; public override void Load() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) new Harmony("EnemyAnimationFix").PatchAll(); ChangeStatePatches.ApplyNativePatch(); ValidTargetPatches.ApplyInstructionPatch(); IsStuckPatches.ApplyInstructionPatch(); Configuration.Init(); AssetAPI.OnStartupAssetsLoaded += AssetAPI_OnStartupAssetsLoaded; LevelAPI.OnLevelCleanup += LevelAPI_OnLevelCleanup; ((BasePlugin)this).Log.LogMessage((object)"Loaded EnemyAnimationFix"); } private void AssetAPI_OnStartupAssetsLoaded() { NotifyManager.Init(); FoamManager.Init(); } private void LevelAPI_OnLevelCleanup() { EB_InCombat.s_globalScreamTimer = 0f; EnemyMovementPatches.OnCleanup(); } } } namespace EnemyAnimationFix.Utils.Extensions { public static class CollectionExtensions { public static void Remove(this List list, Predicate predicate) { for (int i = 0; i < list.Count; i++) { if (predicate(list[i])) { list.RemoveAt(i); break; } } } } internal static class EnemyAgentExt { public static void AddOnDeadOnce(this EnemyAgent agent, Action onDead) { bool called = false; agent.OnDeadCallback += Action.op_Implicit((Action)delegate { if (!called && !CheckpointManager.IsReloadingCheckpoint) { onDead?.Invoke(); called = true; } }); } } } namespace EnemyAnimationFix.Patches { [HarmonyPatch] internal static class EnemyAnimPatches { [HarmonyPatch(typeof(ES_Scream), "OnStateShouldBeActivated")] [HarmonyPrefix] private static void FixGiantScream(ES_Scream __instance, ref pES_EnemyScreamData data) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Invalid comparison between Unknown and I4 if (NotifyManager.MasterHasFix && (int)((ES_Base)__instance).m_locomotion.AnimHandleName == 128) { data.AnimIndex = 0; } } [HarmonyPatch(typeof(ES_StrikerAttack), "OnAttackWindUp")] [HarmonyPatch(typeof(ES_ShooterAttack), "OnAttackWindUp")] [HarmonyPrefix] private static void FixFiddlerAttack(ES_EnemyAttackBase __instance, ref int attackIndex) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Invalid comparison between Unknown and I4 if (NotifyManager.MasterHasFix && (int)((ES_Base)__instance).m_locomotion.AnimHandleName == 8) { attackIndex %= 2; } } [HarmonyPatch(typeof(ES_Hitreact), "DoHitReact")] [HarmonyPrefix] private static void FixLowStagger(ES_Hitreact __instance, ref int index, ES_HitreactType hitreactType, ImpactDirection impactDirection) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Invalid comparison between Unknown and I4 //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Invalid comparison between Unknown and I4 //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Invalid comparison between Unknown and I4 if (NotifyManager.MasterHasFix && (int)((ES_Base)__instance).m_locomotion.AnimHandleName == 16 && (int)hitreactType == 3 && (int)impactDirection == 2) { index %= 3; } } } [HarmonyPatch] internal static class EnemyAttackPatches { [HarmonyPatch(typeof(MovingEnemyTentacleBase), "BuildAndUpdateAttackSpline")] [HarmonyPostfix] private static void Post_AttackSplineBuilt(MovingEnemyTentacleBase __instance, bool __result) { if (!__result) { __instance.TentacleRelLen = __instance.MinLengthWhenIn; __instance.m_inAttackMove = false; __instance.m_goingIn = false; __instance.m_owner.Locomotion.ChangeState((ES_StateEnum)2); } } [HarmonyPatch(typeof(ES_StrikerAttack), "OnAttackPerform")] [HarmonyPostfix] private static void Post_StrikerAttackPerform(ES_StrikerAttack __instance) { if (TryGetTentacle(__instance, out MovingEnemyTentacleBase tentacle)) { tentacle.m_inAttackMove = true; } } [HarmonyPatch(typeof(ES_ShooterAttack), "OnAttackPerform")] [HarmonyPostfix] private static void Post_ShooterAttackPerform(ES_ShooterAttack __instance) { if (!(((ES_EnemyAttackBase)__instance).m_attackDoneTimer >= Clock.Time)) { EAB_ProjectileShooter projectileAbility = __instance.m_projectileAbility; ((ES_EnemyAttackBase)__instance).m_attackDoneTimer = Clock.Time + (projectileAbility.m_shotDelayMin + projectileAbility.m_shotDelayMax) / 2f * (float)projectileAbility.m_burstCount * 0.7f + __instance.m_burstCoolDownBeforeExit; } } [HarmonyPatch(typeof(ES_StrikerAttack), "CommonExit")] [HarmonyPostfix] private static void Post_StrikerAttackExit(ES_StrikerAttack __instance) { if (NotifyManager.MasterHasFix && !(Clock.Time <= ((ES_EnemyAttackBase)__instance).m_performAttackTimer) && TryGetTentacle(__instance, out MovingEnemyTentacleBase tentacle) && tentacle.m_currentRoutine != null) { tentacle.SwitchCoroutine(tentacle.AttackIn(tentacle.m_attackInDuration)); } } private static bool TryGetTentacle(ES_StrikerAttack attack, [MaybeNullWhen(false)] out MovingEnemyTentacleBase tentacle) { tentacle = null; EAB_MovingEnemeyTentacle tentacleAbility = attack.m_tentacleAbility; if ((Object)(object)tentacleAbility == (Object)null) { return false; } tentacle = tentacleAbility.m_tentacle; return (Object)(object)tentacle != (Object)null; } [HarmonyPatch(typeof(ES_PathMove), "Exit")] [HarmonyPostfix] private static void Post_TankPathMoveExit(ES_PathMove __instance) { if (NotifyManager.MasterHasFix) { EnemyAbilities abilities = ((ES_Base)__instance).m_enemyAgent.Abilities; if (!ResetTankTongues(abilities, (AgentAbility)2)) { ResetTankTongues(abilities, (AgentAbility)1); } } } private static bool ResetTankTongues(EnemyAbilities abilities, AgentAbility abilityType) { //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_000d: Expected I4, but got Unknown if (!((AgentAbilities)abilities).HasAbility(abilityType)) { return false; } int num = (int)abilityType; bool result = false; foreach (EnemyAbility item in (Il2CppArrayBase)(object)((Il2CppArrayBase)(object)abilities.AbilityComps)[num].Comps) { EAB_MovingEnemyTentacleMultiple val = ((Il2CppObjectBase)item).TryCast(); if ((Object)(object)val == (Object)null) { continue; } result = true; foreach (MovingEnemyTentacleBase item2 in (Il2CppArrayBase)(object)val.m_tentacles) { if (item2.m_currentRoutine != null) { item2.SwitchCoroutine(item2.AttackIn(item2.m_attackInDuration)); } } } return result; } [HarmonyPatch(typeof(ES_ShooterAttack), "CommonExit")] [HarmonyPostfix] private static void Post_ShooterAttackExit(ES_ShooterAttack __instance) { if (NotifyManager.MasterHasFix && !(Clock.Time <= ((ES_EnemyAttackBase)__instance).m_performAttackTimer)) { __instance.m_projectileAbility.FireEnd(); } } } [HarmonyPatch] internal class EnemyCullPatches { private static readonly List<(Animator animator, C_CullBucket bucket)> _nearbyCullers = new List<(Animator, C_CullBucket)>(5); private static readonly List _animatedCullers = new List(10); private static readonly Dictionary _cachedEnemies = new Dictionary(); private static float _nextUpdateTime = 0f; private const float UpdateInterval = 0.1f; private const float NearbySqrDist = 625f; private const int NearbyCap = 5; [HarmonyPatch(typeof(C_Camera), "PropagateFrustra")] [HarmonyWrapSafe] [HarmonyPostfix] private static void RestoreFootstepCullers() { if (!Configuration.DisableNearCull) { return; } CheckDeadCullers(); float time = Clock.Time; if (time > _nextUpdateTime) { _nextUpdateTime = time + 0.1f; CacheCullers(); } foreach (var nearbyCuller in _nearbyCullers) { C_CullBucket item = nearbyCuller.bucket; if (((C_Cullable)item).CullKey != C_Keys.CurrentCullKey) { ((C_Cullable)item).CullKey = C_Keys.CurrentCullKey; ((C_Cullable)item).Show(); } } foreach (C_CullBucket animatedCuller in _animatedCullers) { if (((C_Cullable)animatedCuller).CullKey != C_Keys.CurrentCullKey) { ((C_Cullable)animatedCuller).CullKey = C_Keys.CurrentCullKey; ((C_Cullable)animatedCuller).Show(); } } } private static void CheckDeadCullers() { for (int num = _nearbyCullers.Count - 1; num >= 0; num--) { if ((Object)(object)_nearbyCullers[num].animator == (Object)null) { _nearbyCullers.RemoveAt(num); } } for (int num2 = _animatedCullers.Count - 1; num2 >= 0; num2--) { if (_animatedCullers[num2] == null) { _animatedCullers.RemoveAt(num2); } } } private static void CacheCullers() { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Invalid comparison between Unknown and I4 //IL_005b: 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_0068: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: 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_00ad: 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_00b3: 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_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Invalid comparison between Unknown and I4 foreach (var nearbyCuller in _nearbyCullers) { Animator item = nearbyCuller.animator; if ((int)item.cullingMode == 1) { item.cullingMode = (AnimatorCullingMode)2; } } _nearbyCullers.Clear(); if (!PlayerManager.HasLocalPlayerAgent()) { return; } PlayerAgent localPlayerAgent = PlayerManager.GetLocalPlayerAgent(); Vector3 position = ((Agent)localPlayerAgent).Position; AIG_CourseNode courseNode = ((Agent)localPlayerAgent).CourseNode; CacheNearbyEnemies(courseNode, position); if (_cachedEnemies.Count < 5) { Enumerator enumerator2 = courseNode.m_portals.GetEnumerator(); while (enumerator2.MoveNext()) { AIG_CoursePortal current = enumerator2.Current; if (current.IsTraversable) { Bounds bounds = ((C_Cullable)current.m_cullPortal).Bounds; Vector3 val = ((Bounds)(ref bounds)).ClosestPoint(position) - position; if (((Vector3)(ref val)).sqrMagnitude < 625f) { CacheNearbyEnemies(current.GetOppositeNode(courseNode), position); } } if (_cachedEnemies.Count > 5) { break; } } } _nearbyCullers.AddRange(from kv in _cachedEnemies.OrderBy, float>((KeyValuePair kv) => kv.Value.sqrDist).Take(5) select (m_animator: kv.Value.enemy.Locomotion.m_animator, CullBucket: kv.Value.enemy.MovingCuller.CullBucket)); foreach (var nearbyCuller2 in _nearbyCullers) { Animator item2 = nearbyCuller2.animator; if ((int)item2.cullingMode == 2) { item2.cullingMode = (AnimatorCullingMode)1; } } _cachedEnemies.Clear(); } private static void CacheNearbyEnemies(AIG_CourseNode node, Vector3 pos) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: 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_0033: 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_0048: Invalid comparison between Unknown and I4 Enumerator enumerator = node.m_enemiesInNode.GetEnumerator(); while (enumerator.MoveNext()) { EnemyAgent current = enumerator.Current; if (!_cachedEnemies.ContainsKey(((Il2CppObjectBase)current).Pointer)) { Vector3 val = current.Position - pos; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; if ((int)current.Locomotion.CurrentStateEnum == 2 && sqrMagnitude < 625f) { _cachedEnemies.Add(((Il2CppObjectBase)current).Pointer, (current, sqrMagnitude)); } if (_cachedEnemies.Count > 5) { break; } } } } [HarmonyPatch(typeof(EnemyAgent), "SetAnimatorCullingEnabled")] [HarmonyWrapSafe] [HarmonyPostfix] private static void FixFootstepCulling(EnemyAgent __instance, bool mode) { if (Configuration.DisableNearCull && mode) { Animator animator = __instance.Locomotion.m_animator; if (_nearbyCullers.Any<(Animator, C_CullBucket)>(((Animator animator, C_CullBucket bucket) pair) => ((Il2CppObjectBase)animator).Pointer == ((Il2CppObjectBase)pair.animator).Pointer)) { animator.cullingMode = (AnimatorCullingMode)1; } } } [HarmonyPatch(typeof(ES_EnemyAttackBase), "DoStartAttack")] [HarmonyPatch(typeof(ES_StrikerMelee), "DoStartMeleeAttack")] [HarmonyWrapSafe] [HarmonyPrefix] private static void DisableAttackAnimCulling(ES_EnemyAttackBase __instance) { DisableAnimationCulling(((ES_Base)__instance).m_locomotion); } [HarmonyPatch(typeof(ES_EnemyAttackBase), "CommonExit")] [HarmonyPatch(typeof(ES_StrikerMelee), "Exit")] [HarmonyPatch(typeof(ES_StrikerMelee), "SyncExit")] [HarmonyWrapSafe] [HarmonyPostfix] private static void EnableAttackCulling(ES_EnemyAttackBase __instance) { EnableAnimationCulling(((ES_Base)__instance).m_locomotion); } [HarmonyPatch(typeof(ES_Hitreact), "DoHitReact")] [HarmonyWrapSafe] [HarmonyPrefix] private static void DisableStaggerCulling(ES_HitreactBase __instance) { DisableAnimationCulling(((ES_Base)__instance).m_locomotion); } [HarmonyPatch(typeof(ES_Hitreact), "Exit")] [HarmonyPatch(typeof(ES_Hitreact), "SyncExit")] [HarmonyWrapSafe] [HarmonyPostfix] private static void EnableStaggerCulling(ES_HitreactBase __instance) { EnableAnimationCulling(((ES_Base)__instance).m_locomotion); } private static void DisableAnimationCulling(EnemyLocomotion locomotion) { if (Configuration.DisableNearCull) { locomotion.m_animator.cullingMode = (AnimatorCullingMode)0; _animatedCullers.Add(locomotion.m_agent.MovingCuller.CullBucket); } } private static void EnableAnimationCulling(EnemyLocomotion locomotion) { if (Configuration.DisableNearCull) { EnemyAgent agent = locomotion.m_agent; C_MovingCuller culler = agent.MovingCuller; ((Agent)agent).SetAnimatorCullingEnabled(culler.m_animatorCullingEnabled); EnemyAnimationFix.Utils.Extensions.CollectionExtensions.Remove(_animatedCullers, (C_CullBucket bucket) => ((Il2CppObjectBase)bucket).Pointer == ((Il2CppObjectBase)culler).Pointer); } } } [HarmonyPatch] internal static class EnemyDeathPatches { [HarmonyPatch(/*Could not decode attribute arguments.*/)] [HarmonyWrapSafe] [HarmonyPrefix] private static void Post_Death(EnemyAgent __instance, bool value) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) if (!value && !((Object)(object)__instance == (Object)null) && (int)__instance.UpdateMode == 0) { EnemyUpdateManager.Current.Register(__instance, (NodeUpdateMode)1); } } } [HarmonyPatch] internal static class EnemyGluePatches { [HarmonyPatch(typeof(Dam_EnemyDamageBase), "AddToTotalGlueVolume")] [HarmonyPostfix] private static void SyncGlueData(Dam_EnemyDamageBase __instance) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Invalid comparison between Unknown and I4 if ((int)__instance.Owner.Locomotion.CurrentStateEnum == 17) { ES_StuckInGlue stuckInGlue = __instance.Owner.Locomotion.StuckInGlue; if (stuckInGlue.m_glueFadeOutTriggered) { float num = (stuckInGlue.m_fadeOutDuration - stuckInGlue.m_fadeInDuration) * __instance.AttachedGlueRel * 1.44f; EnemyAppearance appearance = __instance.Owner.Appearance; appearance.m_lastGlueEnd = num / stuckInGlue.m_fadeOutDuration; appearance.SetGlueAmount(0f, num); } } if (SNet.IsMaster) { FoamManager.SendFoam(__instance.Owner, __instance.AttachedGlueRel); } } [HarmonyPatch(typeof(ES_StuckInGlue), "ActivateState")] [HarmonyPrefix] private static bool StopClientStateSwitch() { return SNet.IsMaster; } [HarmonyPatch(typeof(ES_StuckInGlue), "DoStartStuckInGlue")] [HarmonyPostfix] private static void FixFadeOutTime(ES_StuckInGlue __instance) { if (!SNet.IsMaster) { ((ES_Base)__instance).m_enemyAgent.Damage.m_attachedGlueVolume = ((ES_Base)__instance).m_enemyAgent.EnemyBalancingData.GlueTolerance; } __instance.m_fadeOutTimer = __instance.m_fadeInTimer + (__instance.m_fadeOutDuration - __instance.m_fadeInDuration) * 0.1f; } } [HarmonyPatch] internal static class EnemyMovementPatches { private const float MinBufferTime = 0.25f; private static readonly Dictionary _exitTimes = new Dictionary(); private static readonly Dictionary _smoothRoutines = new Dictionary(); private static readonly HashSet _usedFogs = new HashSet(); public static void OnCleanup() { _exitTimes.Clear(); } internal static bool Pre_ChangeState(StateMachine __instance, ES_Base newState) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Invalid comparison between Unknown and I4 //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Invalid comparison between Unknown and I4 if (SNet.IsMaster) { return true; } if ((int)newState.m_stateEnum != 2) { return true; } IntPtr pointer = ((Il2CppObjectBase)newState.m_enemyAgent).Pointer; if ((int)__instance.m_currentState.m_stateEnum == 27 && _usedFogs.Remove(pointer)) { return true; } if (Clock.Time >= _exitTimes.GetValueOrDefault(pointer)) { return true; } ES_PathMove val = ((Il2CppObjectBase)newState).TryCast(); if (val == null || val.m_positionBuffer.Count == 0) { return true; } StartUpdatePosition(((ES_Base)val).m_enemyAgent, val); return false; } [HarmonyPatch(typeof(EnemyAgent), "SetMaxSfxLOD")] [HarmonyPostfix] private static void Pre_Exit(EnemyAgent __instance) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Invalid comparison between Unknown and I4 //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: 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_00fb: Unknown result type (might be due to invalid IL or missing references) ES_PathMoveBase pathMove = __instance.Locomotion.PathMove; if ((int)((ES_Base)pathMove).m_stateEnum != 2) { return; } ES_PathMove val = ((Il2CppObjectBase)pathMove).TryCast(); if (val == null) { return; } IntPtr pointer = ((Il2CppObjectBase)__instance).Pointer; if (!_exitTimes.ContainsKey(pointer)) { __instance.AddOnDeadOnce(delegate { _exitTimes.Remove(pointer); }); } _exitTimes[((Il2CppObjectBase)__instance).Pointer] = Clock.Time + 0.25f; if (SNet.IsMaster) { pES_PathMoveData pathMoveData = val.m_pathMoveData; ((pCourseNode)(ref pathMoveData.CourseNode)).Set(((Agent)__instance).CourseNode); pathMoveData.Position = ((Agent)__instance).m_position; ((LowResVector3_NoY)(ref pathMoveData.Movement)).Set(val.m_moveDir, 1f); ((LowResVector3_NoY_Normalized)(ref pathMoveData.TargetLookDir)).Value = ((Agent)__instance).TargetLookDir; pathMoveData.Tick++; val.m_pathMoveData = pathMoveData; { foreach (SNet_Player fixedClient in NotifyManager.FixedClients) { val.m_pathMovePacket.Send(pathMoveData, (SNet_ChannelType)4, fixedClient); } return; } } if (val.m_positionBuffer.Count != 0) { StartUpdatePosition(__instance, val); } } private static void StartUpdatePosition(EnemyAgent enemy, ES_PathMove pathMove) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001a: 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_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0039: 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_0075: 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) PositionSnapshotBuffer positionBuffer = pathMove.m_positionBuffer; pES_PathMoveData val = positionBuffer[positionBuffer.Count - 1]; enemy.Locomotion.ForceNode(val.CourseNode); pathMove.m_targetPosition = val.Position; ((Agent)enemy).TargetLookDir = ((LowResVector3_NoY_Normalized)(ref val.TargetLookDir)).Value; pathMove.m_moveDir = ((LowResVector3_NoY)(ref val.Movement)).Get(1f); if (!enemy.MovingCuller.IsShown || Configuration.SyncLerpTime <= 0f) { UpdatePosition(enemy, pathMove, val.Position); return; } if (_smoothRoutines.TryGetValue(((Il2CppObjectBase)enemy).Pointer, out Coroutine value)) { ((MonoBehaviour)enemy).StopCoroutine(value); } _smoothRoutines[((Il2CppObjectBase)enemy).Pointer] = ((MonoBehaviour)enemy).StartCoroutine(CollectionExtensions.WrapToIl2Cpp(LerpPosition(enemy, pathMove))); } private static void UpdatePosition(EnemyAgent enemy, ES_PathMove pathMove, Vector3 pos) { //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_0014: 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) //IL_0026: Unknown result type (might be due to invalid IL or missing references) ((Component)enemy).transform.position = pos; ((ES_PathMoveBase)pathMove).m_lastPos = pos; enemy.Position = pos; enemy.MovingCuller.UpdatePosition(((Agent)enemy).DimensionIndex, pos); ((C_Cullable)enemy.MovingCuller.Culler).NeedsShadowRefresh = true; } private static IEnumerator LerpPosition(EnemyAgent enemy, ES_PathMove pathMove) { PositionSnapshotBuffer positionBuffer = pathMove.m_positionBuffer; pES_PathMoveData val = positionBuffer[positionBuffer.Count - 1]; Vector3 posDiff = val.Position - ((Agent)enemy).m_position; float lastTime = Clock.Time; float totalTime = 0f; float prevProgress = 0f; EnemyLocomotion locomotion = enemy.Locomotion; while (totalTime < Configuration.SyncLerpTime) { yield return null; if ((int)locomotion.CurrentStateEnum == 2) { _smoothRoutines.Remove(((Il2CppObjectBase)enemy).Pointer); yield break; } float time = Clock.Time; totalTime += time - lastTime; lastTime = time; float num = Math.Clamp(totalTime / Configuration.SyncLerpTime, 0f, 1f); float num2 = 1f - (1f - num) * (1f - num); float num3 = num2 - prevProgress; prevProgress = num2; UpdatePosition(enemy, pathMove, ((Agent)enemy).m_position + posDiff * num3); } _smoothRoutines.Remove(((Il2CppObjectBase)enemy).Pointer); } [HarmonyPatch(typeof(EAB_FogSphere), "DoTrigger")] [HarmonyPostfix] private static void Post_TriggerFog(EAB_FogSphere __instance) { _usedFogs.Add(((Il2CppObjectBase)((EnemyAbility)__instance).m_owner).Pointer); } [HarmonyPatch(typeof(EB_InCombatFlyer_GeomorphTraversal), "Enter")] [HarmonyPostfix] private static void Post_FlyerTraversal(EB_InCombatFlyer_GeomorphTraversal __instance) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: 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_003c: 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_0047: 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_0051: 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_0059: 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) EnemyAI ai = ((EB_StateBase)__instance).m_ai; AirGraphTraversalData currentData = __instance.CurrentData; Vector3 val = -ai.m_courseNavigation.m_navPortal.m_cullPortal.GetDirIntoNode(((Agent)ai.m_enemyAgent).CourseNode.m_cullNode); currentData.StartPosition = currentData.GatePosition - val * 1.5f; currentData.Direction = val; ai.m_courseNavigation.FlyerGraphTraversalData = currentData; } } [HarmonyPatch] internal static class EnemyScreamPatches { [HarmonyPatch(typeof(ES_Scream), "Enter")] [HarmonyPostfix] private static void FixScreamReset(ES_Scream __instance) { __instance.m_hasTriggeredPropagation = false; } } [HarmonyPatch] internal static class EnemyTargetPatches { [HarmonyPatch(typeof(EnemyGroup), "RegisterMember")] [HarmonyPostfix] private static void PostEnemyRegistered(EnemyGroup __instance, EnemyAgent enemyAgent) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0014: 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_0018: Invalid comparison between Unknown and I4 EnemyGroupType groupType = __instance.GroupType; if (groupType - 2 <= 1) { enemyAgent.m_hasValidTarget = false; enemyAgent.m_validTargetInterval = Clock.Time + Random.RandomRange(Configuration.MinWaveSleepTime, Configuration.MaxWaveSleepTime); TakeDamageDelegate del = null; del = TakeDamageDelegate.op_Implicit((Action)delegate { enemyAgent.m_validTargetInterval = 0f; EnemyAgent obj2 = enemyAgent; obj2.TookDamage -= del; }); EnemyAgent obj = enemyAgent; obj.TookDamage += del; } } [HarmonyPatch(typeof(EB_InCombat_MoveToNextNode_PathOpen), "UpdateBehaviour")] [HarmonyPostfix] private static void PathfindingFix(EB_InCombat_MoveToNextNode __instance) { if (((MachineState)(object)((MachineState)(object)__instance).m_machine.CurrentState).ENUM_ID != 5 && ((AgentAI)((EB_StateBase)__instance).m_ai).IsTargetValid && ((AgentAI)((EB_StateBase)__instance).m_ai).Target.m_nodeDistance <= 1) { ((EB_InCombat)__instance).TryUpdateNavigation((EnemyCourseNavigationMode)1); } } [HarmonyPatch(typeof(EB_InCombatFlyer_GeomorphTraversal), "Update")] [HarmonyPostfix] private static void FlyerPathfindingFix(EB_InCombat_MoveToNextNode __instance) { if (((MachineState)(object)((MachineState)(object)__instance).m_machine.CurrentState).ENUM_ID != 5 && ((AgentAI)((EB_StateBase)__instance).m_ai).IsTargetValid && ((AgentAI)((EB_StateBase)__instance).m_ai).Target.m_nodeDistance < 1) { ((MachineState)(object)__instance).m_machine.ChangeState((byte)5); } } [HarmonyPatch(typeof(EB_InCombat_MoveToNextNode_DestroyDoor), "UpdateBehaviour")] [HarmonyPrefix] private static void RemoteDoorFix(EB_InCombat_MoveToNextNode_DestroyDoor __instance, ref IntPtr __state) { EnemyCourseNavigation courseNavigation = ((EB_StateBase)__instance).m_ai.m_courseNavigation; __state = (courseNavigation.IsPathBlocked ? ((Il2CppObjectBase)courseNavigation.m_navPortal).Pointer : IntPtr.Zero); } [HarmonyPatch(typeof(EB_InCombat_MoveToNextNode_DestroyDoor), "UpdateBehaviour")] [HarmonyPostfix] private static void RemoteDoorFixPost(EB_InCombat_MoveToNextNode_DestroyDoor __instance, IntPtr __state) { if (!(__state == IntPtr.Zero)) { EnemyCourseNavigation courseNavigation = ((EB_StateBase)__instance).m_ai.m_courseNavigation; if (courseNavigation.IsPathBlocked && ((Il2CppObjectBase)courseNavigation.m_navPortal).Pointer != __state) { ((MachineState)(object)__instance).m_machine.ChangeState((byte)9); } } } } [HarmonyPatch] internal static class LayerPatches { [HarmonyPatch(typeof(LayerManager), "Setup")] [HarmonyPostfix] private static void Post_Setup() { if (Configuration.DisableCorpseHitbox) { LayerManager.MASK_MELEE_ATTACK_TARGETS_WITH_STATIC &= ~LayerManager.MASK_ENEMY_DEAD; } else { LayerManager.MASK_MELEE_ATTACK_TARGETS_WITH_STATIC |= LayerManager.MASK_ENEMY_DEAD; } } } [HarmonyPatch] internal static class NetworkingPatches { [HarmonyPatch(typeof(SNet_SyncManager), "OnFoundMaster")] [HarmonyPatch(typeof(SNet_SyncManager), "OnFoundNewMasterDuringMigration")] [HarmonyPostfix] private static void Post_Joined() { if (!SNet.IsMaster) { NotifyManager.SendNotify(SNet.Master); } else { NotifyManager.SetMaster(); } } [HarmonyPatch(typeof(SNet_SyncManager), "OnPlayerJoinedSessionHub")] [HarmonyPostfix] private static void Post_Joined(SNet_Player player) { if (SNet.IsMaster && !player.IsLocal) { NotifyManager.SendNotify(player); } } [HarmonyPatch(typeof(SNet_SessionHub), "OnLeftLobby")] [HarmonyPatch(typeof(SNet_SessionHub), "RemovePlayerFromSession")] [HarmonyPrefix] private static void Pre_Eject(SNet_Player player) { if (SNet.IsMaster) { NotifyManager.RemoveClient(player); } } [HarmonyPatch(typeof(SNet_SyncManager), "OnResetSession")] [HarmonyPrefix] private static void Pre_OnReset() { NotifyManager.Reset(); } } } namespace EnemyAnimationFix.Networking { public abstract class SyncedEvent where T : struct { public delegate void ReceiveHandler(T packet); private bool _isSetup; public abstract string GUID { get; } public bool IsSetup => _isSetup; public string EventName { get; private set; } = string.Empty; public event ReceiveHandler? OnReceive; public event ReceiveHandler? OnReceiveLocal; public void Setup() { if (!_isSetup) { EventName = "EAF" + GUID; NetworkAPI.RegisterEvent(EventName, (Action)ReceiveClient_Callback); _isSetup = true; } } public void Send(T packetData, SNet_Player? target = null, SNet_ChannelType priority = (SNet_ChannelType)4) { //IL_0020: 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) if ((Object)(object)target != (Object)null) { NetworkAPI.InvokeEvent(EventName, packetData, target, priority); } else { NetworkAPI.InvokeEvent(EventName, packetData, priority); } ReceiveLocal_Callback(packetData); } private void ReceiveLocal_Callback(T packet) { ReceiveLocal(packet); this.OnReceiveLocal?.Invoke(packet); } private void ReceiveClient_Callback(ulong sender, T packet) { Receive(packet); this.OnReceive?.Invoke(packet); } protected virtual void ReceiveLocal(T packet) { } protected virtual void Receive(T packet) { } } } namespace EnemyAnimationFix.Networking.Notify { internal static class NotifyManager { private static readonly NotifySync _sync = new NotifySync(); public static readonly List FixedClients = new List(); public static bool MasterHasFix { get; private set; } = false; internal static void Init() { _sync.Setup(); } internal static void SendNotify(SNet_Player player) { _sync.Send(new NotifyData { lookup = SNet.LocalPlayer.Lookup }, player, (SNet_ChannelType)0); } internal static void ReceiveNotify(SNet_Player player) { if (SNet.IsMaster) { FixedClients.Add(player); } else { MasterHasFix = true; } } internal static void SetMaster() { MasterHasFix = true; } internal static void Reset() { MasterHasFix = false; FixedClients.Clear(); } internal static void RemoveClient(SNet_Player player) { for (int num = FixedClients.Count - 1; num >= 0; num--) { if ((Object)(object)FixedClients[num] == (Object)null || FixedClients[num].Lookup == player.Lookup) { FixedClients.RemoveAt(num); } } } } internal sealed class NotifySync : SyncedEvent { public override string GUID => "NOTIF"; protected override void Receive(NotifyData packet) { SNet_Player player = default(SNet_Player); if (SNet.TryGetPlayer(packet.lookup, ref player)) { NotifyManager.ReceiveNotify(player); } } } public struct NotifyData { public ulong lookup; } } namespace EnemyAnimationFix.Networking.Foam { internal static class FoamManager { private static readonly FoamSync _sync = new FoamSync(); internal static void Init() { _sync.Setup(); } internal static void SendFoam(EnemyAgent enemy, float foamRel) { FoamData packetData = default(FoamData); ((pEnemyAgent)(ref packetData.enemy)).Set(enemy); ((UFloat16)(ref packetData.foamRel)).Set(foamRel, 1f); foreach (SNet_Player fixedClient in NotifyManager.FixedClients) { _sync.Send(packetData, fixedClient, (SNet_ChannelType)4); } } internal static void ReceiveFoam(EnemyAgent enemy, float foamRel) { enemy.Damage.m_attachedGlueVolume = foamRel * enemy.EnemyBalancingData.GlueTolerance; } } internal sealed class FoamSync : SyncedEvent { public override string GUID => "FOAM"; protected override void Receive(FoamData packet) { EnemyAgent enemy = default(EnemyAgent); if (((pEnemyAgent)(ref packet.enemy)).TryGet(ref enemy)) { FoamManager.ReceiveFoam(enemy, ((UFloat16)(ref packet.foamRel)).Get(1f)); } } } public struct FoamData { public pEnemyAgent enemy; public UFloat16 foamRel; } } namespace EnemyAnimationFix.NativePatches { internal static class ChangeStatePatches { private unsafe delegate void d_ChangeStateFromQueue(IntPtr _this, Il2CppMethodInfo* methodInfo); private static INativeDetour? ChangeStateDetour; private static d_ChangeStateFromQueue? orig_ChangeStateFromQueue; internal unsafe static void ApplyNativePatch() { NativePatchAPI.AddChangeStatePrefix(FixMeleeCancel); NativePatchAPI.AddChangeStatePrefix(EnemyMovementPatches.Pre_ChangeState); INativeClassStruct val = UnityVersionHandler.Wrap((Il2CppClass*)(void*)Il2CppClassPointerStore>.NativeClassPtr); for (int i = 0; i < val.MethodCount; i++) { INativeMethodInfoStruct val2 = UnityVersionHandler.Wrap(val.Methods[i]); if (Marshal.PtrToStringAnsi(val2.Name) == "ChangeStateFromQueue") { ChangeStateDetour = INativeDetour.CreateAndApply(val2.MethodPointer, (d_ChangeStateFromQueue)ChangeStatePatch, ref orig_ChangeStateFromQueue); break; } } } private unsafe static void ChangeStatePatch(IntPtr _this, Il2CppMethodInfo* methodInfo) { StateMachine val = new StateMachine(_this); if (val.CurrentState == null) { orig_ChangeStateFromQueue(_this, methodInfo); return; } ES_Base state = val.m_stateQueue.Peek(); bool flag = true; try { flag = NativePatchAPI.RunChangeStatePrefix(val, state); } catch (Exception ex) { DinoLogger.Error("Error running ChangeStatePrefix: " + ex.StackTrace); } if (flag) { orig_ChangeStateFromQueue(_this, methodInfo); } else { val.m_stateQueue.Dequeue(); } try { NativePatchAPI.RunChangeStatePostfix(val, state); } catch (Exception ex2) { DinoLogger.Error("Error running ChangeStatePostfix: " + ex2.StackTrace); } } private static bool FixMeleeCancel(StateMachine __instance, ES_Base newState) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Invalid comparison between Unknown and I4 //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Invalid comparison between Unknown and I4 //IL_003e: Unknown result type (might be due to invalid IL or missing references) ES_Base currentState = __instance.CurrentState; if (!NotifyManager.MasterHasFix || (int)newState.m_stateEnum != 2 || (int)currentState.m_stateEnum != 23) { return true; } ES_StrikerMelee val = ((Il2CppObjectBase)currentState).Cast(); return (Clock.Time - val.m_startTime) * val.m_animSpeed >= val.m_attackData.Duration; } } internal static class IsStuckPatches { private const int AddssLen = 8; private static readonly byte[] Instructions = new byte[8] { 243, 15, 16, 5, 14, 0, 244, 0 }; public unsafe static void ApplyInstructionPatch() { INativeClassStruct val = UnityVersionHandler.Wrap((Il2CppClass*)(void*)Il2CppClassPointerStore.NativeClassPtr); for (int i = 0; i < val.MethodCount; i++) { INativeMethodInfoStruct val2 = UnityVersionHandler.Wrap(val.Methods[i]); if (!(Marshal.PtrToStringAnsi(val2.Name) == "IsStuck")) { continue; } IntPtr intPtr = FindAdd(val2.MethodPointer); if (intPtr == IntPtr.Zero) { DinoLogger.Error("Unable to find instruction in HasValidTarget. Not applying fix for enemies stuck on spawning."); break; } using (new MemoryProtectionCookie(intPtr, Kernel32.MemoryProtectionConstant.ExecuteReadWrite, new IntPtr(16))) { for (int j = 0; j < 8; j++) { *(byte*)(void*)(intPtr + j) = Instructions[j]; } break; } } } private unsafe static IntPtr FindAdd(IntPtr methodPointer) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Invalid comparison between Unknown and I4 //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Invalid comparison between Unknown and I4 StreamCodeReader val = new StreamCodeReader((Stream)new UnmanagedMemoryStream((byte*)(void*)methodPointer, 65536L, 65536L, FileAccess.Read)); Decoder val2 = Decoder.Create(sizeof(void*) * 8, (CodeReader)(object)val, (DecoderOptions)0); val2.IP = (ulong)(long)methodPointer; Instruction val3 = default(Instruction); val2.Decode(ref val3); while ((int)((Instruction)(ref val3)).Mnemonic != 1620) { if ((int)((Instruction)(ref val3)).Mnemonic == 11) { val.Stream.Close(); if (((Instruction)(ref val3)).NextIP - ((Instruction)(ref val3)).IP != 8) { DinoLogger.Error("EnemyAnimationFix found an instruction with an unexpected width."); return IntPtr.Zero; } return (IntPtr)(long)((Instruction)(ref val3)).IP; } val2.Decode(ref val3); } val.Stream.Close(); return IntPtr.Zero; } } internal static class ValidTargetPatches { private const int NOP = 144; private const int AddssLen = 8; public unsafe static void ApplyInstructionPatch() { INativeClassStruct val = UnityVersionHandler.Wrap((Il2CppClass*)(void*)Il2CppClassPointerStore.NativeClassPtr); for (int i = 0; i < val.MethodCount; i++) { INativeMethodInfoStruct val2 = UnityVersionHandler.Wrap(val.Methods[i]); if (!(Marshal.PtrToStringAnsi(val2.Name) == "HasValidTarget")) { continue; } IntPtr intPtr = FindAdd(val2.MethodPointer); if (intPtr == IntPtr.Zero) { DinoLogger.Error("Unable to find instruction in HasValidTarget. Not applying fix for enemies stuck on spawning."); break; } using (new MemoryProtectionCookie(intPtr, Kernel32.MemoryProtectionConstant.ExecuteReadWrite, new IntPtr(16))) { for (int j = 0; j < 8; j++) { *(sbyte*)(void*)(intPtr + j) = -112; } break; } } } private unsafe static IntPtr FindAdd(IntPtr methodPointer) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Invalid comparison between Unknown and I4 //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Invalid comparison between Unknown and I4 StreamCodeReader val = new StreamCodeReader((Stream)new UnmanagedMemoryStream((byte*)(void*)methodPointer, 65536L, 65536L, FileAccess.Read)); Decoder val2 = Decoder.Create(sizeof(void*) * 8, (CodeReader)(object)val, (DecoderOptions)0); val2.IP = (ulong)(long)methodPointer; Instruction val3 = default(Instruction); val2.Decode(ref val3); while ((int)((Instruction)(ref val3)).Mnemonic != 1620) { if ((int)((Instruction)(ref val3)).Mnemonic == 11) { val.Stream.Close(); if (((Instruction)(ref val3)).NextIP - ((Instruction)(ref val3)).IP != 8) { DinoLogger.Error("EnemyAnimationFix found an instruction with an unexpected width."); return IntPtr.Zero; } return (IntPtr)(long)((Instruction)(ref val3)).IP; } val2.Decode(ref val3); } val.Stream.Close(); return IntPtr.Zero; } } } namespace EnemyAnimationFix.NativePatches.KernelTools { internal static class Kernel32 { [Flags] public enum MemoryAllocationConstant : uint { Commit = 0x1000u, Reserve = 0x2000u, Reset = 0x80000u, ResetUndo = 0x1000000u, LargePages = 0x20000000u, Physical = 0x400000u, TopDown = 0x100000u, WriteWatch = 0x200000u } [Flags] public enum MemoryProtectionConstant : uint { NoAccess = 1u, ReadOnly = 2u, ReadWrite = 4u, WriteCopy = 8u, Execute = 0x10u, ExecuteRead = 0x20u, ExecuteReadWrite = 0x40u, ExecuteWriteCopy = 0x80u } [DllImport("kernel32.dll")] public static extern IntPtr VirtualAlloc(IntPtr lpAddress, IntPtr dwSize, MemoryAllocationConstant flAllocationType, MemoryProtectionConstant flProtect); [DllImport("kernel32.dll")] public static extern bool VirtualProtect(IntPtr lpAddress, IntPtr dwSize, MemoryProtectionConstant flNewProtect, out MemoryProtectionConstant lpflOldProtect); } internal struct MemoryProtectionCookie : IDisposable { private Kernel32.MemoryProtectionConstant m_OldProtection; private readonly IntPtr m_Address; private readonly IntPtr m_Size; public MemoryProtectionCookie(IntPtr address, Kernel32.MemoryProtectionConstant newProtection, IntPtr size) { m_Address = address; m_Size = size; if (!Kernel32.VirtualProtect(m_Address, m_Size, newProtection, out m_OldProtection)) { Environment.FailFast($"Failed to protect address 0x{m_Address:X2} with protection {newProtection}"); } } public void Dispose() { if (!Kernel32.VirtualProtect(m_Address, m_Size, m_OldProtection, out m_OldProtection)) { Environment.FailFast($"Failed to revert memory protection of address 0x{m_Address:X2}"); } } } } namespace EnemyAnimationFix.API { public static class NativePatchAPI { public delegate bool ChangeStatePrefix(StateMachine __instance, ES_Base state); public delegate void ChangeStatePostfix(StateMachine __instance, ES_Base state); private static readonly List s_changePrefix = new List(); private static readonly List s_changePostfix = new List(); public static void AddChangeStatePrefix(ChangeStatePrefix detectPrefix) { s_changePrefix.Add(detectPrefix); } public static void AddChangeStatePostfix(ChangeStatePostfix detectPostfix) { s_changePostfix.Add(detectPostfix); } internal static bool RunChangeStatePrefix(StateMachine __instance, ES_Base state) { bool flag = true; foreach (ChangeStatePrefix item in s_changePrefix) { flag &= item(__instance, state); } return flag; } internal static void RunChangeStatePostfix(StateMachine __instance, ES_Base state) { foreach (ChangeStatePostfix item in s_changePostfix) { item(__instance, state); } } } }