From cb794c3fc23f29dff5601bc7f99d5a943ac3425e Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Tue, 14 Oct 2025 14:35:20 -0400 Subject: [PATCH] make kill anim patch update resilient --- .../Events/KillAnimationMurderEventPatch.cs | 35 +++++++++++++++++++ .../Patches/Events/KillAnimationPatches.cs | 30 ---------------- MiraAPI/Utilities/StateMachineWrapper.cs | 35 ++++++++++++++++++- 3 files changed, 69 insertions(+), 31 deletions(-) create mode 100644 MiraAPI/Patches/Events/KillAnimationMurderEventPatch.cs delete mode 100644 MiraAPI/Patches/Events/KillAnimationPatches.cs diff --git a/MiraAPI/Patches/Events/KillAnimationMurderEventPatch.cs b/MiraAPI/Patches/Events/KillAnimationMurderEventPatch.cs new file mode 100644 index 0000000..2618153 --- /dev/null +++ b/MiraAPI/Patches/Events/KillAnimationMurderEventPatch.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using HarmonyLib; +using Il2CppInterop.Runtime.InteropTypes; +using MiraAPI.Events; +using MiraAPI.Events.Vanilla.Gameplay; +using MiraAPI.Utilities; + +namespace MiraAPI.Patches.Events; + +/// +/// Patch to invoke AfterMurderEvent after a kill animation is performed. +/// +[HarmonyPatch] +public static class KillAnimationMurderEventPatch +{ + public static MethodBase TargetMethod() + { + return Helpers.GetStateMachineMoveNext(nameof(KillAnimation.CoPerformKill))!; + } + + public static void Postfix(Il2CppObjectBase __instance) + { + var wrapper = new StateMachineWrapper(__instance); + if (wrapper.GetState() != 1) + { + return; + } + + var source = wrapper.GetParameter("source"); + var target = wrapper.GetParameter("target"); + + var afterMurderEvent = new AfterMurderEvent(source, target, Helpers.GetBodyById(target.PlayerId)); + MiraEventManager.InvokeEvent(afterMurderEvent); + } +} diff --git a/MiraAPI/Patches/Events/KillAnimationPatches.cs b/MiraAPI/Patches/Events/KillAnimationPatches.cs deleted file mode 100644 index d8c6336..0000000 --- a/MiraAPI/Patches/Events/KillAnimationPatches.cs +++ /dev/null @@ -1,30 +0,0 @@ -using HarmonyLib; -using MiraAPI.Events; -using MiraAPI.Events.Vanilla.Gameplay; -using MiraAPI.Utilities; - -namespace MiraAPI.Patches.Events; - -/// -/// General patches for the KillAnimation class. -/// -[HarmonyPatch] -public static class KillAnimationPatches -{ - /// - /// Used to trigger the . - /// - [HarmonyPostfix] - [HarmonyPatch(typeof(KillAnimation._CoPerformKill_d__2), nameof(KillAnimation._CoPerformKill_d__2.MoveNext))] - public static void KillAnimPostfix(KillAnimation._CoPerformKill_d__2 __instance) - { - // Checking if state is -1 to wait for the end of coroutine. - if (__instance.__1__state != -1) - { - return; - } - - var afterMurderEvent = new AfterMurderEvent(__instance.source, __instance.target, Helpers.GetBodyById(__instance.target.PlayerId)); - MiraEventManager.InvokeEvent(afterMurderEvent); - } -} diff --git a/MiraAPI/Utilities/StateMachineWrapper.cs b/MiraAPI/Utilities/StateMachineWrapper.cs index f8d9d8b..a760e8e 100644 --- a/MiraAPI/Utilities/StateMachineWrapper.cs +++ b/MiraAPI/Utilities/StateMachineWrapper.cs @@ -1,4 +1,6 @@ -using System.Reflection; +using System; +using System.Collections.Generic; +using System.Reflection; using HarmonyLib; using Il2CppInterop.Runtime.InteropTypes; @@ -11,8 +13,11 @@ namespace MiraAPI.Utilities; public class StateMachineWrapper where T : Il2CppObjectBase { private readonly Il2CppObjectBase _stateMachine; + + // normally it is fields, but IL2CPP turns them into properties private readonly PropertyInfo _thisProperty; private readonly PropertyInfo _stateProperty; + private readonly Dictionary _propertyCache; private T? _parentInstance; @@ -32,6 +37,13 @@ public class StateMachineWrapper where T : Il2CppObjectBase var type = _stateMachine.GetType(); _thisProperty = AccessTools.Property(type, "__4__this"); _stateProperty = AccessTools.Property(type, "__1__state"); + + if (_thisProperty == null || _stateProperty == null) + { + throw new MissingMemberException($"Could not find required properties in type '{type}'."); + } + + _propertyCache = []; } /// @@ -39,4 +51,25 @@ public class StateMachineWrapper where T : Il2CppObjectBase /// /// The current state as an integer. public int GetState() => (int)_stateProperty.GetValue(_stateMachine)!; + + /// + /// Gets a parameter from the state machine by its name. + /// + /// The name of the parameter to retrieve. + /// The type of the parameter to retrieve. + /// >The value of the specified parameter. + /// Thrown if the specified parameter does not exist. + public TField GetParameter(string parameterName) + { + if (_propertyCache.TryGetValue(parameterName, out var property)) + { + return (TField)property.GetValue(_stateMachine)!; + } + + property = AccessTools.Property(_stateMachine.GetType(), parameterName) + ?? throw new MissingFieldException($"Could not find parameter '{parameterName}' in state machine of type '{_stateMachine.GetType()}'."); + + _propertyCache[parameterName] = property; + return (TField)property.GetValue(_stateMachine)!; + } }