diff --git a/MiraAPI/Events/Mira/MiraButtonClickEvent.cs b/MiraAPI/Events/Mira/MiraButtonClickEvent.cs
index dea3635..6126483 100644
--- a/MiraAPI/Events/Mira/MiraButtonClickEvent.cs
+++ b/MiraAPI/Events/Mira/MiraButtonClickEvent.cs
@@ -11,7 +11,7 @@ namespace MiraAPI.Events.Mira;
/// The type.
/// The that was clicked.
/// The generic invoked before button-specific events.
-public sealed class MiraButtonClickEvent(T button, MiraButtonClickEvent genericClickEvent) : MiraCancelableEvent where T : CustomActionButton
+public class MiraButtonClickEvent(T button, MiraButtonClickEvent genericClickEvent) : MiraCancelableEvent where T : CustomActionButton
{
///
/// Gets the that was clicked.
@@ -31,7 +31,7 @@ public sealed class MiraButtonClickEvent(T button, MiraButtonClickEvent gener
/// Do not use for vanilla s.
///
/// The that was clicked.
-public sealed class MiraButtonClickEvent(CustomActionButton button) : MiraCancelableEvent
+public class MiraButtonClickEvent(CustomActionButton button) : MiraCancelableEvent
{
///
/// Gets the that was clicked.
diff --git a/MiraAPI/Hud/CustomButtonManager.cs b/MiraAPI/Hud/CustomButtonManager.cs
index 378c907..9ea1953 100644
--- a/MiraAPI/Hud/CustomButtonManager.cs
+++ b/MiraAPI/Hud/CustomButtonManager.cs
@@ -18,6 +18,20 @@ public static class CustomButtonManager
///
public static ReadOnlyCollection Buttons { get; internal set; } = new([]);
+ ///
+ /// Gets a list of all registered button-specific s.
+ ///
+ public static ReadOnlyDictionary EventTypes { get; internal set; } = new(
+ new Dictionary
+ { });
+
+ ///
+ /// Gets a list of all registered button-specific s.
+ ///
+ public static ReadOnlyDictionary CancelledEventTypes { get; internal set; } = new(
+ new Dictionary
+ { });
+
internal static readonly List CustomButtons = [];
internal static readonly Dictionary ButtonEventTypes = [];
internal static readonly Dictionary ButtonCancelledEventTypes = [];
diff --git a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs
index 4aecf06..b564f81 100644
--- a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs
+++ b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs
@@ -545,12 +545,8 @@ public static class RoleSettingMenuPatches
__instance.roleDescriptionText.transform.parent.localScale = new Vector3(0.0675f, 0.1494f, 0.5687f);
labelBg.transform.localPosition = new Vector3(1.082f, 0.1054f, -2.5f);
- __instance.roleScreenshot.sprite = Sprite.Create(
- role.RoleScreenshot.texture,
- new Rect(0, 0, 370, 230),
- Vector2.one / 2,
- 100);
- __instance.roleScreenshot.drawMode = SpriteDrawMode.Sliced;
+ __instance.roleScreenshot.sprite = role.RoleScreenshot;
+ __instance.roleScreenshot.drawMode = SpriteDrawMode.Simple;
}
__instance.roleHeaderSprite.color = customRole.OptionsMenuColor;
diff --git a/MiraAPI/PluginLoading/MiraPluginManager.cs b/MiraAPI/PluginLoading/MiraPluginManager.cs
index 8d385b5..1194533 100644
--- a/MiraAPI/PluginLoading/MiraPluginManager.cs
+++ b/MiraAPI/PluginLoading/MiraPluginManager.cs
@@ -139,6 +139,8 @@ public sealed class MiraPluginManager
{
// Save all buttons into a read-only collection for easy access
CustomButtonManager.Buttons = new ReadOnlyCollection(CustomButtonManager.CustomButtons);
+ CustomButtonManager.EventTypes = new ReadOnlyDictionary(CustomButtonManager.ButtonEventTypes);
+ CustomButtonManager.CancelledEventTypes = new ReadOnlyDictionary(CustomButtonManager.ButtonCancelledEventTypes);
// Cache all the registered plugins into an array for easy access
RegisteredPlugins = [.. _registeredPlugins.Values];
diff --git a/MiraAPI/Roles/CustomRoleManager.cs b/MiraAPI/Roles/CustomRoleManager.cs
index c5d62b2..91c3763 100644
--- a/MiraAPI/Roles/CustomRoleManager.cs
+++ b/MiraAPI/Roles/CustomRoleManager.cs
@@ -141,11 +141,18 @@ public static class CustomRoleManager
roleBehaviour.CanVent = customRole.Configuration.CanUseVent || customRole.Configuration.GetsVentData;
roleBehaviour.DefaultGhostRole = customRole.Configuration.GhostRole;
roleBehaviour.MaxCount = customRole.Configuration.MaxRoleCount;
- roleBehaviour.RoleScreenshot = Sprite.Create(
- customRole.Configuration.OptionsScreenshot?.LoadAsset().texture,
- new Rect(0, 0, 370, 230),
- Vector2.one / 2,
- 100);
+ if (customRole.Configuration.OptionsScreenshot != null)
+ {
+ roleBehaviour.RoleScreenshot = customRole.Configuration.OptionsScreenshot.LoadAsset();
+ }
+ else
+ {
+ roleBehaviour.RoleScreenshot = Sprite.Create(
+ null,
+ new Rect(0, 0, 370, 230),
+ Vector2.one / 2,
+ 100);
+ }
_emptySettings ??= new(0);
_emptyKillAnimations ??= new(0);
diff --git a/MiraAPI/Utilities/Assets/LoadableBundleSubAsset.cs b/MiraAPI/Utilities/Assets/LoadableBundleSubAsset.cs
new file mode 100644
index 0000000..ef7aea3
--- /dev/null
+++ b/MiraAPI/Utilities/Assets/LoadableBundleSubAsset.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Linq;
+using UnityEngine;
+
+namespace MiraAPI.Utilities.Assets;
+
+///
+/// A utility class for loading multiple assets from an .
+///
+/// The name of the asset.
+/// The that contains the asset.
+public class LoadableBundleSubAsset(string name, LoadableBundleSubAssetHolder assetHolder) : LoadableAsset
+{
+ ///
+ /// Loads the asset from the .
+ ///
+ /// The asset.
+ /// The asset did not load properly.
+ public override Sprite LoadAsset()
+ {
+ if (LoadedAsset != null)
+ {
+ return LoadedAsset;
+ }
+
+ assetHolder.TryInit();
+
+ var loadedAsset = assetHolder.SubSprites.FirstOrDefault(x => x.name == name);
+
+ if (loadedAsset == null)
+ {
+ throw new InvalidOperationException($"INVALID ASSETS: {name}");
+ }
+ LoadedAsset = loadedAsset;
+ return LoadedAsset;
+ }
+}
diff --git a/MiraAPI/Utilities/Assets/LoadableBundleSubAssetHolder.cs b/MiraAPI/Utilities/Assets/LoadableBundleSubAssetHolder.cs
new file mode 100644
index 0000000..3b84d8d
--- /dev/null
+++ b/MiraAPI/Utilities/Assets/LoadableBundleSubAssetHolder.cs
@@ -0,0 +1,86 @@
+using System;
+using System.Linq;
+using HarmonyLib;
+using Il2CppInterop.Runtime;
+using Reactor.Utilities.Extensions;
+using UnityEngine;
+
+namespace MiraAPI.Utilities.Assets;
+
+///
+/// A utility class for loading multiple assets from an .
+///
+public class LoadableBundleSubAssetHolder
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The name of the assets to pull from.
+ /// The that contains the assets.
+ public LoadableBundleSubAssetHolder(string[] names, AssetBundle bundle)
+ {
+ Bundle = bundle;
+ SpriteNames = names;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The name of the asset.
+ /// The that contains the assets.
+ public LoadableBundleSubAssetHolder(string name, AssetBundle bundle)
+ {
+ Bundle = bundle;
+ SpriteNames = [name];
+ }
+ internal string[] SpriteNames = [];
+ internal AssetBundle Bundle;
+ public Sprite[] SubSprites = [];
+
+ public void TryInit()
+ {
+ if (SubSprites.Length == 0)
+ {
+ var newSprites = Array.Empty();
+ foreach (var name in SpriteNames)
+ {
+ var loadedAssets = Bundle.LoadAssetWithSubAssets(name, Il2CppType.From(typeof(Sprite))).ToArray();
+
+ if (loadedAssets == null)
+ {
+ throw new InvalidOperationException($"INVALID ASSETS: {name}");
+ }
+ foreach (var obj in loadedAssets)
+ {
+ var img = obj.TryCast();
+ if (img != null)
+ {
+ img.DontDestroy().DontUnload();
+ newSprites = newSprites.AddToArray(img);
+ }
+ }
+ }
+
+ SubSprites = newSprites;
+ }
+ }
+
+ ///
+ /// Unloads an asset.
+ ///
+ /// True if the asset was unloaded, false otherwise.
+ public bool UnloadAsset()
+ {
+ if (SubSprites.Length == 0)
+ {
+ return false;
+ }
+
+ foreach (var sprite in SubSprites)
+ {
+ sprite.DestroyImmediate();
+ }
+ SubSprites = [];
+ return true;
+ }
+}
diff --git a/MiraAPI/Utilities/Assets/TmpSpriteUtils.cs b/MiraAPI/Utilities/Assets/TmpSpriteUtils.cs
index 8b9bf9f..561bfd0 100644
--- a/MiraAPI/Utilities/Assets/TmpSpriteUtils.cs
+++ b/MiraAPI/Utilities/Assets/TmpSpriteUtils.cs
@@ -106,8 +106,8 @@ public static class TmpSpriteUtils
{
name = spriteName,
hashCode = TMP_TextUtilities.GetSimpleHashCode(spriteName),
- x = 0,
- y = 0,
+ x = rect.x,
+ y = rect.y,
width = rect.width,
height = rect.height,
xOffset = -(rect.width / 3) + (scale - 1) * (rect.width / 2),