From 513be01c02a2402bbf928ce430617fb01ee8709a Mon Sep 17 00:00:00 2001
From: WanderingPixel <126269533+WanderingPix@users.noreply.github.com>
Date: Sat, 9 Aug 2025 22:12:15 +0100
Subject: [PATCH 1/8] Add Keybind icons for custom buttons
---
MiraAPI/Hud/CustomActionButton.cs | 17 ++++++++++++++++-
MiraAPI/Resources/KeybindButton.png | Bin 0 -> 3418 bytes
MiraAPI/Utilities/Assets/MiraAssets.cs | 5 +++++
3 files changed, 21 insertions(+), 1 deletion(-)
create mode 100644 MiraAPI/Resources/KeybindButton.png
diff --git a/MiraAPI/Hud/CustomActionButton.cs b/MiraAPI/Hud/CustomActionButton.cs
index ac48c4ae..78fa114b 100644
--- a/MiraAPI/Hud/CustomActionButton.cs
+++ b/MiraAPI/Hud/CustomActionButton.cs
@@ -1,11 +1,14 @@
using System;
using System.Globalization;
+using System.Linq;
using MiraAPI.Events;
using MiraAPI.Events.Mira;
+using MiraAPI.Keybinds;
using MiraAPI.Patches;
using MiraAPI.Utilities;
using MiraAPI.Utilities.Assets;
using Rewired;
+using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
@@ -120,6 +123,11 @@ public abstract class CustomActionButton
///
public ActionButton? Button { get; set; }
+ ///
+ /// Gets the gameObject used for the keybind icon.
+ ///
+ public GameObject KeybindIcon { get; set; }
+
///
/// The method used to create the button.
///
@@ -191,6 +199,13 @@ public abstract class CustomActionButton
}
}
}));
+
+ KeybindIcon = Object.Instantiate(Button.usesRemainingSprite.gameObject, Button.transform);
+ KeybindIcon.GetComponent().sprite = MiraAssets.KeybindButton.LoadAsset();
+ KeybindIcon.transform.GetChild(0).GetComponent().text =
+ KeybindManager.GetEntries().First(x => x.Id == $"{Name}_Keybind").Key.ToString();
+ KeybindIcon.name = "KeybindIcon";
+ KeybindIcon.transform.localPosition = new(0.341f, 0.374f, -0.1f);
}
///
@@ -558,4 +573,4 @@ public abstract class CustomActionButton : CustomActionButton where T : MonoB
Target = null;
SetOutline(false);
}
-}
+}
\ No newline at end of file
diff --git a/MiraAPI/Resources/KeybindButton.png b/MiraAPI/Resources/KeybindButton.png
new file mode 100644
index 0000000000000000000000000000000000000000..e537b3b5eb9228e966561af347d06adc898296e4
GIT binary patch
literal 3418
zcmZ8kc{tQv8~%+MW5(84LPHEGWH4jN5@p{hM6zULsVqf^NMvQ@^uSChP!2;cO+-*tW0^_}ZH=f0oi+|P6VJV|G5&G@*H+yDUZS(qE!vr(08
zSQwOj=2UJ=u>llfZ)OP8^hzwT9mr(^8v_8Kr1AXrTumRG~Dxs4{+A=>a|d1Ta$Ar4P~sdmiX1>GImXqkF$lZg~f3I#&$RXB#09L
zv6&^?#Q)3xOd}5h{}bn6b1aAhK(Nt;ZCCF>{)zjs`JdHs69g=R|A+tA6kw-FTmXb!
ziBVO0jL4*
z8(nV9Te$%4AnJBYfB(j8iwW`z*-i7+CUa_?_%2a)XBi_E?W2XPIpn2J#|Q`zaA-nm
zevJKXMiM1W8l=xbl16l$G(oLCNyL+BuMr@atjr}t*ns-C-KMLj)u~FVzjxZ&!YjYM
z3$yy1q3FwEc93yv?`Ee*>ZX1~1+KKaOJoTf;$cQ80G`MZ}0kKVBO>lA8u8w!0sIfv6e9>K7@`Ju({n7!=4Of`6-@6C4o{AI`j!vbVZD@t%=D
z!P1!r2r)SeV~P#+8|t33F~Fo8ApvUCUAXgmt$CGvxrdR!Bc}2k)hCK0c18Gb&a8^j
zebePY2Ku-Ux4u{b2v3X{^^Y|B@DW0W-P9Cq8>;Iq1X3WuYkOsyFV#I?hKb2}Gg>T1Dlx3)m=j3YWP0
zvjRpSgA?V$dyvJ(IDxzLx>8zDqFsTPIs=O&V%S?^4w)m3f=CHOY6X5pM?_2%u7t8d
zii&W~^VDKs8D_QQNUD4vMPrO!+MP`!l$MM0LgQ$A<-8Y$_T=!1ezW_C+=9M
z*aG+RuIc*%@zuQ?kODLh7H5Rt1Tmu!0wUQWltw+;u(u4#R9h+C=a7biKhldjKy^v!
zn)5-U(XsI#bkd#%PPy~M1DBwJ@gVrB=3T~Lu?J-YX+ZZ^G7P~V)cg3+LwWvZSMrd<
z$XOmE-ZfAv7AA1J};8r3O#9dm9tyRYN_
zLhK|gmc%{up4eJ?B&-^iM4r(qcY8Oh;F2*|cp$b6B7&a@PP{fOqzW%Ef7Mpvon%?4
zaJ#qh0Vdz9Yev?104e8jTE!UElPo4Ak#(fXP}R6vGQn~@F488z(_#mfJO@ONEay0x
zD6HXXA+;F8Gk8S3Pj;0g{@OJL^Ud_Jk{S7cSW$zsY02<3D&PK4)%r@uGF0w3
zhw{?B(w)yN)Qv8^F1<3Voa_h?mbhM982GB^6=5IT&FDMDp}@5^rU(tteAv6gVl8?L
zE)O1xmF4RZ!_6Nu)H0A2-(&OF8Q<U{JFgzO)
z5-@Fx&XnXw9$CFWpn>#p<}jFUid=prYf$*9?t@NT`e^kYrA~K8Q||LmnwU>A(tH1eVgxsiPd*n`(oWcca!(m4@xgzQn0DmFR-q@EVFLc
zHabYRtFBfau5@tE9nNK9<7WNk)-Lxoekj#;zYW)dsD~xA)VvZSw^f~=m4pV9(So+b
z6k(P6j}mL4_Lo!1C3C_nNxn5%%62`2K%-=gav$HW`
z1Q@o|Ecj_Fb@p&9HC_Arkglk7KI-b}6OwQ8j3LSp2n*y(>LlLVyJq>K_$RK!C!k`U
z5hT@rsY*d7BCRFx1N51{fcS;di#B9Y*)hLkki^hm;)9a);Ce3E*l1zP@bi+~RoqMH
zzrFCEbgk^C6|Zt-AJ3k
z*V?4VLgrV+tSa(!o6bTr+JdJEGN}rD`fwxY#*qJT*ypQif4FZk@tm;`n5pUt;8y
zg3iphMi)8*%%UF`;9A&&<OI6PrD`wdhZ@aEIYf3)vTDw{<
zZ`IZzZz&u`Y+5H_JmlgJF&l#d`B;Z3Fy6Y}bzT6&Uq(qj}KQQ+U~cRCs${iS&W
zJ6Kk4S+(7(o#_}?hsG*#OW35zJZ+nq$#KsUamd8ZGb&8S)qh-6jM)ZHk8t>A4l1w(
znwtbX1!Q$G7_w!{8h2+1CZr8p@i3;EMqI!#FXHVvb#Pihl#eCP+(b8#s
zs50vzNZTTIOsH3i4zG4@>-d0o`8@-mooU#2Oa%?O7g6YCw^QpE`?Fe&+#hP`d@&1-
zz%S8GF(YUA_Qw4?uFS!@VN3t5+g&FvBj}`3#mG2c!UgnOKmO3gVgVD;V&rmH^RHQ%w2j*w5>q0Bs03Ai4)$^fGK$@g$=TQ3|
z5fs5r)p`y3XaET-z!XjQKt3oW-iS)+5fbw@2(P+%vxYoA{<*cb!Pn`{j0!k{=6y4e
z#AiyUKPOUjHr7uq3>0dWUpLuQl^A_FyqlfC61R%^6vgUY0csmV#@jn~?khh8Ca1er
zLVr#3ap1oVJySSeaN4msVhO7Et7PxY*l?GBLt{w3;Ql`N98<7x?^IFipAUkCiLG&s
HAujemkRu07
literal 0
HcmV?d00001
diff --git a/MiraAPI/Utilities/Assets/MiraAssets.cs b/MiraAPI/Utilities/Assets/MiraAssets.cs
index 1bd130a1..148c65a3 100644
--- a/MiraAPI/Utilities/Assets/MiraAssets.cs
+++ b/MiraAPI/Utilities/Assets/MiraAssets.cs
@@ -94,4 +94,9 @@ public static class MiraAssets
/// Gets the white CategoryHeader used in the Settings Menu.
///
public static LoadableResourceAsset CategoryHeader { get; } = new("MiraAPI.Resources.CategoryHeader.png");
+
+ ///
+ /// Gets the sprite used for the Keybind icons.
+ ///
+ public static LoadableResourceAsset KeybindButton { get; } = new("MiraAPI.Resources.KeybindButton.png");
}
From 2ec3eec370f2640f26e401b55a92ef334208e458 Mon Sep 17 00:00:00 2001
From: XtraCube <72575280+XtraCube@users.noreply.github.com>
Date: Sun, 17 Aug 2025 16:34:56 -0400
Subject: [PATCH 2/8] bump version
---
Directory.Build.props | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Directory.Build.props b/Directory.Build.props
index f6b4be75..c8f52ba8 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -8,7 +8,7 @@
true
- 0.2.0
+ 0.2.1
dev
All Of Us, XtraCube, Angxl
From ce15e93e596996d674ca219ea7f644ce15672061 Mon Sep 17 00:00:00 2001
From: Atony <75191600+AtonyGit@users.noreply.github.com>
Date: Sun, 17 Aug 2025 20:40:03 -0500
Subject: [PATCH 3/8] Fix DefaultKeybind name + remove requirement
By default, buttons will use no keybind, and will not add a new keybind option in doing so.
---
MiraAPI.Example/Buttons/Freezer/FreezeButton.cs | 2 +-
MiraAPI.Example/Buttons/MeetingButton.cs | 2 +-
MiraAPI.Example/Buttons/NeutralKillerButton.cs | 2 +-
MiraAPI.Example/Buttons/Teleporter/TeleportButton.cs | 2 +-
MiraAPI/Hud/CustomActionButton.cs | 2 +-
MiraAPI/PluginLoading/MiraPluginManager.cs | 9 +++++++--
6 files changed, 12 insertions(+), 7 deletions(-)
diff --git a/MiraAPI.Example/Buttons/Freezer/FreezeButton.cs b/MiraAPI.Example/Buttons/Freezer/FreezeButton.cs
index 4edd4f82..ddefcc2c 100644
--- a/MiraAPI.Example/Buttons/Freezer/FreezeButton.cs
+++ b/MiraAPI.Example/Buttons/Freezer/FreezeButton.cs
@@ -20,7 +20,7 @@ public class FreezeButton : CustomActionButton
public override int MaxUses => (int)OptionGroupSingleton.Instance.FreezeUses;
public override LoadableAsset Sprite => ExampleAssets.ExampleButton;
- public override KeyboardKeyCode Defaultkeybind => KeyboardKeyCode.T;
+ public override KeyboardKeyCode DefaultKeybind => KeyboardKeyCode.T;
public override ModifierKey Modifier1 => ModifierKey.Control;
protected override void OnClick()
diff --git a/MiraAPI.Example/Buttons/MeetingButton.cs b/MiraAPI.Example/Buttons/MeetingButton.cs
index c0acc206..900d8a2c 100644
--- a/MiraAPI.Example/Buttons/MeetingButton.cs
+++ b/MiraAPI.Example/Buttons/MeetingButton.cs
@@ -16,7 +16,7 @@ public class MeetingButton : CustomActionButton
public override int MaxUses => 3;
public override LoadableAsset Sprite => ExampleAssets.ExampleButton;
- public override KeyboardKeyCode Defaultkeybind => KeyboardKeyCode.P;
+ public override KeyboardKeyCode DefaultKeybind => KeyboardKeyCode.P;
public override ModifierKey Modifier1 => ModifierKey.Control;
public override ModifierKey Modifier2 => ModifierKey.Shift;
diff --git a/MiraAPI.Example/Buttons/NeutralKillerButton.cs b/MiraAPI.Example/Buttons/NeutralKillerButton.cs
index 02fc9086..79fe06bb 100644
--- a/MiraAPI.Example/Buttons/NeutralKillerButton.cs
+++ b/MiraAPI.Example/Buttons/NeutralKillerButton.cs
@@ -13,7 +13,7 @@ public class NeutralKillerButton : CustomActionButton
public override string Name => "Win Game";
public override float Cooldown => 0f;
public override LoadableAsset Sprite => ExampleAssets.ExampleButton;
- public override KeyboardKeyCode Defaultkeybind => KeyboardKeyCode.K;
+ public override KeyboardKeyCode DefaultKeybind => KeyboardKeyCode.K;
protected override void OnClick()
{
CustomGameOver.Trigger([PlayerControl.LocalPlayer.Data]);
diff --git a/MiraAPI.Example/Buttons/Teleporter/TeleportButton.cs b/MiraAPI.Example/Buttons/Teleporter/TeleportButton.cs
index c4af2ca7..55f773be 100644
--- a/MiraAPI.Example/Buttons/Teleporter/TeleportButton.cs
+++ b/MiraAPI.Example/Buttons/Teleporter/TeleportButton.cs
@@ -22,7 +22,7 @@ public class TeleportButton : CustomActionButton
public override LoadableAsset Sprite => ExampleAssets.TeleportButton;
public override Color TextOutlineColor => new Color32(221, 176, 152, 255);
public static bool IsZoom { get; private set; }
- public override KeyboardKeyCode Defaultkeybind => KeyboardKeyCode.M;
+ public override KeyboardKeyCode DefaultKeybind => KeyboardKeyCode.M;
public override bool Enabled(RoleBehaviour? role)
{
return role is TeleporterRole;
diff --git a/MiraAPI/Hud/CustomActionButton.cs b/MiraAPI/Hud/CustomActionButton.cs
index ac48c4ae..81ac5bd2 100644
--- a/MiraAPI/Hud/CustomActionButton.cs
+++ b/MiraAPI/Hud/CustomActionButton.cs
@@ -56,7 +56,7 @@ public abstract class CustomActionButton
///
/// Gets or sets the current key binding for this button.
///
- public abstract KeyboardKeyCode Defaultkeybind { get; }
+ public virtual KeyboardKeyCode DefaultKeybind { get; } = KeyboardKeyCode.None;
///
/// The first optional modifier key (e.g., Control, Shift, Alt) that must be held with the main key to activate the button.
diff --git a/MiraAPI/PluginLoading/MiraPluginManager.cs b/MiraAPI/PluginLoading/MiraPluginManager.cs
index 02e91022..49adbda9 100644
--- a/MiraAPI/PluginLoading/MiraPluginManager.cs
+++ b/MiraAPI/PluginLoading/MiraPluginManager.cs
@@ -18,6 +18,7 @@ using MiraAPI.Roles;
using MiraAPI.Utilities;
using Reactor.Networking;
using Reactor.Utilities;
+using Rewired;
using UnityEngine;
namespace MiraAPI.PluginLoading;
@@ -137,9 +138,13 @@ public sealed class MiraPluginManager
foreach (var button in CustomButtonManager.Buttons)
{
- KeybindManager.Register($"{button.Name}_Keybind", $"Keybind for {button.Name}", button.Defaultkeybind, button.ClickHandler, modifier1: button.Modifier1, modifier2: button.Modifier2, modifier3: button.Modifier3);
+ if (button.DefaultKeybind is KeyboardKeyCode.None)
+ {
+ continue;
+ }
+ KeybindManager.Register($"{button.Name}_Keybind", $"Keybind for {button.Name}", button.DefaultKeybind, button.ClickHandler, modifier1: button.Modifier1, modifier2: button.Modifier2, modifier3: button.Modifier3);
- Logger.Info($"Registered keybind for button '{button.GetType().Name}' with default key {button.Defaultkeybind}.");
+ Logger.Info($"Registered keybind for button '{button.GetType().Name}' with default key {button.DefaultKeybind}.");
}
};
}
From f1a5aef581829259a812ab5539de5c00a97a3c1c Mon Sep 17 00:00:00 2001
From: Atony <75191600+AtonyGit@users.noreply.github.com>
Date: Sun, 17 Aug 2025 20:52:46 -0500
Subject: [PATCH 4/8] Remove requirement but allow blank keybind
---
MiraAPI.Example/Buttons/Freezer/FreezeButton.cs | 2 +-
MiraAPI.Example/Buttons/MeetingButton.cs | 2 +-
MiraAPI.Example/Buttons/NeutralKillerButton.cs | 2 +-
MiraAPI.Example/Buttons/Teleporter/TeleportButton.cs | 2 +-
MiraAPI/Hud/CustomActionButton.cs | 10 +++++-----
MiraAPI/PluginLoading/MiraPluginManager.cs | 6 +++---
6 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/MiraAPI.Example/Buttons/Freezer/FreezeButton.cs b/MiraAPI.Example/Buttons/Freezer/FreezeButton.cs
index ddefcc2c..1f10c6f5 100644
--- a/MiraAPI.Example/Buttons/Freezer/FreezeButton.cs
+++ b/MiraAPI.Example/Buttons/Freezer/FreezeButton.cs
@@ -20,7 +20,7 @@ public class FreezeButton : CustomActionButton
public override int MaxUses => (int)OptionGroupSingleton.Instance.FreezeUses;
public override LoadableAsset Sprite => ExampleAssets.ExampleButton;
- public override KeyboardKeyCode DefaultKeybind => KeyboardKeyCode.T;
+ public override KeyboardKeyCode? DefaultKeybind => KeyboardKeyCode.T;
public override ModifierKey Modifier1 => ModifierKey.Control;
protected override void OnClick()
diff --git a/MiraAPI.Example/Buttons/MeetingButton.cs b/MiraAPI.Example/Buttons/MeetingButton.cs
index 900d8a2c..89e722b0 100644
--- a/MiraAPI.Example/Buttons/MeetingButton.cs
+++ b/MiraAPI.Example/Buttons/MeetingButton.cs
@@ -16,7 +16,7 @@ public class MeetingButton : CustomActionButton
public override int MaxUses => 3;
public override LoadableAsset Sprite => ExampleAssets.ExampleButton;
- public override KeyboardKeyCode DefaultKeybind => KeyboardKeyCode.P;
+ public override KeyboardKeyCode? DefaultKeybind => KeyboardKeyCode.P;
public override ModifierKey Modifier1 => ModifierKey.Control;
public override ModifierKey Modifier2 => ModifierKey.Shift;
diff --git a/MiraAPI.Example/Buttons/NeutralKillerButton.cs b/MiraAPI.Example/Buttons/NeutralKillerButton.cs
index 79fe06bb..58d8beea 100644
--- a/MiraAPI.Example/Buttons/NeutralKillerButton.cs
+++ b/MiraAPI.Example/Buttons/NeutralKillerButton.cs
@@ -13,7 +13,7 @@ public class NeutralKillerButton : CustomActionButton
public override string Name => "Win Game";
public override float Cooldown => 0f;
public override LoadableAsset Sprite => ExampleAssets.ExampleButton;
- public override KeyboardKeyCode DefaultKeybind => KeyboardKeyCode.K;
+ public override KeyboardKeyCode? DefaultKeybind => KeyboardKeyCode.K;
protected override void OnClick()
{
CustomGameOver.Trigger([PlayerControl.LocalPlayer.Data]);
diff --git a/MiraAPI.Example/Buttons/Teleporter/TeleportButton.cs b/MiraAPI.Example/Buttons/Teleporter/TeleportButton.cs
index 55f773be..78beb0b9 100644
--- a/MiraAPI.Example/Buttons/Teleporter/TeleportButton.cs
+++ b/MiraAPI.Example/Buttons/Teleporter/TeleportButton.cs
@@ -22,7 +22,7 @@ public class TeleportButton : CustomActionButton
public override LoadableAsset Sprite => ExampleAssets.TeleportButton;
public override Color TextOutlineColor => new Color32(221, 176, 152, 255);
public static bool IsZoom { get; private set; }
- public override KeyboardKeyCode DefaultKeybind => KeyboardKeyCode.M;
+ public override KeyboardKeyCode? DefaultKeybind => KeyboardKeyCode.M;
public override bool Enabled(RoleBehaviour? role)
{
return role is TeleporterRole;
diff --git a/MiraAPI/Hud/CustomActionButton.cs b/MiraAPI/Hud/CustomActionButton.cs
index 81ac5bd2..4bd8c8ea 100644
--- a/MiraAPI/Hud/CustomActionButton.cs
+++ b/MiraAPI/Hud/CustomActionButton.cs
@@ -54,23 +54,23 @@ public abstract class CustomActionButton
public virtual int MaxUses => 0;
///
- /// Gets or sets the current key binding for this button.
+ /// Gets the current key binding for this button. If null, no keybind will be added.
///
- public virtual KeyboardKeyCode DefaultKeybind { get; } = KeyboardKeyCode.None;
+ public virtual KeyboardKeyCode? DefaultKeybind { get; }
///
- /// The first optional modifier key (e.g., Control, Shift, Alt) that must be held with the main key to activate the button.
+ /// Gets the first optional modifier key (e.g., Control, Shift, Alt) that must be held with the main key to activate the button.
///
public virtual ModifierKey Modifier1 => ModifierKey.None;
///
- /// The second optional modifier key.
+ /// Gets the second optional modifier key.
/// Set to if unused.
///
public virtual ModifierKey Modifier2 => ModifierKey.None;
///
- /// The third optional modifier key.
+ /// Gets the third optional modifier key.
/// Set to if unused.
///
public virtual ModifierKey Modifier3 => ModifierKey.None;
diff --git a/MiraAPI/PluginLoading/MiraPluginManager.cs b/MiraAPI/PluginLoading/MiraPluginManager.cs
index 49adbda9..759a0fd3 100644
--- a/MiraAPI/PluginLoading/MiraPluginManager.cs
+++ b/MiraAPI/PluginLoading/MiraPluginManager.cs
@@ -138,13 +138,13 @@ public sealed class MiraPluginManager
foreach (var button in CustomButtonManager.Buttons)
{
- if (button.DefaultKeybind is KeyboardKeyCode.None)
+ if (button.DefaultKeybind == null)
{
continue;
}
- KeybindManager.Register($"{button.Name}_Keybind", $"Keybind for {button.Name}", button.DefaultKeybind, button.ClickHandler, modifier1: button.Modifier1, modifier2: button.Modifier2, modifier3: button.Modifier3);
+ KeybindManager.Register($"{button.Name}_Keybind", $"Keybind for {button.Name}", (KeyboardKeyCode)button.DefaultKeybind, button.ClickHandler, modifier1: button.Modifier1, modifier2: button.Modifier2, modifier3: button.Modifier3);
- Logger.Info($"Registered keybind for button '{button.GetType().Name}' with default key {button.DefaultKeybind}.");
+ Logger.Info($"Registered keybind for button '{button.GetType().Name}' with default key {(KeyboardKeyCode)button.DefaultKeybind}.");
}
};
}
From f006deab3faada833693f5db3669fc5831d4be4f Mon Sep 17 00:00:00 2001
From: Atony <75191600+AtonyGit@users.noreply.github.com>
Date: Sun, 24 Aug 2025 18:33:52 -0500
Subject: [PATCH 5/8] Fix Send Immediately warning from Reactor
---
MiraAPI/Modifiers/ModifierExtensions.cs | 2 +-
MiraAPI/Networking/CustomMurderRpc.cs | 2 +-
MiraAPI/Voting/VotingUtils.cs | 4 ++--
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/MiraAPI/Modifiers/ModifierExtensions.cs b/MiraAPI/Modifiers/ModifierExtensions.cs
index 309a7d21..9c6f7416 100644
--- a/MiraAPI/Modifiers/ModifierExtensions.cs
+++ b/MiraAPI/Modifiers/ModifierExtensions.cs
@@ -63,7 +63,7 @@ public static class ModifierExtensions
///
/// The player to remove the modifier from.
/// The unique ID of the modifier.
- [MethodRpc((uint)MiraRpc.RemoveModifier, SendImmediately = true)]
+ [MethodRpc((uint)MiraRpc.RemoveModifier)]
public static void RpcRemoveModifier(this PlayerControl target, Guid uniqueId)
{
target.RemoveModifier(uniqueId);
diff --git a/MiraAPI/Networking/CustomMurderRpc.cs b/MiraAPI/Networking/CustomMurderRpc.cs
index 4a7bc30a..209afcd0 100644
--- a/MiraAPI/Networking/CustomMurderRpc.cs
+++ b/MiraAPI/Networking/CustomMurderRpc.cs
@@ -29,7 +29,7 @@ public static class CustomMurderRpc
/// Should the killer be snapped to the dead player.
/// Should the kill animation be shown.
/// Should the kill sound be played.
- [MethodRpc((uint)MiraRpc.CustomMurder, LocalHandling = RpcLocalHandling.Before, SendImmediately = true)]
+ [MethodRpc((uint)MiraRpc.CustomMurder, LocalHandling = RpcLocalHandling.Before)]
public static void RpcCustomMurder(
this PlayerControl source,
PlayerControl target,
diff --git a/MiraAPI/Voting/VotingUtils.cs b/MiraAPI/Voting/VotingUtils.cs
index 159b5762..38fbf166 100644
--- a/MiraAPI/Voting/VotingUtils.cs
+++ b/MiraAPI/Voting/VotingUtils.cs
@@ -71,7 +71,7 @@ public static class VotingUtils
/// The player who is sending the RPC. Should be the host.
/// The player who voted.
/// The player who the voter voted for.
- [MethodRpc((uint)MiraRpc.RemoveVote, SendImmediately = true)]
+ [MethodRpc((uint)MiraRpc.RemoveVote)]
public static void RpcRemoveVote(PlayerControl source, byte voterId, byte votedFor)
{
if (!source.IsHost()) return;
@@ -107,7 +107,7 @@ public static class VotingUtils
/// The player who sent this RPC.
/// The id of the player who casted the vote.
/// The voted player's id.
- [MethodRpc((uint)MiraRpc.CastVote, SendImmediately = true)]
+ [MethodRpc((uint)MiraRpc.CastVote)]
public static void RpcCastVote(PlayerControl source, byte srcPlayerId, byte suspectPlayerId)
{
CustomCastVote(srcPlayerId, suspectPlayerId);
From 85a0275eeac01309c9f2bbb6282940a7fdfab091 Mon Sep 17 00:00:00 2001
From: Atony <75191600+AtonyGit@users.noreply.github.com>
Date: Sun, 24 Aug 2025 19:48:43 -0500
Subject: [PATCH 6/8] Add IntroRoleRevealEvent
Also needs to be changed for the android branch
---
.../Vanilla/Gameplay/IntroRoleRevealEvent.cs | 21 +++++++++++++++++++
MiraAPI/Patches/IntroCutscenePatches.cs | 10 +++++++++
2 files changed, 31 insertions(+)
create mode 100644 MiraAPI/Events/Vanilla/Gameplay/IntroRoleRevealEvent.cs
diff --git a/MiraAPI/Events/Vanilla/Gameplay/IntroRoleRevealEvent.cs b/MiraAPI/Events/Vanilla/Gameplay/IntroRoleRevealEvent.cs
new file mode 100644
index 00000000..809ce116
--- /dev/null
+++ b/MiraAPI/Events/Vanilla/Gameplay/IntroRoleRevealEvent.cs
@@ -0,0 +1,21 @@
+namespace MiraAPI.Events.Vanilla.Gameplay;
+
+///
+/// The event that is invoked when the player's role is shown on the intro cutscene. Non cancelable.
+///
+public class IntroRoleRevealEvent : MiraEvent
+{
+ ///
+ /// Gets the instance of the IntroCutscene.
+ ///
+ public IntroCutscene IntroCutscene { get; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The intro cutscene.
+ public IntroRoleRevealEvent(IntroCutscene introCutscene)
+ {
+ IntroCutscene = introCutscene;
+ }
+}
diff --git a/MiraAPI/Patches/IntroCutscenePatches.cs b/MiraAPI/Patches/IntroCutscenePatches.cs
index af7ce8c4..41217e6e 100644
--- a/MiraAPI/Patches/IntroCutscenePatches.cs
+++ b/MiraAPI/Patches/IntroCutscenePatches.cs
@@ -29,6 +29,16 @@ public static class IntroCutscenePatches
MiraEventManager.InvokeEvent(@event);
}
+ [HarmonyPostfix]
+ [HarmonyPatch(typeof(IntroCutscene._ShowRole_d__41), nameof(IntroCutscene._ShowRole_d__41.MoveNext))]
+ [HarmonyPriority(Priority.Last)]
+ public static void ShowRolePatch(IntroCutscene._ShowRole_d__41 __instance)
+ {
+ var realInstance = __instance.__4__this;
+ var @event = new IntroRoleRevealEvent(realInstance);
+ MiraEventManager.InvokeEvent(@event);
+ }
+
[HarmonyPrefix]
[HarmonyPatch(nameof(IntroCutscene.BeginImpostor))]
[HarmonyPatch(nameof(IntroCutscene.BeginCrewmate))]
From e708c23f81133781520a5937bcb617b146c0c0d5 Mon Sep 17 00:00:00 2001
From: ang-xd
Date: Thu, 28 Aug 2025 11:26:45 -0400
Subject: [PATCH 7/8] Fix checks and positions for keybinds
---
MiraAPI/Hud/CustomActionButton.cs | 3 ++-
MiraAPI/Patches/HudManagerPatches.cs | 3 ---
2 files changed, 2 insertions(+), 4 deletions(-)
diff --git a/MiraAPI/Hud/CustomActionButton.cs b/MiraAPI/Hud/CustomActionButton.cs
index 29a76b35..1400f4cb 100644
--- a/MiraAPI/Hud/CustomActionButton.cs
+++ b/MiraAPI/Hud/CustomActionButton.cs
@@ -205,7 +205,8 @@ public abstract class CustomActionButton
KeybindIcon.transform.GetChild(0).GetComponent().text =
KeybindManager.GetEntries().First(x => x.Id == $"{Name}_Keybind").Key.ToString();
KeybindIcon.name = "KeybindIcon";
- KeybindIcon.transform.localPosition = new(0.341f, 0.374f, -0.1f);
+ KeybindIcon.transform.localPosition = new(0.5f, 0.5f, -0.1f);
+ Button.usesRemainingSprite.transform.localPosition = new(-0.341f, 0.5f, -0.1f);
}
///
diff --git a/MiraAPI/Patches/HudManagerPatches.cs b/MiraAPI/Patches/HudManagerPatches.cs
index acb44826..ce244dbd 100644
--- a/MiraAPI/Patches/HudManagerPatches.cs
+++ b/MiraAPI/Patches/HudManagerPatches.cs
@@ -132,9 +132,6 @@ public static class HudManagerPatches
[HarmonyPostfix]
public static void StartPostfix()
{
- if (AmongUsClient.Instance.GameState != InnerNet.InnerNetClient.GameStates.Started) return;
- if (PlayerControl.LocalPlayer.Data.IsDead) return;
-
foreach (var entry in KeybindManager.GetEntries())
{
var player = ReInput.players.GetPlayer(0);
From 2237d3a2dfbd41909f3f29b4a34b416b37c0520a Mon Sep 17 00:00:00 2001
From: ang-xd
Date: Thu, 28 Aug 2025 12:33:19 -0400
Subject: [PATCH 8/8] Additional changes to keybind icons
---
.../Buttons/Freezer/FreezeButton.cs | 2 --
MiraAPI/Hud/CustomActionButton.cs | 36 ++++++++++++++-----
MiraAPI/Hud/CustomButtonManager.cs | 1 +
MiraAPI/Patches/HudManagerPatches.cs | 30 ++++++++++++++++
MiraAPI/Utilities/Extensions.cs | 2 +-
MiraAPI/Utilities/Helpers.cs | 22 ++++++++++++
6 files changed, 82 insertions(+), 11 deletions(-)
diff --git a/MiraAPI.Example/Buttons/Freezer/FreezeButton.cs b/MiraAPI.Example/Buttons/Freezer/FreezeButton.cs
index 1f10c6f5..726c16ff 100644
--- a/MiraAPI.Example/Buttons/Freezer/FreezeButton.cs
+++ b/MiraAPI.Example/Buttons/Freezer/FreezeButton.cs
@@ -17,8 +17,6 @@ public class FreezeButton : CustomActionButton
public override float Cooldown => OptionGroupSingleton.Instance.FreezeDuration;
- public override int MaxUses => (int)OptionGroupSingleton.Instance.FreezeUses;
-
public override LoadableAsset Sprite => ExampleAssets.ExampleButton;
public override KeyboardKeyCode? DefaultKeybind => KeyboardKeyCode.T;
public override ModifierKey Modifier1 => ModifierKey.Control;
diff --git a/MiraAPI/Hud/CustomActionButton.cs b/MiraAPI/Hud/CustomActionButton.cs
index 1400f4cb..ffaa315b 100644
--- a/MiraAPI/Hud/CustomActionButton.cs
+++ b/MiraAPI/Hud/CustomActionButton.cs
@@ -126,7 +126,17 @@ public abstract class CustomActionButton
///
/// Gets the gameObject used for the keybind icon.
///
- public GameObject KeybindIcon { get; set; }
+ public GameObject? KeybindIcon { get; private set; }
+
+ ///
+ /// Gets or sets the keybind entry for the button.
+ ///
+ private KeybindManager.KeybindEntry? KeybindEntry { get; set; }
+
+ ///
+ /// Gets or sets the keybind icon text.
+ ///
+ private TextMeshPro? KeybindText { get; set; }
///
/// The method used to create the button.
@@ -200,13 +210,18 @@ public abstract class CustomActionButton
}
}));
- KeybindIcon = Object.Instantiate(Button.usesRemainingSprite.gameObject, Button.transform);
- KeybindIcon.GetComponent().sprite = MiraAssets.KeybindButton.LoadAsset();
- KeybindIcon.transform.GetChild(0).GetComponent().text =
- KeybindManager.GetEntries().First(x => x.Id == $"{Name}_Keybind").Key.ToString();
- KeybindIcon.name = "KeybindIcon";
- KeybindIcon.transform.localPosition = new(0.5f, 0.5f, -0.1f);
- Button.usesRemainingSprite.transform.localPosition = new(-0.341f, 0.5f, -0.1f);
+ if (DefaultKeybind != KeyboardKeyCode.None)
+ {
+ KeybindEntry = KeybindManager.GetEntries().First(x => x.Id == $"{Name}_Keybind");
+ KeybindIcon =
+ Helpers.CreateKeybindIcon(
+ Button.gameObject,
+ KeybindEntry.Key,
+ new Vector3(MaxUses <= 0 ? -0.4f : 0.4f, 0.45f, -9f)
+ );
+ KeybindText = KeybindIcon.transform.GetChild(0).GetComponent();
+ Button.usesRemainingSprite.transform.localPosition = new(-0.341f, 0.45f, -0.1f);
+ }
}
///
@@ -465,6 +480,11 @@ public abstract class CustomActionButton
/// The local PlayerControl.
public virtual void FixedUpdateHandler(PlayerControl playerControl)
{
+ if (KeybindText != null && KeybindEntry != null)
+ {
+ KeybindText.text = KeybindEntry.Key.ToString();
+ }
+
if (Timer >= 0)
{
if (!TimerPaused)
diff --git a/MiraAPI/Hud/CustomButtonManager.cs b/MiraAPI/Hud/CustomButtonManager.cs
index e576595e..8e57c400 100644
--- a/MiraAPI/Hud/CustomButtonManager.cs
+++ b/MiraAPI/Hud/CustomButtonManager.cs
@@ -4,6 +4,7 @@ using System.Collections.ObjectModel;
using System.Reflection;
using MiraAPI.Events.Mira;
using MiraAPI.PluginLoading;
+using UnityEngine;
namespace MiraAPI.Hud;
diff --git a/MiraAPI/Patches/HudManagerPatches.cs b/MiraAPI/Patches/HudManagerPatches.cs
index ce244dbd..fa848dec 100644
--- a/MiraAPI/Patches/HudManagerPatches.cs
+++ b/MiraAPI/Patches/HudManagerPatches.cs
@@ -1,11 +1,14 @@
using System;
using System.Collections;
+using System.Collections.Generic;
using HarmonyLib;
using MiraAPI.Hud;
using MiraAPI.Keybinds;
+using MiraAPI.Utilities;
using Reactor.Utilities;
using Reactor.Utilities.Extensions;
using Rewired;
+using TMPro;
using UnityEngine;
using Object = UnityEngine.Object;
@@ -22,6 +25,8 @@ public static class HudManagerPatches
public static Transform? BottomRight { get; private set; }
public static Transform? Buttons { get; private set; }
+ private static Dictionary VanillaKeybindIcons = new();
+
/*
///
/// Trigger hudstart on current custom gamemode
@@ -96,6 +101,26 @@ public static class HudManagerPatches
gridArrange.ArrangeChilds();
aspectPosition.AdjustPosition();
+
+ var keybindIconPos = new Vector3(-0.4f, 0.4f, -9f);
+ var vanillaButtons = new Dictionary
+ {
+ { __instance.KillButton.gameObject, 8 },
+ { __instance.UseButton.gameObject, 6 },
+ { __instance.ReportButton.gameObject, 7 },
+ { __instance.ImpostorVentButton.gameObject, 50 },
+ { __instance.SabotageButton.gameObject, 4 },
+ };
+
+ foreach (var kvp in vanillaButtons)
+ {
+ var buttonObj = kvp.Key;
+ var actionId = kvp.Value;
+
+ var key = Helpers.GetKeybindByActionId(actionId);
+ var icon = Helpers.CreateKeybindIcon(buttonObj, key, keybindIconPos);
+ VanillaKeybindIcons.Add(icon.transform.GetChild(0).GetComponent(), actionId);
+ }
}
///
@@ -132,6 +157,11 @@ public static class HudManagerPatches
[HarmonyPostfix]
public static void StartPostfix()
{
+ foreach (var btnIcon in VanillaKeybindIcons)
+ {
+ btnIcon.Key.text = Helpers.GetKeybindByActionId(btnIcon.Value).ToString();
+ }
+
foreach (var entry in KeybindManager.GetEntries())
{
var player = ReInput.players.GetPlayer(0);
diff --git a/MiraAPI/Utilities/Extensions.cs b/MiraAPI/Utilities/Extensions.cs
index 863df730..e5f991f0 100644
--- a/MiraAPI/Utilities/Extensions.cs
+++ b/MiraAPI/Utilities/Extensions.cs
@@ -94,7 +94,7 @@ public static class Extensions
/// The current timer value.
/// The maximum timer value.
/// The format string to use for the timer text.
- public static void SetCooldownFormat(this ActionButton button, float timer, float maxTimer, string format = "0")
+ public static void SetCooldownFormat(this ActionButton? button, float timer, float maxTimer, string format = "0")
{
var num = Mathf.Clamp(timer / maxTimer, 0f, 1f);
button.isCoolingDown = num > 0f;
diff --git a/MiraAPI/Utilities/Helpers.cs b/MiraAPI/Utilities/Helpers.cs
index 2bf7b37a..6c0bf4cd 100644
--- a/MiraAPI/Utilities/Helpers.cs
+++ b/MiraAPI/Utilities/Helpers.cs
@@ -5,6 +5,7 @@ using System.Globalization;
using System.Linq;
using Il2CppSystem.IO;
using MiraAPI.Keybinds;
+using MiraAPI.Utilities.Assets;
using Rewired;
using TMPro;
using UnityEngine;
@@ -25,6 +26,27 @@ public static class Helpers
{
return [.. GameData.Instance.AllPlayers.ToArray().Where(x => !x.IsDead && !x.Disconnected && x.Object).Select(x => x.Object)];
}
+ internal static GameObject CreateKeybindIcon(GameObject button, KeyboardKeyCode keyCode, Vector3 localPos)
+ {
+ var keybindIcon = Object.Instantiate(HudManager.Instance.AbilityButton.usesRemainingSprite.gameObject, button.transform);
+ keybindIcon.GetComponent().sprite = MiraAssets.KeybindButton.LoadAsset();
+ keybindIcon.transform.GetChild(0).GetComponent().text = keyCode.ToString();
+ keybindIcon.name = "KeybindIcon";
+ keybindIcon.transform.localPosition = localPos;
+ return keybindIcon;
+ }
+
+ ///
+ /// Gets the keybind for an action with ReInput.
+ ///
+ /// The action ID.
+ /// The keyboard key code.
+ public static KeyboardKeyCode GetKeybindByActionId(int actionId)
+ {
+ var player = ReInput.players.GetPlayer(0);
+ return player.controllers.maps.GetFirstElementMapWithAction(ControllerType.Keyboard, actionId, false)
+ .keyboardKeyCode;
+ }
///
/// Determines whether a given probability check succeeds.