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