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 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 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.