Controller Support

This commit is contained in:
CallOfCreator committed 2026-09-27 16:14:56 +00:00
1 parent bfee7d44a2
commit 041c39d7ff
18 files changed
+516 -476

No files matched your search

+4 -1
View File
@@ -1,4 +1,5 @@
using MiraAPI.Hud;
using MiraAPI.Keybinds;
using MiraAPI.Translation;
using MiraAPI.Utilities.Assets;
using UnityEngine;
@@ -13,6 +14,8 @@ public class ThinkButton : CustomActionButton
public override int MaxUses => 1;
public override ButtonUsesMode UsesMode => ButtonUsesMode.PerRound;
public override LoadableAsset<Sprite> Sprite => ExampleAssets.ExampleButton;
public override MiraKeybind Keybind => MiraGlobalKeybinds.SecondaryAbility;
protected override void OnClick()
{
Button!.OverrideText("button.think.thinking".Translate());
@@ -32,4 +35,4 @@ public class ThinkButton : CustomActionButton
{
return role != null && role.IsImpostor;
}
}
}
+3 -3
View File
@@ -1,4 +1,4 @@
using MiraAPI.Keybinds;
using MiraAPI.Keybinds;
using Rewired;
namespace MiraAPI.Example;
@@ -6,5 +6,5 @@ namespace MiraAPI.Example;
[RegisterCustomKeybinds]
public static class ExampleKeybinds
{
public static MiraKeybind NeutralWinKeybind { get; } = new("Neutral Win", KeyboardKeyCode.C, [ModifierKey.Shift]);
}
public static MiraKeybind NeutralWinKeybind { get; } = new("Neutral Win", KeyboardKeyCode.C, ControllerButton.RightStick, [ModifierKey.Shift]);
}
+50 -122
View File
@@ -139,10 +139,7 @@ public abstract class CustomActionButton
/// <param name="parent">The parent of the button.</param>
public virtual void CreateButton(Transform parent)
{
if (Button)
{
return;
}
if (Button) return;
UsesLeft = MaxUses;
Timer = AmongUsClient.Instance?.NetworkMode == NetworkModes.FreePlay ? 0 : InitialCooldown;
@@ -152,19 +149,15 @@ public abstract class CustomActionButton
Button = Object.Instantiate(HudManager.Instance.AbilityButton, parent);
Button.name = Name + "Button";
Button.OverrideText(Name.Translate().ToUpperInvariant());
Object.Destroy(Button.glyph.GetComponent<SpriteRenderer>());
Object.Destroy(Button.glyph.GetComponent<Image>());
Button.graphic.sprite = Sprite.LoadAsset();
Button.SetUsesRemaining(MaxUses);
if (MaxUses <= 0)
{
Button.SetInfiniteUses();
}
if (MaxUses <= 0) Button.SetInfiniteUses();
if (TextOutlineColor != Color.clear)
{
SetTextOutline(TextOutlineColor);
}
if (TextOutlineColor != Color.clear) SetTextOutline(TextOutlineColor);
var pb = Button.GetComponent<PassiveButton>();
pb.OnClick = new Button.ButtonClickedEvent();
@@ -173,10 +166,7 @@ public abstract class CustomActionButton
// Invoke the generic button click event.
var genericEvent = new MiraButtonClickEvent(this);
MiraEventManager.InvokeEvent(genericEvent);
if (genericEvent.IsCancelled)
{
MiraEventManager.InvokeEvent(new MiraButtonCancelledEvent(this));
}
if (genericEvent.IsCancelled) MiraEventManager.InvokeEvent(new MiraButtonCancelledEvent(this));
// Invoke the button click event for specific button.
var eventType = CustomButtonManager.ButtonEventTypes[GetType()];
@@ -191,68 +181,47 @@ public abstract class CustomActionButton
if (specificInvoked)
{
if (!@event.IsCancelled)
{
ClickHandler();
}
if (!@event.IsCancelled) ClickHandler();
}
else
{
if (!genericEvent.IsCancelled)
{
ClickHandler();
}
if (!genericEvent.IsCancelled) ClickHandler();
}
}));
if (Keybind == null) return;
Keybind.OnActivate(() =>
{
if (!Enabled(PlayerControl.LocalPlayer.Data.Role)) return;
Keybind.RemoveOnActivate(OnKeybind);
Keybind.OnActivate(OnKeybind);
// Invoke the generic button click event.
var genericEvent = new MiraButtonClickEvent(this);
MiraEventManager.InvokeEvent(genericEvent);
if (genericEvent.IsCancelled)
{
MiraEventManager.InvokeEvent(new MiraButtonCancelledEvent(this));
}
// Invoke the button click event for specific button.
var eventType = CustomButtonManager.ButtonEventTypes[GetType()];
var @event = (MiraCancelableEvent)Activator.CreateInstance(eventType, this, genericEvent)!;
var specificInvoked = MiraEventManager.InvokeEvent(@event, eventType);
if (@event.IsCancelled)
{
var cancelEventType = CustomButtonManager.ButtonCancelledEventTypes[GetType()];
var cancelEvent = (MiraEvent)Activator.CreateInstance(cancelEventType, this)!;
MiraEventManager.InvokeEvent(cancelEvent, cancelEventType);
}
if (specificInvoked)
{
if (!@event.IsCancelled)
{
ClickHandler();
}
}
else
{
if (!genericEvent.IsCancelled)
{
ClickHandler();
}
}
});
KeybindIcon =
Helpers.CreateKeybindIcon(
Button.gameObject,
Keybind.CurrentKey,
new Vector3(0.4f, 0.45f, -9f));
KeybindIcon = Helpers.CreateKeybindIcon(Button.gameObject, Keybind.CurrentKey, new Vector3(0.4f, 0.45f, -9f));
KeybindText = KeybindIcon.transform.GetChild(0).GetComponent<TextMeshPro>();
HudManagerPatches.ModdedKeybindIcons.Add(KeybindText);
Button.usesRemainingSprite.transform.localPosition = new(-0.341f, 0.45f, -0.1f);
KeybindText.fontSizeMax = KeybindText.fontSize;
KeybindText.fontSizeMin = KeybindText.fontSize * 0.5f;
KeybindText.enableAutoSizing = true;
Button.usesRemainingSprite.transform.localPosition = new Vector3(-0.341f, 0.45f, -0.1f);
UpdateKeybindIcon();
}
private void OnKeybind()
{
if (Button == null || !Enabled(PlayerControl.LocalPlayer.Data.Role)) return;
Button.GetComponent<PassiveButton>().OnClick.Invoke();
}
internal void UpdateKeybindIcon()
{
if (Keybind == null || KeybindText == null) return;
var text = ActiveInputManager.currentControlType switch
{
ActiveInputManager.InputType.Keyboard when Keybind.CurrentKey != KeyboardKeyCode.None => Keybind.CurrentKey.ToString(),
ActiveInputManager.InputType.Joystick => Keybind.CurrentControllerButton,
_ => string.Empty
};
KeybindText.text = text;
KeybindIcon?.SetActive(text.Length > 0 && LocalSettingsTabSingleton<MiraApiSettings>.Instance.ShowKeybinds.Value);
}
/// <summary>
@@ -262,22 +231,13 @@ public abstract class CustomActionButton
/// <param name="moveButton">Whether the button's position should change in-game.</param>
public virtual void SetButtonLocation(ButtonLocation location, bool moveButton = true)
{
if (!HudManager.InstanceExists || Button == null)
{
return;
}
if (!HudManager.InstanceExists || Button == null) return;
Location = location;
if (!moveButton)
{
return;
}
if (!moveButton) return;
if (HudManagerPatches.BottomLeft == null || HudManagerPatches.BottomRight == null)
{
return;
}
if (HudManagerPatches.BottomLeft == null || HudManagerPatches.BottomRight == null) return;
switch (location)
{
@@ -305,10 +265,7 @@ public abstract class CustomActionButton
public virtual void ResetCooldownAndOrEffect()
{
Timer = Cooldown;
if (EffectActive)
{
OnEffectEnd();
}
if (EffectActive) OnEffectEnd();
EffectActive = false;
}
@@ -328,10 +285,7 @@ public abstract class CustomActionButton
/// <param name="sprite">The new <see cref="UnityEngine.Sprite"/> to override with.</param>
public virtual void OverrideSprite(Sprite sprite)
{
if (Button != null)
{
Button.graphic.sprite = sprite;
}
if (Button != null) Button.graphic.sprite = sprite;
}
/// <summary>
@@ -388,10 +342,7 @@ public abstract class CustomActionButton
UsesLeft = Mathf.Clamp(amount, 0, int.MaxValue);
Button?.SetUsesRemaining(UsesLeft);
if (Button != null)
{
Button.usesRemainingSprite.color = UsesLeft == 0 ? Color.red : Color.white;
}
if (Button != null) Button.usesRemainingSprite.color = UsesLeft == 0 ? Color.red : Color.white;
}
/// <summary>
@@ -469,8 +420,7 @@ public abstract class CustomActionButton
/// <returns>A value that represents whether the button should light up or not.</returns>
public virtual bool CanUse()
{
return PlayerControl.LocalPlayer.moveable &&
((EffectActive && IsEffectCancellable()) || (!EffectActive && (!LimitedUses || UsesLeft > 0)));
return PlayerControl.LocalPlayer.moveable && ((EffectActive && IsEffectCancellable()) || (!EffectActive && (!LimitedUses || UsesLeft > 0)));
}
/// <summary>
@@ -498,10 +448,7 @@ public abstract class CustomActionButton
return;
}
if (!CanClick())
{
return;
}
if (!CanClick()) return;
if (LimitedUses)
{
@@ -530,20 +477,9 @@ public abstract class CustomActionButton
/// <param name="playerControl">The local PlayerControl.</param>
public virtual void FixedUpdateHandler(PlayerControl playerControl)
{
if (Keybind != null && KeybindText != null)
{
KeybindText.text = Keybind.CurrentKey.ToString();
KeybindIcon?.SetActive(ActiveInputManager.currentControlType is ActiveInputManager.InputType.Keyboard &&
LocalSettingsTabSingleton<MiraApiSettings>.Instance.ShowKeybinds.Value &&
Keybind.CurrentKey != KeyboardKeyCode.None);
}
if (Timer >= 0)
{
if (!TimerPaused && (!PauseTimerInVent || !playerControl.inVent))
{
Timer -= Time.deltaTime;
}
if (!TimerPaused && (!PauseTimerInVent || !playerControl.inVent)) Timer -= Time.deltaTime;
}
else if (HasEffect && EffectActive && EffectDuration > 0)
{
@@ -555,13 +491,9 @@ public abstract class CustomActionButton
if (Button)
{
if (CanUse())
{
Button!.SetEnabled();
}
else
{
Button!.SetDisabled();
}
if (EffectActive && EffectDuration > 0)
{
@@ -584,8 +516,7 @@ public abstract class CustomActionButton
/// Custom action button that has a target <typeparamref name="T"/>.
/// </summary>
/// <typeparam name="T">The type of the target object.</typeparam>
public abstract class CustomActionButton<T> : CustomActionButton
where T : MonoBehaviour
public abstract class CustomActionButton<T> : CustomActionButton where T : MonoBehaviour
{
/// <summary>
/// Gets or sets the target <typeparamref name="T"/> of the button.
@@ -623,10 +554,7 @@ public abstract class CustomActionButton<T> : CustomActionButton
public override bool CanUse()
{
var newTarget = GetTarget();
if (newTarget != Target)
{
SetOutline(false);
}
if (newTarget != Target) SetOutline(false);
Target = IsTargetValid(newTarget) ? newTarget : null;
SetOutline(true);
@@ -648,4 +576,4 @@ public abstract class CustomActionButton<T> : CustomActionButton
SetOutline(false);
Target = null;
}
}
}
+7 -2
View File
@@ -1,4 +1,4 @@
using System;
using System;
using Rewired;
namespace MiraAPI.Keybinds;
@@ -23,6 +23,11 @@ public class BaseKeybind
/// </summary>
public KeyboardKeyCode CurrentKey => KeybindUtils.GetKeycodeByKeybind(this);
/// <summary>
/// Gets the display label of the currently assigned button on the active controller.
/// </summary>
public string CurrentControllerButton => KeybindUtils.GetControllerButtonByKeybind(this);
/// <summary>
/// Gets or sets the handler of the keybind. Invoked when the keybind is activated.
/// </summary>
@@ -74,4 +79,4 @@ public class BaseKeybind
Handler -= action;
#pragma warning restore CS8601 // Possible null reference assignment
}
}
}
+87
View File
@@ -0,0 +1,87 @@
namespace MiraAPI.Keybinds;
/// <summary>
/// Controller buttons used for default keybinds.
/// </summary>
public enum ControllerButton
{
/// <summary>
/// The bottom face button (Cross on PlayStation, A on Xbox).
/// </summary>
South = 4,
/// <summary>
/// The right face button (Circle on PlayStation, B on Xbox).
/// </summary>
East = 5,
/// <summary>
/// The left face button (Square on PlayStation, X on Xbox).
/// </summary>
West = 7,
/// <summary>
/// The top face button (Triangle on PlayStation, Y on Xbox).
/// </summary>
North = 8,
/// <summary>
/// The left bumper (L1 on PlayStation, LB on Xbox).
/// </summary>
LeftBumper = 10,
/// <summary>
/// The right bumper (R1 on PlayStation, RB on Xbox).
/// </summary>
RightBumper = 12,
/// <summary>
/// The left trigger (L2 on PlayStation, LT on Xbox).
/// </summary>
LeftTrigger = 11,
/// <summary>
/// The right trigger (R2 on PlayStation, RT on Xbox).
/// </summary>
RightTrigger = 13,
/// <summary>
/// The left stick button (L3 on PlayStation).
/// </summary>
LeftStick = 17,
/// <summary>
/// The right stick button (R3 on PlayStation).
/// </summary>
RightStick = 18,
/// <summary>
/// The back button (Create on PlayStation, View on Xbox).
/// </summary>
Back = 14,
/// <summary>
/// The start button (Options on PlayStation, Menu on Xbox).
/// </summary>
Start = 15,
/// <summary>
/// The D-pad up button.
/// </summary>
DPadUp = 19,
/// <summary>
/// The D-pad right button.
/// </summary>
DPadRight = 20,
/// <summary>
/// The D-pad down button.
/// </summary>
DPadDown = 21,
/// <summary>
/// The D-pad left button.
/// </summary>
DPadLeft = 22,
}
+86 -21
View File
@@ -17,8 +17,7 @@ public static class KeybindManager
/// </summary>
public static List<MiraKeybind> Keybinds { get; } = [];
[HideFromIl2Cpp]
internal static Dictionary<Type, VanillaKeybind> VanillaKeybinds { get; set; } = [];
[HideFromIl2Cpp] internal static Dictionary<Type, VanillaKeybind> VanillaKeybinds { get; set; } = [];
internal static void RewiredInit()
{
@@ -33,12 +32,40 @@ public static class KeybindManager
continue;
}
keybind.RewiredInputAction = instance.userData.RegisterModBind(
keybind.Id,
keybind.Name,
keybind.SourcePluginName,
keybind.DefaultKey,
modifiers: keybind.ModifierKeys);
keybind.RewiredInputAction = instance.userData.RegisterModBind(keybind.Id, keybind.Name, keybind.SourcePluginName, keybind.DefaultKey, modifiers: keybind.ModifierKeys);
if (keybind.DefaultControllerButton == null) continue;
var button = (int)keybind.DefaultControllerButton.Value;
var template = instance.dataFiles.GetJoystickTemplate(GamepadTemplate.typeGuid);
foreach (var map in instance.userData.joystickMaps)
{
if (map.categoryId != 1) continue;
var elementId = button;
var elementType = keybind.DefaultControllerButton is ControllerButton.LeftTrigger or ControllerButton.RightTrigger ? ControllerElementType.Axis : ControllerElementType.Button;
if (map.hardwareGuid != GamepadTemplate.typeGuid)
{
var controller = template?.joysticks.ToArray().FirstOrDefault(x => x.JoystickGuid == map.hardwareGuid);
elementId = controller?.GetJoystickElementId(button) ?? -1;
if (elementId < 0) continue;
var element = instance.dataFiles.GetHardwareJoystickMap(map.hardwareGuid)?.GetElementIdentifier(elementId);
if (element == null) continue;
elementType = element.elementType;
}
map.actionElementMaps.Add(new ActionElementMap
{
_actionId = keybind.RewiredInputAction.id,
_elementIdentifierId = elementId,
_elementType = elementType,
_axisRange = AxisRange.Positive,
_axisContribution = Pole.Positive
});
}
}
}
catch (Exception e)
@@ -50,24 +77,66 @@ public static class KeybindManager
/// <summary>
/// Returns all conflicts where <see cref="MiraKeybind"/>s use the same key.
/// </summary>
/// <returns>A list of pairs of keybinds that conflict.</returns>
/// <returns>The conflicting keybinds grouped by keycode.</returns>
public static Dictionary<KeyboardKeyCode, List<MiraKeybind>> GetConflicts()
{
var conflicts = new Dictionary<KeyboardKeyCode, List<MiraKeybind>>();
foreach (var keybind in Keybinds)
{
var all = GetKeybindsForKey(keybind.CurrentKey, true);
var key = keybind.CurrentKey;
if (key == KeyboardKeyCode.None) continue;
var all = GetKeybindsForKey(key, true);
if (all.Length > 1)
{
if (!conflicts.TryGetValue(keybind.CurrentKey, out var group))
if (!conflicts.TryGetValue(key, out var group))
{
conflicts.Add(keybind.CurrentKey, []);
group = conflicts[keybind.CurrentKey];
group = [];
conflicts.Add(key, group);
}
if (!group.Contains(keybind))
if (!group.Contains(keybind)) group.Add(keybind);
}
}
return conflicts;
}
/// <summary>
/// Returns controller bindings shared by exclusive custom keybinds.
/// </summary>
/// <param name="enabledOnly">Whether to check only active controller maps and available HUD buttons.</param>
/// <returns>The conflicting custom bindings.</returns>
public static List<ActionElementMap> GetControllerConflicts(bool enabledOnly = false)
{
var conflicts = new List<ActionElementMap>();
var actions = Keybinds.Where(x => x.Exclusive && x.RewiredInputAction != null && (!enabledOnly || KeybindUtils.IsControllerActionActive(x.RewiredInputAction.id))).Select(x => x.RewiredInputAction!.id).ToHashSet();
var player = ReInput.players.GetPlayer(0);
for (var i = 0; i < player.controllers.joystickCount; i++)
{
var controller = player.controllers.Joysticks[i];
var maps = new CppCollections.List<ControllerMap>(player.controllers.maps.GetMaps(controller).Cast<CppCollections.IEnumerable<ControllerMap>>()).ToArray();
foreach (var map in maps)
{
if (enabledOnly && !map.enabled) continue;
foreach (var binding in map.GetElementMaps())
{
group.Add(keybind);
if (!binding.enabled || !actions.Contains(binding.actionId)) continue;
foreach (var otherMap in maps)
{
if (enabledOnly && !otherMap.enabled) continue;
if (!enabledOnly && map.categoryId != otherMap.categoryId && map.categoryId != 0 && otherMap.categoryId != 0) continue;
foreach (var other in otherMap.GetElementMaps())
{
if (!other.enabled || binding.actionId == other.actionId || !actions.Contains(other.actionId) || !binding.CheckForAssignmentConflict(other)) continue;
if (!conflicts.Contains(binding)) conflicts.Add(binding);
if (!conflicts.Contains(other)) conflicts.Add(other);
}
}
}
}
}
@@ -79,19 +148,15 @@ public static class KeybindManager
/// Returns all <see cref="MiraKeybind"/>s for a specified keycode.
/// </summary>
/// <param name="keyCode">The key to look for.</param>
/// /// <param name="exclusiveCheck">Should only exclusive keybinds be present.</param>
/// <param name="exclusiveCheck">Whether to return only exclusive keybinds.</param>
/// <returns>The list of <see cref="MiraKeybind"/>s.</returns>
public static MiraKeybind[] GetKeybindsForKey(KeyboardKeyCode keyCode, bool exclusiveCheck = false)
{
var all = new List<MiraKeybind>();
foreach (var keybind in Keybinds)
{
if (keybind.CurrentKey == keyCode && (keybind.Exclusive || !exclusiveCheck))
{
all.Add(keybind);
}
}
return [.. all];
}
}
}
+44 -5
View File
@@ -1,10 +1,12 @@
using System;
using System.Linq;
using MiraAPI.Hud;
using Rewired;
namespace MiraAPI.Keybinds;
/// <summary>
/// Collection of keybind-related utility classes.
/// Collection of keybind-related utilities.
/// </summary>
public static class KeybindUtils
{
@@ -23,6 +25,25 @@ public static class KeybindUtils
return keybind.RewiredInputAction == null ? KeyboardKeyCode.None : GetKeycodeByActionId(keybind.RewiredInputAction.id);
}
/// <summary>
/// Returns the display label for the currently assigned controller button of a <see cref="BaseKeybind"/>.
/// </summary>
/// <param name="keybind">The keybind to get the controller button from.</param>
/// <returns>The button label, or an empty string if no controller binding is available.</returns>
public static string GetControllerButtonByKeybind(BaseKeybind keybind)
{
var map = keybind.RewiredInputAction == null ? null : GetControllerElementMap(keybind.RewiredInputAction.id);
var name = map?.elementIdentifierName ?? string.Empty;
return name.ToLowerInvariant() switch
{
"cross" => "X",
"square" => "□",
"circle" => "○",
"triangle" => "△",
_ => name
};
}
/// <summary>
/// Gets the keycode for an action with <see cref="ReInput"/>.
/// </summary>
@@ -37,23 +58,41 @@ public static class KeybindUtils
/// Gets a <see cref="ActionElementMap"/> by id with <see cref="ReInput"/>.
/// </summary>
/// <param name="actionId">The action ID.</param>
/// <returns>The keyboard keycode.</returns>
/// <returns>The keyboard binding, or null if the action is unassigned.</returns>
public static ActionElementMap? GetActionElementMap(int actionId)
{
var player = ReInput.players.GetPlayer(0);
return player.controllers.maps.GetFirstElementMapWithAction(ControllerType.Keyboard, actionId, false);
}
/// <summary>
/// Gets the binding for an action on the active controller.
/// </summary>
/// <param name="actionId">The action ID.</param>
/// <returns>The controller binding, or null if no active controller or binding is available.</returns>
public static ActionElementMap? GetControllerElementMap(int actionId)
{
var player = ReInput.players.GetPlayer(0);
var controller = player.controllers.GetLastActiveController(ControllerType.Joystick);
return controller == null ? null : player.controllers.maps.GetFirstElementMapWithAction(controller, actionId, false);
}
/// <summary>
/// Gets an <see cref="InputAction"/> by id.
/// </summary>
/// <param name="id">The action identifier name.</param>
/// <returns>The keyboard keycode.</returns>
/// <param name="id">The action ID.</param>
/// <returns>The input action, or null if it is unavailable.</returns>
public static InputAction? GetInputActionById(int id)
{
return RewiredInputManager?.userData?.GetActionById(id);
}
internal static bool IsControllerActionActive(int actionId)
{
var buttons = CustomButtonManager.Buttons.Where(x => x.Keybind?.RewiredInputAction?.id == actionId).ToArray();
return buttons.Length == 0 || buttons.Any(x => x.Button != null && x.Button.isActiveAndEnabled && x.Enabled(PlayerControl.LocalPlayer?.Data?.Role));
}
/// <summary>
/// Finds and returns an unused KeyCode that is not equal to the excluded key.
/// </summary>
@@ -73,4 +112,4 @@ public static class KeybindUtils
return KeyboardKeyCode.None;
}
}
}
+5 -5
View File
@@ -1,4 +1,4 @@
using Rewired;
using Rewired;
namespace MiraAPI.Keybinds;
@@ -12,12 +12,12 @@ public static class MiraGlobalKeybinds
/// <summary>
/// Gets the keybind for primary abilities.
/// </summary>
public static MiraKeybind PrimaryAbility { get; } = new("Primary Ability", KeyboardKeyCode.T);
public static MiraKeybind PrimaryAbility { get; } = new("Primary Ability", KeyboardKeyCode.T, ControllerButton.RightTrigger);
/// <summary>
/// Gets the keybind for secondary abilities.
/// </summary>
public static MiraKeybind SecondaryAbility { get; } = new("Secondary Ability", KeyboardKeyCode.Y);
public static MiraKeybind SecondaryAbility { get; } = new("Secondary Ability", KeyboardKeyCode.Y, ControllerButton.RightBumper);
/// <summary>
/// Gets the keybind for Tertiary abilities.
@@ -27,7 +27,7 @@ public static class MiraGlobalKeybinds
/// <summary>
/// Gets the keybind for primary modifier abilities.
/// </summary>
public static MiraKeybind ModifierPrimaryAbility { get; } = new("Modifier Primary Ability", KeyboardKeyCode.I);
public static MiraKeybind ModifierPrimaryAbility { get; } = new("Modifier Primary Ability", KeyboardKeyCode.I, ControllerButton.LeftTrigger);
/// <summary>
/// Gets the keybind for secondary modifier abilities.
@@ -38,4 +38,4 @@ public static class MiraGlobalKeybinds
/// Gets the keybind for Tertiary modifier abilities.
/// </summary>
public static MiraKeybind ModifierTertiaryAbility { get; } = new("Modifier Tertiary Ability", KeyboardKeyCode.P);
}
}
+22 -7
View File
@@ -1,4 +1,4 @@
using System.Globalization;
using System.Globalization;
using Rewired;
namespace MiraAPI.Keybinds;
@@ -10,11 +10,7 @@ namespace MiraAPI.Keybinds;
/// <param name="defaultKeycode">The default keycode.</param>
/// <param name="modifierKeys">Up to 3 optional <see cref="ModifierKey"/>s.</param>
/// <param name="exclusive">Is exclusive.</param>
public class MiraKeybind(
string name,
KeyboardKeyCode? defaultKeycode,
ModifierKey[]? modifierKeys = null,
bool exclusive = true) : BaseKeybind(name.ToLower(CultureInfo.InvariantCulture).Replace(' ', '_'))
public class MiraKeybind(string name, KeyboardKeyCode? defaultKeycode, ModifierKey[]? modifierKeys = null, bool exclusive = true) : BaseKeybind(name.ToLower(CultureInfo.InvariantCulture).Replace(' ', '_'))
{
/// <summary>
/// Gets name of the keybind.
@@ -26,6 +22,11 @@ public class MiraKeybind(
/// </summary>
public KeyboardKeyCode DefaultKey { get; } = defaultKeycode ?? KeyboardKeyCode.None;
/// <summary>
/// Gets the default controller button. If <see langword="null"/>, the controller binding is left unassigned.
/// </summary>
public ControllerButton? DefaultControllerButton { get; }
/// <summary>
/// Gets the <see cref="ModifierKey"/>s assigned to this keybind.
/// Due to Rewired limitations, there can only be 3 <see cref="ModifierKey"/>s.
@@ -34,14 +35,28 @@ public class MiraKeybind(
/// <summary>
/// Gets a value indicating whether this keybind should be checked for conflicts with other exclusive keybinds using the same key.
/// Active exclusive controller bindings take priority over vanilla actions during gameplay.
/// </summary>
public bool Exclusive { get; } = exclusive;
internal string? SourcePluginName { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="MiraKeybind"/> class with a controller binding.
/// </summary>
/// <param name="name">The name of the keybind.</param>
/// <param name="defaultKeycode">The default keycode.</param>
/// <param name="defaultControllerButton">The default controller button.</param>
/// <param name="modifierKeys">Up to 3 optional <see cref="ModifierKey"/>s for the keyboard binding.</param>
/// <param name="exclusive">Is exclusive.</param>
public MiraKeybind(string name, KeyboardKeyCode? defaultKeycode, ControllerButton defaultControllerButton, ModifierKey[]? modifierKeys = null, bool exclusive = true) : this(name, defaultKeycode, modifierKeys, exclusive)
{
DefaultControllerButton = defaultControllerButton;
}
/// <inheritdoc/>
public override string ToString()
{
return $"{Name} ({Id})";
}
}
}
@@ -1,4 +1,4 @@
using System;
using System;
namespace MiraAPI.Keybinds;
@@ -6,4 +6,4 @@ namespace MiraAPI.Keybinds;
/// Used to mark a class for <see cref="MiraKeybind"/>s registration.
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class RegisterCustomKeybindsAttribute : Attribute;
public class RegisterCustomKeybindsAttribute : Attribute;
+2 -2
View File
@@ -1,4 +1,4 @@
using System.Globalization;
using System.Globalization;
namespace MiraAPI.Keybinds;
@@ -21,4 +21,4 @@ public class VanillaKeybind : BaseKeybind
RewiredInputAction = KeybindUtils.GetInputActionById(id);
Id = RewiredInputAction != null ? RewiredInputAction.name : id.ToString(CultureInfo.InvariantCulture);
}
}
}
+3 -4
View File
@@ -1,4 +1,4 @@
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.CodeAnalysis;
namespace MiraAPI.Keybinds;
@@ -6,8 +6,7 @@ namespace MiraAPI.Keybinds;
/// Singleton for getting a <see cref="VanillaKeybind"/> by <see cref="ActionButton"/>.
/// </summary>
/// <typeparam name="T"><see cref="ActionButton"/> type.</typeparam>
public static class VanillaKeybinding<T>
where T : ActionButton
public static class VanillaKeybinding<T> where T : ActionButton
{
[SuppressMessage("Major Code Smell", "S2743:Static fields should not be used in generic types", Justification = "The instance holds a reference to the button that defines the class's type parameter.")]
private static VanillaKeybind? _instance;
@@ -17,4 +16,4 @@ public static class VanillaKeybinding<T>
/// </summary>
[SuppressMessage("Design", "CA1000:Do not declare static members on generic types", Justification = "This is a utility class to get the instance of a vanilla keybind.")]
public static VanillaKeybind Instance => _instance ??= KeybindManager.VanillaKeybinds[typeof(T)];
}
}
+32 -123
View File
@@ -1,6 +1,6 @@
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using HarmonyLib;
using MiraAPI.Hud;
using MiraAPI.Keybinds;
@@ -28,60 +28,37 @@ public static class HudManagerPatches
private static Dictionary<TextMeshPro, int> vanillaKeybindIcons = [];
[SuppressMessage("Critical Code Smell", "S2223:Non-constant static fields should not be visible", Justification = "This is internal and will never be used by user code.")]
[SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "This is internal and will never be used by user code.")]
internal static List<TextMeshPro> ModdedKeybindIcons = [];
public static IEnumerator CoResizeUI()
{
while (!HudManager.Instance)
{
yield return null;
}
while (!HudManager.Instance) yield return null;
yield return new WaitForSeconds(0.01f);
ResizeUI(LocalSettingsTabSingleton<MiraApiSettings>.Instance.ButtonUIFactorSlider.Value);
}
private static Transform _storedButtonsParent;
public static void ResizeUI(float scaleFactor)
{
if (!_storedButtonsParent)
{
_storedButtonsParent = HudManager.Instance.transform.FindChild("Buttons");
}
if (!_storedButtonsParent) _storedButtonsParent = HudManager.Instance.transform.FindChild("Buttons");
if (_storedButtonsParent)
{
foreach (var aspect in _storedButtonsParent.GetComponentsInChildren<AspectPosition>(true))
{
if (!aspect.gameObject)
{
continue;
}
if (!aspect.gameObject) continue;
if (aspect.gameObject.transform.parent.name == "TopRight")
{
continue;
}
if (aspect.gameObject.transform.parent.name == "TopRight") continue;
if (aspect.gameObject.name.Contains("TopRight"))
{
continue;
}
if (aspect.gameObject.name.Contains("TopRight")) continue;
aspect.gameObject.SetActive(!aspect.isActiveAndEnabled);
aspect.DistanceFromEdge *= new Vector2(scaleFactor, scaleFactor);
aspect.gameObject.SetActive(!aspect.isActiveAndEnabled);
}
}
foreach (var button in HudManager.Instance.GetComponentsInChildren<ActionButton>(true))
{
if (!button.gameObject)
{
continue;
}
if (!button.gameObject) continue;
button.gameObject.SetActive(!button.isActiveAndEnabled);
button.gameObject.transform.localScale *= scaleFactor;
@@ -91,15 +68,9 @@ public static class HudManagerPatches
if (!_storedButtonsParent) return;
foreach (var arrange in _storedButtonsParent.GetComponentsInChildren<GridArrange>(true))
{
if (!arrange.gameObject || !arrange.transform)
{
continue;
}
if (!arrange.gameObject || !arrange.transform) continue;
if (arrange.gameObject.name.Contains("TopRight"))
{
continue;
}
if (arrange.gameObject.name.Contains("TopRight")) continue;
arrange.gameObject.SetActive(!arrange.isActiveAndEnabled);
arrange.CellSize = new Vector2(scaleFactor, scaleFactor);
@@ -143,10 +114,7 @@ public static class HudManagerPatches
[HarmonyPatch(typeof(ChatController), nameof(ChatController.Close))]
public static void TogglePrefix(ChatController __instance)
{
if (!MiraHudHelper.ClonedChatButton)
{
return;
}
if (!MiraHudHelper.ClonedChatButton) return;
__instance.chatButton.transform.localPosition = MiraHudHelper.ClonedChatButton.transform.localPosition + new Vector3(-0.3f, 0);
}
@@ -179,25 +147,13 @@ public static class HudManagerPatches
public static void StartPostfix(HudManager __instance)
{
__instance.gameObject.AddComponent<MiraHudHelper>();
if (Buttons == null)
{
Buttons = __instance.transform.Find("Buttons");
}
if (Buttons == null) Buttons = __instance.transform.Find("Buttons");
if (BottomRight == null)
{
BottomRight = Buttons.Find("BottomRight");
}
if (BottomRight == null) BottomRight = Buttons.Find("BottomRight");
if (BottomLeft == null)
{
BottomLeft = Object.Instantiate(BottomRight.gameObject, Buttons);
}
if (BottomLeft == null) BottomLeft = Object.Instantiate(BottomRight.gameObject, Buttons);
foreach (var t in BottomLeft.GetComponentsInChildren<ActionButton>(true))
{
t.gameObject.Destroy();
}
foreach (var t in BottomLeft.GetComponentsInChildren<ActionButton>(true)) t.gameObject.Destroy();
var gridArrange = BottomLeft.GetComponent<GridArrange>();
var aspectPosition = BottomLeft.GetComponent<AspectPosition>();
@@ -209,10 +165,7 @@ public static class HudManagerPatches
if (Constants.GetPlatformType() is Platforms.Android or Platforms.IPhone)
{
var fakeButton = Object.Instantiate(__instance.AbilityButton, BottomLeft.transform);
foreach (var renderer in fakeButton.GetComponentsInChildren<Renderer>())
{
renderer.enabled = false;
}
foreach (var renderer in fakeButton.GetComponentsInChildren<Renderer>()) renderer.enabled = false;
fakeButton.buttonLabelText.Destroy();
fakeButton.cooldownTimerText.Destroy();
@@ -222,21 +175,16 @@ public static class HudManagerPatches
fakeButton.Destroy();
}
ModdedKeybindIcons = [];
foreach (var button in CustomButtonManager.CustomButtons)
{
var location = button.Location switch
{
ButtonLocation.BottomLeft => BottomLeft.transform,
ButtonLocation.BottomRight => BottomRight,
_ => null,
_ => null
};
if (location is null)
{
continue;
}
if (location is null) continue;
try
{
@@ -264,7 +212,7 @@ public static class HudManagerPatches
{ __instance.ReportButton.gameObject, 7 },
{ __instance.ImpostorVentButton.gameObject, 50 },
{ __instance.SabotageButton.gameObject, 4 },
{ __instance.AbilityButton.gameObject, 49 },
{ __instance.AbilityButton.gameObject, 49 }
};
foreach (var kvp in vanillaButtons)
@@ -272,25 +220,21 @@ public static class HudManagerPatches
var buttonObj = kvp.Key;
var actionId = kvp.Value;
var key = KeybindUtils.GetKeycodeByActionId(actionId);
if (key == KeyboardKeyCode.None)
{
continue;
}
var icon = Helpers.CreateKeybindIcon(buttonObj, key, keybindIconPos);
vanillaKeybindIcons.Add(icon.transform.GetChild(0).GetComponent<TextMeshPro>(), actionId);
if (!MiraApiPlugin.IsMobile)
{
var comp = buttonObj.GetComponent<ActionButton>();
KeybindManager.VanillaKeybinds[comp.GetType()].Button = comp;
}
var key = KeybindUtils.GetKeycodeByActionId(actionId);
if (key == KeyboardKeyCode.None) continue;
var icon = Helpers.CreateKeybindIcon(buttonObj, key, keybindIconPos);
vanillaKeybindIcons.Add(icon.transform.GetChild(0).GetComponent<TextMeshPro>(), actionId);
}
MiraApiSettings.OldButtonScaleFactor =
LocalSettingsTabSingleton<MiraApiSettings>.Instance.ButtonUIFactorSlider.Value;
MiraApiSettings.OldUiButtonScaleFactor =
LocalSettingsTabSingleton<MiraApiSettings>.Instance.TopRightButtonsFactorSlider.Value;
MiraApiSettings.OldButtonScaleFactor = LocalSettingsTabSingleton<MiraApiSettings>.Instance.ButtonUIFactorSlider.Value;
MiraApiSettings.OldUiButtonScaleFactor = LocalSettingsTabSingleton<MiraApiSettings>.Instance.TopRightButtonsFactorSlider.Value;
Coroutines.Start(CoResizeUI());
}
@@ -306,13 +250,9 @@ public static class HudManagerPatches
public static void SetHudActivePostfix(HudManager __instance, PlayerControl localPlayer, RoleBehaviour role, bool isActive)
{
__instance.AdminButton.ToggleVisible(isActive && role.IsImpostor && GameOptionsManager.Instance.CurrentGameOptions.GameMode == AmongUs.GameOptions.GameModes.HideNSeek);
if (localPlayer.Data == null)
{
return;
}
if (localPlayer.Data == null) return;
foreach (var button in CustomButtonManager.CustomButtons)
{
try
{
button.SetActive(isActive, role);
@@ -321,15 +261,13 @@ public static class HudManagerPatches
{
Error($"Failed to set custom button {button.GetType().Name} active: {e}");
}
}
}
[HarmonyPatch(nameof(HudManager.Update))]
[HarmonyPostfix]
public static void UpdatePostfix()
{
var canSeeBinds = ActiveInputManager.currentControlType == ActiveInputManager.InputType.Keyboard &&
LocalSettingsTabSingleton<MiraApiSettings>.Instance.ShowKeybinds.Value;
var canSeeBinds = ActiveInputManager.currentControlType == ActiveInputManager.InputType.Keyboard && LocalSettingsTabSingleton<MiraApiSettings>.Instance.ShowKeybinds.Value;
foreach (var btnIcon in vanillaKeybindIcons)
{
@@ -337,44 +275,15 @@ public static class HudManagerPatches
btnIcon.Key.transform.parent.gameObject.SetActive(canSeeBinds);
}
foreach (var btnIcon in ModdedKeybindIcons)
{
btnIcon.transform.parent.gameObject.SetActive(canSeeBinds);
}
foreach (var button in CustomButtonManager.CustomButtons) button.UpdateKeybindIcon();
var player = ReInput.players.GetPlayer(0);
var keyboard = player.controllers.Keyboard;
foreach (var entry in KeybindManager.Keybinds)
{
var modKeys = entry.ModifierKeys;
bool modifierKeysPressed;
if (modKeys.Length == 0)
{
modifierKeysPressed = true;
}
else
{
modifierKeysPressed = true;
foreach (var key in modKeys)
{
if (keyboard.GetModifierKey(key)) continue;
modifierKeysPressed = false;
break;
}
}
if (player.GetButtonDown(entry.Id) && modifierKeysPressed)
{
if (entry.RewiredInputAction != null && player.GetButtonDown(entry.RewiredInputAction.id))
entry.Invoke();
}
}
foreach (var entry in KeybindManager.VanillaKeybinds.Values)
{
if (player.GetButtonDown(entry.Id))
{
entry.Invoke();
}
}
}
}
}
@@ -1,6 +1,7 @@
using System.Linq;
using System.Linq;
using HarmonyLib;
using MiraAPI.Keybinds;
using Rewired;
using Rewired.UI.ControlMapper;
using UnityEngine;
using UnityEngine.UI;
@@ -10,54 +11,72 @@ namespace MiraAPI.Patches.Keybinds;
[HarmonyPatch(typeof(ControlMapper))]
public static class ControlMapperPatches
{
// Patching this crashes for Epic Games users
/*[HarmonyPrefix]
[HarmonyPatch(nameof(ControlMapper.Start))]
private static void StartPrefix(ControlMapper __instance)
[HarmonyPrefix]
[HarmonyPatch(nameof(ControlMapper.Initialize))]
private static void InitializePrefix(ControlMapper __instance)
{
var doneButton = GameObject.Find("DoneButton")?.GetComponent<CustomButton>();
if (doneButton == null)
{
return;
}
__instance.showControllers = true;
__instance.showGlyphs = false;
}
var toggle = Object.Instantiate(doneButton, doneButton.transform.parent);
var text = toggle.GetComponentInChildren<TextMeshProUGUI>();
var entry = LocalSettingsTabSingleton<MiraApiSettings>.Instance.ShowKeybinds;
toggle.gameObject.name = "KeybindsVisibleToggle";
text.text = $"Show Keybinds: {(entry.Value ? "On" : "Off")}";
toggle.onClick.RemoveAllListeners();
toggle.GetComponent<ButtonInfo>().identifier = "KeybindsVisibleToggle";
toggle.onClick.AddListener((UnityAction)(() =>
{
entry.Value = !entry.Value;
text.text = $"Show Keybinds: {(entry.Value ? "On" : "Off")}";
}));
}*/
[HarmonyPostfix]
[HarmonyPatch(nameof(ControlMapper.CreateLayout))]
private static void CreateLayoutPostfix(ControlMapper __instance)
{
__instance.references.controllerGroup.gameObject.SetActive(false);
__instance.references.assignedControllersGroup.gameObject.SetActive(false);
}
[HarmonyPrefix]
[HarmonyPatch(nameof(ControlMapper.GetControllerMap))]
private static bool GetControllerMapPrefix(ControlMapper __instance, ControllerType type, ref ControllerMap? __result)
{
if (type != ControllerType.Joystick) return true;
__result = __instance.currentPlayer?.controllers.maps.GetFirstMapInCategory(type, __instance.currentJoystickId, 1);
return false;
}
[HarmonyPrefix]
[HarmonyPatch(nameof(ControlMapper.HasElementAssignmentConflicts))]
private static bool HasElementAssignmentConflictsPrefix(ControlMapper.InputMapping mapping, ref bool __result)
{
if (mapping == null || mapping.controllerType != ControllerType.Joystick || !KeybindManager.Keybinds.Any(x => x.RewiredInputAction?.id == mapping.fieldInfo.actionId)) return true;
__result = false;
return false;
}
[HarmonyPrefix]
[HarmonyPatch(nameof(ControlMapper.Update))]
private static void UpdatePrefix(ControlMapper __instance)
{
var conflicts = KeybindManager.GetConflicts();
var controllerConflicts = KeybindManager.GetControllerConflicts();
foreach (var element in __instance.themedElements)
{
var info = element.GetComponent<InputFieldInfo>();
if (info == null)
{
continue;
}
if (info == null) continue;
var key = info.glyphOrText?.actionElementMap?.keyboardKeyCode;
if (key == null)
{
continue;
}
var map = ReInput.mapping.GetActionElementMap(info.actionElementMapId);
var image = element.GetComponent<Image>();
if (image == null) continue;
var conflicts = KeybindManager.GetConflicts();
if (conflicts.Any(x => x.Key.ToString() == key.ToString()))
var hasConflict = map?.controllerMap.controllerType switch
{
element.GetComponent<Image>().color = Color.red;
ControllerType.Keyboard => conflicts.ContainsKey(map.keyboardKeyCode),
ControllerType.Joystick => controllerConflicts.Any(x => x.controllerMap.id == map.controllerMap.id && x.id == map.id),
_ => false
};
if (hasConflict)
{
image.color = Color.red;
}
else if (image.color == Color.red)
{
image.color = Color.white;
element.ApplyTheme();
}
}
}
}
}
+8 -7
View File
@@ -1,3 +1,5 @@
using System;
using System.Collections.Generic;
using HarmonyLib;
using MiraAPI.Keybinds;
using Rewired;
@@ -10,26 +12,25 @@ public static class KeybindMenuPatch
private static bool _registered;
[HarmonyPrefix]
// ReSharper disable once InconsistentNaming
private static void AwakePrefix(InputManager_Base __instance)
{
if (_registered)
{
return;
}
if (_registered) return;
KeybindUtils.RewiredInputManager = __instance;
KeybindManager.RewiredInit();
_registered = true;
// Set these here right after input manager initializes
KeybindManager.VanillaKeybinds = new()
KeybindManager.VanillaKeybinds = new Dictionary<Type, VanillaKeybind>
{
{ typeof(KillButton), new VanillaKeybind(8) },
{ typeof(UseButton), new VanillaKeybind(6) },
{ typeof(ReportButton), new VanillaKeybind(7) },
{ typeof(VentButton), new VanillaKeybind(50) },
{ typeof(SabotageButton), new VanillaKeybind(4) },
{ typeof(AbilityButton), new VanillaKeybind(49) },
{ typeof(AbilityButton), new VanillaKeybind(49) }
};
}
}
}
@@ -1,4 +1,4 @@
using HarmonyLib;
using HarmonyLib;
using MiraAPI.Keybinds;
using UnityEngine;
@@ -10,7 +10,7 @@ public static class OptionsMenuBehaviourPatch
private static ButtonRolloverHandler? _remapRollover;
private static SpriteRenderer? _remapBackground;
private static bool Conflicts => KeybindManager.GetConflicts().Count > 0;
private static bool Conflicts => KeybindManager.GetConflicts().Count > 0 || KeybindManager.GetControllerConflicts().Count > 0;
[HarmonyPostfix]
[HarmonyPatch(nameof(OptionsMenuBehaviour.Open))]
@@ -32,18 +32,14 @@ public static class OptionsMenuBehaviourPatch
[HarmonyPatch(nameof(OptionsMenuBehaviour.Update))]
private static void UpdatePostfix()
{
if (_remapRollover == null)
{
return;
}
if (_remapRollover == null) return;
if (_remapBackground == null)
{
return;
}
if (_remapBackground == null) return;
_remapRollover.OutColor = Conflicts ? Color.red : Color.white;
_remapRollover.UnselectedColor = Conflicts ? Color.red : Color.white;
_remapRollover.OverColor = Conflicts ? new Color32(255, 55, 55, 255) : Palette.AcceptedGreen;
var conflicts = Conflicts;
_remapRollover.OutColor = conflicts ? Color.red : Color.white;
_remapRollover.UnselectedColor = conflicts ? Color.red : Color.white;
_remapRollover.OverColor = conflicts ? new Color32(255, 55, 55, 255) : Palette.AcceptedGreen;
if (!conflicts && _remapBackground.color == Color.red) _remapBackground.color = Color.white;
}
}
}
@@ -0,0 +1,64 @@
using System.Linq;
using HarmonyLib;
using MiraAPI.Keybinds;
using Rewired;
namespace MiraAPI.Patches.Keybinds;
[HarmonyPatch(typeof(Player))]
public static class PlayerKeybindPatches
{
[HarmonyPostfix]
[HarmonyPatch(nameof(Player.GetButtonDown), typeof(int))]
private static void GetButtonPostfix(Player __instance, int __0, ref bool __result)
{
if (!__result || __instance.id != 0 || PlayerControl.LocalPlayer == null || ConsoleJoystick.inputState != ConsoleJoystick.ConsoleInputState.Gameplay || ControllerManager.Instance.IsUiControllerActive) return;
var keybind = KeybindManager.Keybinds.FirstOrDefault(x => x.RewiredInputAction?.id == __0);
if (keybind is { Exclusive: false }) return;
var sources = new CppCollections.List<InputActionSourceData>(__instance.GetCurrentInputSources(__0).Cast<CppCollections.IEnumerable<InputActionSourceData>>()).ToArray();
if (sources.Length == 0 || sources.Any(x => x.controllerType != ControllerType.Joystick)) return;
foreach (var source in sources)
{
var claimed = false;
foreach (var custom in KeybindManager.Keybinds)
{
if (custom == keybind) break;
if (!custom.Exclusive || custom.RewiredInputAction == null || !KeybindUtils.IsControllerActionActive(custom.RewiredInputAction.id)) continue;
var inputs = new CppCollections.List<InputActionSourceData>(__instance.GetCurrentInputSources(custom.RewiredInputAction.id).Cast<CppCollections.IEnumerable<InputActionSourceData>>()).ToArray();
if (!inputs.Any(x => x.controllerType == ControllerType.Joystick && x.controller.id == source.controller.id && x.actionElementMap.CheckForAssignmentConflict(source.actionElementMap))) continue;
claimed = true;
break;
}
if (!claimed) return;
}
__result = false;
}
[HarmonyPostfix]
[HarmonyPatch(nameof(Player.GetButton), typeof(int))]
private static void GetButtonHeldPostfix(Player __instance, int __0, ref bool __result)
{
GetButtonPostfix(__instance, __0, ref __result);
}
[HarmonyPostfix]
[HarmonyPatch(nameof(Player.GetButtonDown), typeof(string))]
private static void GetButtonByNamePostfix(Player __instance, string __0, ref bool __result)
{
if (__result) GetButtonPostfix(__instance, ReInput.mapping.GetActionId(__0), ref __result);
}
[HarmonyPostfix]
[HarmonyPatch(nameof(Player.GetButton), typeof(string))]
private static void GetButtonHeldByNamePostfix(Player __instance, string __0, ref bool __result)
{
if (__result) GetButtonPostfix(__instance, ReInput.mapping.GetActionId(__0), ref __result);
}
}
+32 -122
View File
@@ -43,9 +43,7 @@ public static class Extensions
chance = 0;
}
return new NetData(
RoleId.Get(role),
BitConverter.GetBytes(count.Value).AddRangeToArray(BitConverter.GetBytes(chance.Value)));
return new NetData(RoleId.Get(role), BitConverter.GetBytes(count.Value).AddRangeToArray(BitConverter.GetBytes(chance.Value)));
}
/// <summary>
@@ -60,20 +58,11 @@ public static class Extensions
return;
}
if (self.CloseSound && Constants.ShouldPlaySfx())
{
SoundManager.Instance.PlaySound(self.CloseSound, false);
}
if (self.CloseSound && Constants.ShouldPlaySfx()) SoundManager.Instance.PlaySound(self.CloseSound, false);
if (PlayerControl.LocalPlayer.Data.Role.TeamType == RoleTeamTypes.Crewmate)
{
GameManager.Instance.LogicMinigame.OnMinigameClose();
}
if (PlayerControl.LocalPlayer.Data.Role.TeamType == RoleTeamTypes.Crewmate) GameManager.Instance.LogicMinigame.OnMinigameClose();
if (PlayerControl.LocalPlayer)
{
PlayerControl.HideCursorTemporarily();
}
if (PlayerControl.LocalPlayer) PlayerControl.HideCursorTemporarily();
self.amClosing = Minigame.CloseState.Closing;
self.logger.Info(string.Concat("Closing minigame ", self.GetType().Name));
@@ -145,9 +134,7 @@ public static class Extensions
{
var list = input.ToList();
return list.Count == 0
? throw new InvalidOperationException("Cannot get random element from an empty collection.")
: list[UnityEngine.Random.Range(0, list.Count)];
return list.Count == 0 ? throw new InvalidOperationException("Cannot get random element from an empty collection.") : list[UnityEngine.Random.Range(0, list.Count)];
}
/// <summary>
@@ -201,10 +188,7 @@ public static class Extensions
// ReSharper disable once InconsistentNaming (Justification: Acronym.)
var il2cppList = new CppCollections.List<T>();
foreach (var item in systemList)
{
il2cppList.Add(item);
}
foreach (var item in systemList) il2cppList.Add(item);
return il2cppList;
}
@@ -273,7 +257,6 @@ public static class Extensions
// Left as is intentionally, did you guys mean to use it in DeepDestroy?
[SuppressMessage("Style", "IDE0051:Remove unused private members", Justification = "Not known until intent it clear.")]
[SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Unclear, but please shut up.")]
#pragma warning disable SA1515 // Single-line comment should be preceded by blank line (Justification: ReSharper suppressions.)
// ReSharper disable once UnusedParameter.Local (Justification: Read above.)
// ReSharper disable once UnusedMember.Local (Justification: Read above.)
@@ -333,10 +316,8 @@ public static class Extensions
try
{
foreach (var mat in renderer.materials)
{
if (mat != null)
UnityEngine.Object.Destroy(mat);
}
}
catch
{
@@ -383,10 +364,7 @@ public static class Extensions
/// <returns>An <see cref="IEnumerable"/> that contains <see cref="GameObject"/>s.</returns>
public static IEnumerable<GameObject> GetAllChildren(this GameObject go)
{
for (var i = 0; i < go.transform.childCount; i++)
{
yield return go.transform.GetChild(i).gameObject;
}
for (var i = 0; i < go.transform.childCount; i++) yield return go.transform.GetChild(i).gameObject;
}
/// <summary>
@@ -396,10 +374,7 @@ public static class Extensions
/// <returns>An <see cref="IEnumerable"/> that contains <see cref="GameObject"/>s.</returns>
public static IEnumerable<GameObject> GetAllChildren(this Transform go)
{
for (var i = 0; i < go.transform.childCount; i++)
{
yield return go.transform.GetChild(i).gameObject;
}
for (var i = 0; i < go.transform.childCount; i++) yield return go.transform.GetChild(i).gameObject;
}
/// <summary>
@@ -410,17 +385,12 @@ public static class Extensions
public static void SetSizeLimit(this SpriteRenderer sprite, float pixelSize)
{
sprite.drawMode = SpriteDrawMode.Sliced;
if (!sprite.sprite)
{
return;
}
if (!sprite.sprite) return;
var spriteWidth = sprite.sprite.rect.width;
var spriteHeight = sprite.sprite.rect.height;
sprite.size = spriteWidth < spriteHeight
? new Vector2(pixelSize * spriteWidth / spriteHeight, pixelSize)
: new Vector2(pixelSize, pixelSize * spriteHeight / spriteWidth);
sprite.size = spriteWidth < spriteHeight ? new Vector2(pixelSize * spriteWidth / spriteHeight, pixelSize) : new Vector2(pixelSize, pixelSize * spriteHeight / spriteWidth);
}
/// <summary>
@@ -430,10 +400,7 @@ public static class Extensions
/// <param name="pixelSize">The scale for the sprite to be adjusted to.</param>
public static void SetSizeLimit(this GameObject spriteObj, float pixelSize)
{
if (!spriteObj.TryGetComponent<SpriteRenderer>(out var sprite))
{
return;
}
if (!spriteObj.TryGetComponent<SpriteRenderer>(out var sprite)) return;
sprite.SetSizeLimit(pixelSize);
}
@@ -450,16 +417,10 @@ public static class Extensions
/// <returns>A <see cref="PlayerVoteData"/> if there is one, <see langword="null"/> otherwise.</returns>
public static PlayerVoteData GetVoteData(this PlayerControl player)
{
if (VoteDataComponents.TryGetValue(player, out var component))
{
return component;
}
if (VoteDataComponents.TryGetValue(player, out var component)) return component;
component = player.GetComponent<PlayerVoteData>();
if (!component)
{
throw new InvalidOperationException("PlayerVoteData is not attached to the player.");
}
if (!component) throw new InvalidOperationException("PlayerVoteData is not attached to the player.");
VoteDataComponents[player] = component;
return component;
@@ -476,7 +437,6 @@ public static class Extensions
tie = true;
var result = new KeyValuePair<byte, int>(byte.MaxValue, int.MinValue);
foreach (var keyValuePair in self)
{
if (keyValuePair.Value > result.Value)
{
result = keyValuePair;
@@ -486,7 +446,6 @@ public static class Extensions
{
tie = true;
}
}
return result;
}
@@ -502,7 +461,6 @@ public static class Extensions
tie = true;
var result = new KeyValuePair<byte, float>(byte.MaxValue, int.MinValue);
foreach (var keyValuePair in self)
{
if (keyValuePair.Value > result.Value)
{
result = keyValuePair;
@@ -512,7 +470,6 @@ public static class Extensions
{
tie = true;
}
}
return result;
}
@@ -525,12 +482,7 @@ public static class Extensions
/// <returns>The best constructor.</returns>
public static ConstructorInfo? GetBestConstructor(this Type type, params object[] args)
{
return type.GetValidConstructors(args)
.OrderBy(
ctor => ctor.GetParameters()
.Select((p, i) => GetInheritanceDistance(args[i].GetType(), p.ParameterType))
.Sum())
.FirstOrDefault();
return type.GetValidConstructors(args).OrderBy(ctor => ctor.GetParameters().Select((p, i) => GetInheritanceDistance(args[i].GetType(), p.ParameterType)).Sum()).FirstOrDefault();
}
/// <summary>
@@ -552,14 +504,11 @@ public static class Extensions
/// <returns>A collection of valid constructors.</returns>
public static IEnumerable<ConstructorInfo> GetValidConstructors(this Type type, params object[] args)
{
return type.GetConstructors().Where(
x =>
{
var parameters = x.GetParameters();
return parameters.Length == args.Length && Array.TrueForAll(
parameters,
t => t.ParameterType.IsInstanceOfType(args[t.Position]));
});
return type.GetConstructors().Where(x =>
{
var parameters = x.GetParameters();
return parameters.Length == args.Length && Array.TrueForAll(parameters, t => t.ParameterType.IsInstanceOfType(args[t.Position]));
});
}
/// <summary>
@@ -571,10 +520,7 @@ public static class Extensions
/// <returns>The distance between the types.</returns>
public static int GetInheritanceDistance(Type from, Type to)
{
if (!from.IsAssignableFrom(to))
{
return int.MaxValue;
}
if (!from.IsAssignableFrom(to)) return int.MaxValue;
var type = from;
var distance = 0;
@@ -615,12 +561,7 @@ public static class Extensions
/// <returns>The darkened <see cref="Color32"/>.</returns>
public static Color32 GetShadowColor(this Color32 color, byte darknessAmount)
{
return
new Color32(
(byte)Mathf.Clamp(color.r - darknessAmount, 0, 255),
(byte)Mathf.Clamp(color.g - darknessAmount, 0, 255),
(byte)Mathf.Clamp(color.b - darknessAmount, 0, 255),
byte.MaxValue);
return new Color32((byte)Mathf.Clamp(color.r - darknessAmount, 0, 255), (byte)Mathf.Clamp(color.g - darknessAmount, 0, 255), (byte)Mathf.Clamp(color.b - darknessAmount, 0, 255), byte.MaxValue);
}
/// <summary>
@@ -632,9 +573,7 @@ public static class Extensions
/// <returns>A truncated string of maxLength with the attached suffix.</returns>
public static string? Truncate(this string? value, int maxLength, string truncationSuffix = "…")
{
return value?.Length > maxLength
? value[..maxLength] + truncationSuffix
: value;
return value?.Length > maxLength ? value[..maxLength] + truncationSuffix : value;
}
/// <summary>
@@ -670,10 +609,7 @@ public static class Extensions
count += length;
}
if (current.Count > 0)
{
chunks.Enqueue([.. current]);
}
if (current.Count > 0) chunks.Enqueue([.. current]);
return chunks;
}
@@ -685,9 +621,7 @@ public static class Extensions
/// <returns><see langword="true"/> if the <see cref="OptionBehaviour"/> is for a custom options, <see langword="false"/> otherwise.</returns>
public static bool IsCustom(this OptionBehaviour optionBehaviour)
{
return ModdedOptionsManager.ModdedOptions.Values.Any(
opt => opt.OptionBehaviour && opt.OptionBehaviour == optionBehaviour)
|| optionBehaviour.Equals(GameModeOption.OptionBehaviour);
return ModdedOptionsManager.ModdedOptions.Values.Any(opt => opt.OptionBehaviour && opt.OptionBehaviour == optionBehaviour) || optionBehaviour.Equals(GameModeOption.OptionBehaviour);
}
/// <summary>
@@ -740,9 +674,7 @@ public static class Extensions
/// <returns>The <see cref="DeadBody"/> if it is found, or <see langword="null"/> there is none within the radius.</returns>
public static DeadBody? GetNearestDeadBody(this PlayerControl playerControl, float radius)
{
return Helpers
.GetNearestDeadBodies(playerControl.GetTruePosition(), radius, Helpers.CreateFilter(Constants.NotShipMask))
.Find(component => component && !component.Reported);
return Helpers.GetNearestDeadBodies(playerControl.GetTruePosition(), radius, Helpers.CreateFilter(Constants.NotShipMask)).Find(component => component && !component.Reported);
}
/// <summary>
@@ -755,16 +687,9 @@ public static class Extensions
/// <param name="predicate">Optional predicate to test if the <typeparamref name="T"/> is valid.</param>
/// <typeparam name="T">The type of the object.</typeparam>
/// <returns>The <typeparamref name="T"/> if it was found, or <see langword="null"/> if there is none within the radius.</returns>
public static T? GetNearestObjectOfType<T>(
this PlayerControl playerControl,
float radius,
ContactFilter2D filter,
string? colliderTag = null,
Predicate<T>? predicate = null)
where T : Component
public static T? GetNearestObjectOfType<T>(this PlayerControl playerControl, float radius, ContactFilter2D filter, string? colliderTag = null, Predicate<T>? predicate = null) where T : Component
{
return Helpers.GetNearestObjectsOfType<T>(playerControl.GetTruePosition(), radius, filter, colliderTag)
.Find(predicate ?? (component => component));
return Helpers.GetNearestObjectsOfType<T>(playerControl.GetTruePosition(), radius, filter, colliderTag).Find(predicate ?? (component => component));
}
/// <summary>
@@ -777,21 +702,9 @@ public static class Extensions
/// <param name="includeGhosts">Determines if Ghosts are included.</param>
/// <param name="predicate">Optional predicate to test if the <see cref="PlayerControl"/> is valid.</param>
/// <returns>The closest <see cref="PlayerControl"/> if there is one, <see langword="false"/> otherwise.</returns>
public static PlayerControl? GetClosestPlayer(
this PlayerControl playerControl,
bool includeImpostors,
float distance,
bool ignoreColliders = false,
bool includeGhosts = false,
Predicate<PlayerControl>? predicate = null)
public static PlayerControl? GetClosestPlayer(this PlayerControl playerControl, bool includeImpostors, float distance, bool ignoreColliders = false, bool includeGhosts = false, Predicate<PlayerControl>? predicate = null)
{
var filteredPlayers = Helpers.GetClosestPlayers(playerControl, distance, ignoreColliders)
.Where(
playerInfo => !playerInfo.Data.Disconnected &&
playerInfo.PlayerId != playerControl.PlayerId &&
(includeGhosts || !playerInfo.Data.IsDead) &&
(includeImpostors || !playerInfo.Data.Role.IsImpostor))
.ToList();
var filteredPlayers = Helpers.GetClosestPlayers(playerControl, distance, ignoreColliders).Where(playerInfo => !playerInfo.Data.Disconnected && playerInfo.PlayerId != playerControl.PlayerId && (includeGhosts || !playerInfo.Data.IsDead) && (includeImpostors || !playerInfo.Data.Role.IsImpostor)).ToList();
return predicate != null ? filteredPlayers.Find(predicate) : filteredPlayers.FirstOrDefault();
}
@@ -828,9 +741,7 @@ public static class Extensions
var action = userData.GetAction(userData.actions.Count - 1)!;
action.name = id;
action.descriptiveName = group != null
? $"<b><size=70%>{Palette.CrewmateRoleHeaderDarkBlue.ToTextColor()}{group.ReplaceLineEndings(" ")}</color></size></b>\n{name}"
: name;
action.descriptiveName = group != null ? $"<b><size=70%>{Palette.CrewmateRoleHeaderDarkBlue.ToTextColor()}{group.ReplaceLineEndings(" ")}</color></size></b>\n{name}" : name;
action.categoryId = category;
action.type = type;
action.userAssignable = true;
@@ -841,7 +752,7 @@ public static class Extensions
_actionId = action.id,
_elementType = ControllerElementType.Button,
_axisContribution = Pole.Positive,
_keyboardKeyCode = key,
_keyboardKeyCode = key
};
if (modifiers != null)
@@ -852,7 +763,6 @@ public static class Extensions
}
userData.keyboardMaps[0].actionElementMaps.Add(map);
userData.joystickMaps[0].actionElementMaps.Add(map);
return action;
}
}
}