diff --git a/MiraAPI/Animations/AnimationPresetManager.cs b/MiraAPI/Animations/AnimationPresetManager.cs new file mode 100644 index 0000000..f3fa1bf --- /dev/null +++ b/MiraAPI/Animations/AnimationPresetManager.cs @@ -0,0 +1,46 @@ +using MiraAPI.Animations; +using System; +using System.Collections.Generic; + +namespace MiraAPI.Modifiers; + +/// +/// The manager for handling modifiers. +/// +public static class AnimationPresetManager +{ + private static uint _nextId; + + /// + /// Gets registered Animation Presets. + /// + public static List Presets { get; internal set; } = new List(); + + private static uint GetNextId() + { + _nextId++; + return _nextId; + } + + /// + /// Get Animation Preset by ID. + /// + /// The id of the preset you want to find. + /// The animation preset. + public static CustomAnimationPreset? GetPresetById(uint id) + { + return Presets.Find(p => p.Id == id); + } + + internal static void RegisterPreset(Type modifierType) + { + if (!typeof(CustomAnimationPreset).IsAssignableFrom(modifierType)) + { + return; + } + + CustomAnimationPreset preset = (CustomAnimationPreset)Activator.CreateInstance(modifierType); + preset.Id = GetNextId(); + Presets.Add(preset); + } +} diff --git a/MiraAPI/Animations/CustomAnimationPreset.cs b/MiraAPI/Animations/CustomAnimationPreset.cs new file mode 100644 index 0000000..21167ff --- /dev/null +++ b/MiraAPI/Animations/CustomAnimationPreset.cs @@ -0,0 +1,24 @@ +using UnityEngine; + +namespace MiraAPI.Animations; + +/// +/// Configure an animation's settings using presets. +/// +/// The animation clip the preset will use. +public abstract class CustomAnimationPreset(AnimationClip clip) +{ + internal uint Id; + + internal void PlayAnimation(PlayerControl source) + { + + } + + /// + /// Check whether the local player should be able to see this animation. + /// + /// The player who the animation is being triggered upon. + /// Whether the local player can see the animation or not. + public abstract bool AnimationVisible(PlayerControl source); +} \ No newline at end of file diff --git a/MiraAPI/Animations/CustomAnimator.cs b/MiraAPI/Animations/CustomAnimator.cs new file mode 100644 index 0000000..bf29de8 --- /dev/null +++ b/MiraAPI/Animations/CustomAnimator.cs @@ -0,0 +1,38 @@ +using MiraAPI.Animations.Presets; +using MiraAPI.Modifiers; +using MiraAPI.Networking; +using MiraAPI.Utilities; +using Reactor.Networking.Attributes; +using Reactor.Utilities.Attributes; +using System; +using UnityEngine; + +namespace MiraAPI.Animations; + +[RegisterInIl2Cpp] +public class CustomAnimator(IntPtr cppPtr) : MonoBehaviour(cppPtr) +{ + private PlayerControl _player; + + private void Start() + { + _player = gameObject.GetComponent(); + + _player.GetCustomAnimator()?.TriggerCustomAnimation(new LocalAnimationPreset(new AnimationClip())); + } + + public void TriggerCustomAnimation(CustomAnimationPreset preset) => RpcTriggerCustomAnimation(_player, preset.Id); + + + [MethodRpc((uint)MiraRpc.TriggerAnimation)] + internal static void RpcTriggerCustomAnimation(PlayerControl source, uint presetId) + { + CustomAnimationPreset preset = AnimationPresetManager.GetPresetById(presetId); + if (preset is null) return; + + if (preset.AnimationVisible(source)) + { + preset.PlayAnimation(source); + } + } +} \ No newline at end of file diff --git a/MiraAPI/Animations/Presets/LocalAnimationPreset.cs b/MiraAPI/Animations/Presets/LocalAnimationPreset.cs new file mode 100644 index 0000000..d3d2298 --- /dev/null +++ b/MiraAPI/Animations/Presets/LocalAnimationPreset.cs @@ -0,0 +1,11 @@ +using UnityEngine; + +namespace MiraAPI.Animations.Presets; +public class LocalAnimationPreset(AnimationClip clip) : CustomAnimationPreset(clip) +{ + /// + public override bool AnimationVisible(PlayerControl source) + { + return source.AmOwner; + } +} diff --git a/MiraAPI/Animations/RegisterAnimationPresetAttribute.cs b/MiraAPI/Animations/RegisterAnimationPresetAttribute.cs new file mode 100644 index 0000000..ee2e1bc --- /dev/null +++ b/MiraAPI/Animations/RegisterAnimationPresetAttribute.cs @@ -0,0 +1,6 @@ +using System; + +namespace MiraAPI.Animations; + +[AttributeUsage(AttributeTargets.Class)] +public class RegisterAnimationPresetAttribute : Attribute; \ No newline at end of file diff --git a/MiraAPI/Networking/MiraRpc.cs b/MiraAPI/Networking/MiraRpc.cs index 5126dea..f5bbe56 100644 --- a/MiraAPI/Networking/MiraRpc.cs +++ b/MiraAPI/Networking/MiraRpc.cs @@ -34,4 +34,9 @@ public enum MiraRpc : uint /// Custom Murder RPC. /// CustomMurder, + + /// + /// Trigger Animation using CustomAnimator. + /// + TriggerAnimation, } diff --git a/MiraAPI/Patches/PlayerControlPatches.cs b/MiraAPI/Patches/PlayerControlPatches.cs index b9bc0f2..e58d49c 100644 --- a/MiraAPI/Patches/PlayerControlPatches.cs +++ b/MiraAPI/Patches/PlayerControlPatches.cs @@ -1,4 +1,5 @@ using HarmonyLib; +using MiraAPI.Animations; using MiraAPI.Hud; using MiraAPI.Modifiers; using MiraAPI.Roles; @@ -13,8 +14,9 @@ namespace MiraAPI.Patches; public static class PlayerControlPatches { /// - /// Adds the modifier component to the player on start. + /// Adds the modifier and animator component to the player on start. /// + /// The player which will be given the components. [HarmonyPrefix] [HarmonyPatch(nameof(PlayerControl.Start))] public static void PlayerControlStartPostfix(PlayerControl __instance) @@ -24,12 +26,20 @@ public static class PlayerControlPatches comp.DestroyImmediate(); } + if (__instance.gameObject.TryGetComponent(out var anim)) + { + anim.DestroyImmediate(); + } + + __instance.gameObject.AddComponent(); __instance.gameObject.AddComponent(); } /// /// Calls the OnDeath method for all active modifiers. /// + /// The player who died. + /// The reason of the death. [HarmonyPostfix] [HarmonyPatch(nameof(PlayerControl.Die))] public static void PlayerControlDiePostfix(PlayerControl __instance, DeathReason reason) @@ -38,13 +48,14 @@ public static class PlayerControlPatches if (modifiersComponent) { - modifiersComponent.ActiveModifiers.ForEach(x=>x.OnDeath(reason)); + modifiersComponent.ActiveModifiers.ForEach(x => x.OnDeath(reason)); } } /// /// FixedUpdate handler for custom roles and custom buttons. /// + /// The player who's fixed update is being called. [HarmonyPostfix] [HarmonyPatch(nameof(PlayerControl.FixedUpdate))] public static void PlayerControlFixedUpdatePostfix(PlayerControl __instance) diff --git a/MiraAPI/PluginLoading/MiraPluginManager.cs b/MiraAPI/PluginLoading/MiraPluginManager.cs index 2a735b8..a9cccd0 100644 --- a/MiraAPI/PluginLoading/MiraPluginManager.cs +++ b/MiraAPI/PluginLoading/MiraPluginManager.cs @@ -1,8 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using BepInEx.Unity.IL2CPP; +using BepInEx.Unity.IL2CPP; +using MiraAPI.Animations; using MiraAPI.Colors; using MiraAPI.GameOptions; using MiraAPI.GameOptions.Attributes; @@ -10,17 +7,29 @@ using MiraAPI.Hud; using MiraAPI.Modifiers; using MiraAPI.Roles; using MiraAPI.Utilities; +using MiraAPI.Utilities.Assets; using Reactor.Networking; using Reactor.Utilities; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; namespace MiraAPI.PluginLoading; internal sealed class MiraPluginManager { private readonly Dictionary _registeredPlugins = []; - internal MiraPluginInfo[] RegisteredPlugins() => [.. _registeredPlugins.Values]; + public List> BundleAssets = new(); + internal uint NextAssetId { get; private set; } + internal uint GetNextAssetId() + { + NextAssetId++; + return NextAssetId; + } + public static MiraPluginManager Instance { get; private set; } = new(); internal void Initialize() @@ -36,6 +45,7 @@ internal sealed class MiraPluginManager var info = new MiraPluginInfo(miraPlugin, pluginInfo); RegisterModifierAttribute(assembly); + RegisterAnimPresetAttribute(assembly); RegisterAllOptions(assembly, info); RegisterRoleAttribute(assembly, info); @@ -161,6 +171,18 @@ internal sealed class MiraPluginManager } } + private static void RegisterAnimPresetAttribute(Assembly assembly) + { + foreach (var type in assembly.GetTypes()) + { + var attribute = type.GetCustomAttribute(); + if (attribute != null) + { + AnimationPresetManager.RegisterPreset(type); + } + } + } + private static void RegisterButtonAttribute(Assembly assembly) { foreach (var type in assembly.GetTypes()) diff --git a/MiraAPI/Utilities/Assets/LoadableBundleAsset.cs b/MiraAPI/Utilities/Assets/LoadableBundleAsset.cs index 6f46eef..69204fd 100644 --- a/MiraAPI/Utilities/Assets/LoadableBundleAsset.cs +++ b/MiraAPI/Utilities/Assets/LoadableBundleAsset.cs @@ -1,5 +1,6 @@ -using System; +using MiraAPI.PluginLoading; using Reactor.Utilities.Extensions; +using System; using UnityEngine; namespace MiraAPI.Utilities.Assets; @@ -10,8 +11,30 @@ namespace MiraAPI.Utilities.Assets; /// The name of the asset. /// The AssetBundle that contains the asset. /// The type of the asset to be loaded. -public class LoadableBundleAsset(string name, AssetBundle bundle) : LoadableAsset where T : UnityEngine.Object +public class LoadableBundleAsset : LoadableAsset where T : UnityEngine.Object { + + /// + /// Gets the ID of this asset. Used for networking. + /// + public uint AssetId { get; internal set; } + private string _name { get; } + private AssetBundle _bundle { get; } + + /// + /// Initializes a new instance of the class. + /// + /// Name of the bundle. + /// Asset bundle. + public LoadableBundleAsset(string name, AssetBundle bundle) + { + AssetId = MiraPluginManager.Instance.GetNextAssetId(); + //MiraPluginManager.Instance.BundleAssets.Add((LoadableBundleAsset)this); + + _name = name; + _bundle = bundle; + } + /// /// Loads the asset from the asset bundle. /// @@ -24,11 +47,11 @@ public class LoadableBundleAsset(string name, AssetBundle bundle) : LoadableA return LoadedAsset; } - LoadedAsset = bundle.LoadAsset(name); + LoadedAsset = _bundle.LoadAsset(_name); if (LoadedAsset == null) { - throw new InvalidOperationException($"INVALID ASSET: {name}"); + throw new InvalidOperationException($"INVALID ASSET: {_name}"); } return LoadedAsset; diff --git a/MiraAPI/Utilities/Extensions.cs b/MiraAPI/Utilities/Extensions.cs index 8374649..bf9ecd3 100644 --- a/MiraAPI/Utilities/Extensions.cs +++ b/MiraAPI/Utilities/Extensions.cs @@ -1,13 +1,14 @@ -using System; -using System.Collections.Generic; -using System.Linq; using HarmonyLib; +using MiraAPI.Animations; using MiraAPI.GameOptions; using MiraAPI.Modifiers; using MiraAPI.Networking; using MiraAPI.Roles; using Reactor.Networking.Attributes; using Reactor.Utilities; +using System; +using System.Collections.Generic; +using System.Linq; using UnityEngine; namespace MiraAPI.Utilities; @@ -116,6 +117,7 @@ public static class Extensions } private static readonly Dictionary ModifierComponents = []; + private static readonly Dictionary AnimatorComponents = []; /// /// Gets the ModifierComponent for a player. @@ -138,6 +140,27 @@ public static class Extensions return component; } + /// + /// Gets the CustomAnimator for a player. + /// + /// The PlayerControl object. + /// A CustomAnimator if there is one, null otherwise. + public static CustomAnimator? GetCustomAnimator(this PlayerControl player) + { + if (AnimatorComponents.TryGetValue(player, out var component)) + { + return component; + } + + component = player.GetComponent(); + if (component) + { + AnimatorComponents[player] = component; + } + + return component; + } + /// /// Randomizes a list. ///