diff --git a/MiraAPI.Example/Buttons/ThinkButton.cs b/MiraAPI.Example/Buttons/ThinkButton.cs index 71013e9..6497784 100644 --- a/MiraAPI.Example/Buttons/ThinkButton.cs +++ b/MiraAPI.Example/Buttons/ThinkButton.cs @@ -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 => ExampleAssets.ExampleButton; + public override MiraKeybind Keybind => MiraGlobalKeybinds.SecondaryAbility; + protected override void OnClick() { Button!.OverrideText("button.think.thinking".Translate()); diff --git a/MiraAPI.Example/ExampleEventHandlers.cs b/MiraAPI.Example/ExampleEventHandlers.cs index 3647899..20c0def 100644 --- a/MiraAPI.Example/ExampleEventHandlers.cs +++ b/MiraAPI.Example/ExampleEventHandlers.cs @@ -1,5 +1,4 @@ -using System.Diagnostics.CodeAnalysis; -using System.Globalization; +using System.Globalization; using System.Linq; using MiraAPI.Events; using MiraAPI.Events.Mira; diff --git a/MiraAPI.Example/ExampleKeybinds.cs b/MiraAPI.Example/ExampleKeybinds.cs index dba6b67..4c8f04f 100644 --- a/MiraAPI.Example/ExampleKeybinds.cs +++ b/MiraAPI.Example/ExampleKeybinds.cs @@ -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]); } diff --git a/MiraAPI/Hud/CustomActionButton.cs b/MiraAPI/Hud/CustomActionButton.cs index abc762e..ce91ad5 100644 --- a/MiraAPI/Hud/CustomActionButton.cs +++ b/MiraAPI/Hud/CustomActionButton.cs @@ -139,10 +139,7 @@ public abstract class CustomActionButton /// The parent of the button. 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()); + Object.Destroy(Button.glyph.GetComponent()); 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(); 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(); - 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().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.Instance.ShowKeybinds.Value); } /// @@ -262,22 +231,13 @@ public abstract class CustomActionButton /// Whether the button's position should change in-game. 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 /// The new to override with. public virtual void OverrideSprite(Sprite sprite) { - if (Button != null) - { - Button.graphic.sprite = sprite; - } + if (Button != null) Button.graphic.sprite = sprite; } /// @@ -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; } /// @@ -469,8 +420,7 @@ public abstract class CustomActionButton /// A value that represents whether the button should light up or not. public virtual bool CanUse() { - return PlayerControl.LocalPlayer.moveable && - ((EffectActive && IsEffectCancellable()) || (!EffectActive && (!LimitedUses || UsesLeft > 0))); + return PlayerControl.LocalPlayer.moveable && ((EffectActive && IsEffectCancellable()) || (!EffectActive && (!LimitedUses || UsesLeft > 0))); } /// @@ -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 /// The local PlayerControl. 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.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) { @@ -623,10 +555,7 @@ public abstract class CustomActionButton : 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); diff --git a/MiraAPI/Keybinds/BaseKeybind.cs b/MiraAPI/Keybinds/BaseKeybind.cs index 95de3c8..9944964 100644 --- a/MiraAPI/Keybinds/BaseKeybind.cs +++ b/MiraAPI/Keybinds/BaseKeybind.cs @@ -1,4 +1,4 @@ -using System; +using System; using Rewired; namespace MiraAPI.Keybinds; @@ -23,6 +23,11 @@ public class BaseKeybind /// public KeyboardKeyCode CurrentKey => KeybindUtils.GetKeycodeByKeybind(this); + /// + /// Gets the display label of the currently assigned button on the active controller. + /// + public string CurrentControllerButton => KeybindUtils.GetControllerButtonByKeybind(this); + /// /// Gets or sets the handler of the keybind. Invoked when the keybind is activated. /// diff --git a/MiraAPI/Keybinds/ControllerButton.cs b/MiraAPI/Keybinds/ControllerButton.cs new file mode 100644 index 0000000..c00eb4b --- /dev/null +++ b/MiraAPI/Keybinds/ControllerButton.cs @@ -0,0 +1,87 @@ +namespace MiraAPI.Keybinds; + +/// +/// Controller buttons used for default keybinds. +/// +public enum ControllerButton +{ + /// + /// The bottom face button (Cross on PlayStation, A on Xbox). + /// + South = 4, + + /// + /// The right face button (Circle on PlayStation, B on Xbox). + /// + East = 5, + + /// + /// The left face button (Square on PlayStation, X on Xbox). + /// + West = 7, + + /// + /// The top face button (Triangle on PlayStation, Y on Xbox). + /// + North = 8, + + /// + /// The left bumper (L1 on PlayStation, LB on Xbox). + /// + LeftBumper = 10, + + /// + /// The right bumper (R1 on PlayStation, RB on Xbox). + /// + RightBumper = 12, + + /// + /// The left trigger (L2 on PlayStation, LT on Xbox). + /// + LeftTrigger = 11, + + /// + /// The right trigger (R2 on PlayStation, RT on Xbox). + /// + RightTrigger = 13, + + /// + /// The left stick button (L3 on PlayStation). + /// + LeftStick = 17, + + /// + /// The right stick button (R3 on PlayStation). + /// + RightStick = 18, + + /// + /// The back button (Create on PlayStation, View on Xbox). + /// + Back = 14, + + /// + /// The start button (Options on PlayStation, Menu on Xbox). + /// + Start = 15, + + /// + /// The D-pad up button. + /// + DPadUp = 19, + + /// + /// The D-pad right button. + /// + DPadRight = 20, + + /// + /// The D-pad down button. + /// + DPadDown = 21, + + /// + /// The D-pad left button. + /// + DPadLeft = 22, +} diff --git a/MiraAPI/Keybinds/KeybindManager.cs b/MiraAPI/Keybinds/KeybindManager.cs index 055d2ca..143863e 100644 --- a/MiraAPI/Keybinds/KeybindManager.cs +++ b/MiraAPI/Keybinds/KeybindManager.cs @@ -33,12 +33,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 +78,66 @@ public static class KeybindManager /// /// Returns all conflicts where s use the same key. /// - /// A list of pairs of keybinds that conflict. + /// The conflicting keybinds grouped by keycode. public static Dictionary> GetConflicts() { var conflicts = new Dictionary>(); 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; + } + + /// + /// Returns controller bindings shared by exclusive custom keybinds. + /// + /// Whether to check only active controller maps and available HUD buttons. + /// The conflicting custom bindings. + public static List GetControllerConflicts(bool enabledOnly = false) + { + var conflicts = new List(); + 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(player.controllers.maps.GetMaps(controller).Cast>()).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,7 +149,7 @@ public static class KeybindManager /// Returns all s for a specified keycode. /// /// The key to look for. - /// /// Should only exclusive keybinds be present. + /// Whether to return only exclusive keybinds. /// The list of s. public static MiraKeybind[] GetKeybindsForKey(KeyboardKeyCode keyCode, bool exclusiveCheck = false) { @@ -87,9 +157,7 @@ public static class KeybindManager foreach (var keybind in Keybinds) { if (keybind.CurrentKey == keyCode && (keybind.Exclusive || !exclusiveCheck)) - { all.Add(keybind); - } } return [.. all]; diff --git a/MiraAPI/Keybinds/KeybindUtils.cs b/MiraAPI/Keybinds/KeybindUtils.cs index 3b8502b..26bcf55 100644 --- a/MiraAPI/Keybinds/KeybindUtils.cs +++ b/MiraAPI/Keybinds/KeybindUtils.cs @@ -1,10 +1,12 @@ using System; +using System.Linq; +using MiraAPI.Hud; using Rewired; namespace MiraAPI.Keybinds; /// -/// Collection of keybind-related utility classes. +/// Collection of keybind-related utilities. /// public static class KeybindUtils { @@ -23,6 +25,25 @@ public static class KeybindUtils return keybind.RewiredInputAction == null ? KeyboardKeyCode.None : GetKeycodeByActionId(keybind.RewiredInputAction.id); } + /// + /// Returns the display label for the currently assigned controller button of a . + /// + /// The keybind to get the controller button from. + /// The button label, or an empty string if no controller binding is available. + 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, + }; + } + /// /// Gets the keycode for an action with . /// @@ -37,23 +58,40 @@ public static class KeybindUtils /// Gets a by id with . /// /// The action ID. - /// The keyboard keycode. + /// The keyboard binding, or null if the action is unassigned. public static ActionElementMap? GetActionElementMap(int actionId) { var player = ReInput.players.GetPlayer(0); return player.controllers.maps.GetFirstElementMapWithAction(ControllerType.Keyboard, actionId, false); } + /// + /// Gets the binding for an action on the active controller. + /// + /// The action ID. + /// The controller binding, or null if no active controller or binding is available. + 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); + } + /// /// Gets an by id. /// - /// The action identifier name. - /// The keyboard keycode. + /// The action ID. + /// The input action, or null if it is unavailable. public static InputAction? GetInputActionById(int id) { return RewiredInputManager?.userData?.GetActionById(id); } + internal static bool IsControllerActionActive(int actionId) + { + return CustomButtonManager.Buttons.Any(x => x.Keybind?.RewiredInputAction?.id == actionId && x.Button != null && x.Button.isActiveAndEnabled && x.Enabled(PlayerControl.LocalPlayer?.Data?.Role)); + } + /// /// Finds and returns an unused KeyCode that is not equal to the excluded key. /// diff --git a/MiraAPI/Keybinds/MiraGlobalKeybinds.cs b/MiraAPI/Keybinds/MiraGlobalKeybinds.cs index 7e4b21b..5ef5380 100644 --- a/MiraAPI/Keybinds/MiraGlobalKeybinds.cs +++ b/MiraAPI/Keybinds/MiraGlobalKeybinds.cs @@ -1,10 +1,11 @@ -using Rewired; +using Rewired; namespace MiraAPI.Keybinds; /// /// Class containing all default s. /// Recommended for cross-mod compatibility. +/// Controller bindings are unassigned by default. /// [RegisterCustomKeybinds] public static class MiraGlobalKeybinds diff --git a/MiraAPI/Keybinds/MiraKeybind.cs b/MiraAPI/Keybinds/MiraKeybind.cs index 5eca155..ef466b5 100644 --- a/MiraAPI/Keybinds/MiraKeybind.cs +++ b/MiraAPI/Keybinds/MiraKeybind.cs @@ -1,4 +1,4 @@ -using System.Globalization; +using System.Globalization; using Rewired; namespace MiraAPI.Keybinds; @@ -10,11 +10,7 @@ namespace MiraAPI.Keybinds; /// The default keycode. /// Up to 3 optional s. /// Is exclusive. -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(' ', '_')) { /// /// Gets name of the keybind. @@ -26,6 +22,11 @@ public class MiraKeybind( /// public KeyboardKeyCode DefaultKey { get; } = defaultKeycode ?? KeyboardKeyCode.None; + /// + /// Gets the default controller button. If , the controller binding is left unassigned. + /// + public ControllerButton? DefaultControllerButton { get; } + /// /// Gets the s assigned to this keybind. /// Due to Rewired limitations, there can only be 3 s. @@ -34,11 +35,26 @@ public class MiraKeybind( /// /// 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. /// public bool Exclusive { get; } = exclusive; internal string? SourcePluginName { get; set; } + /// + /// Initializes a new instance of the class with a controller binding. + /// + /// The name of the keybind. + /// The default keycode. + /// The default controller button. + /// Up to 3 optional s for the keyboard binding. + /// Is exclusive. + public MiraKeybind(string name, KeyboardKeyCode? defaultKeycode, ControllerButton defaultControllerButton, ModifierKey[]? modifierKeys = null, bool exclusive = true) + : this(name, defaultKeycode, modifierKeys, exclusive) + { + DefaultControllerButton = defaultControllerButton; + } + /// public override string ToString() { diff --git a/MiraAPI/Keybinds/RegisterCustomKeybindsAttribute.cs b/MiraAPI/Keybinds/RegisterCustomKeybindsAttribute.cs index 088f158..50f8039 100644 --- a/MiraAPI/Keybinds/RegisterCustomKeybindsAttribute.cs +++ b/MiraAPI/Keybinds/RegisterCustomKeybindsAttribute.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace MiraAPI.Keybinds; diff --git a/MiraAPI/Keybinds/VanillaKeybind.cs b/MiraAPI/Keybinds/VanillaKeybind.cs index a6b7350..01d37b3 100644 --- a/MiraAPI/Keybinds/VanillaKeybind.cs +++ b/MiraAPI/Keybinds/VanillaKeybind.cs @@ -1,4 +1,4 @@ -using System.Globalization; +using System.Globalization; namespace MiraAPI.Keybinds; diff --git a/MiraAPI/Keybinds/VanillaKeybindSingleton.cs b/MiraAPI/Keybinds/VanillaKeybindSingleton.cs index 0754756..d4e3fd1 100644 --- a/MiraAPI/Keybinds/VanillaKeybindSingleton.cs +++ b/MiraAPI/Keybinds/VanillaKeybindSingleton.cs @@ -1,4 +1,4 @@ -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; namespace MiraAPI.Keybinds; diff --git a/MiraAPI/Patches/HudManagerPatches.cs b/MiraAPI/Patches/HudManagerPatches.cs index 5c3038b..ad12c68 100644 --- a/MiraAPI/Patches/HudManagerPatches.cs +++ b/MiraAPI/Patches/HudManagerPatches.cs @@ -1,6 +1,5 @@ using System.Collections; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using HarmonyLib; using MiraAPI.Hud; using MiraAPI.Keybinds; @@ -28,47 +27,29 @@ public static class HudManagerPatches private static Dictionary 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 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.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(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); @@ -78,10 +59,7 @@ public static class HudManagerPatches foreach (var button in HudManager.Instance.GetComponentsInChildren(true)) { - if (!button.gameObject) - { - continue; - } + if (!button.gameObject) continue; button.gameObject.SetActive(!button.isActiveAndEnabled); button.gameObject.transform.localScale *= scaleFactor; @@ -91,15 +69,9 @@ public static class HudManagerPatches if (!_storedButtonsParent) return; foreach (var arrange in _storedButtonsParent.GetComponentsInChildren(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 +115,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 +148,13 @@ public static class HudManagerPatches public static void StartPostfix(HudManager __instance) { __instance.gameObject.AddComponent(); - 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(true)) - { - t.gameObject.Destroy(); - } + foreach (var t in BottomLeft.GetComponentsInChildren(true)) t.gameObject.Destroy(); var gridArrange = BottomLeft.GetComponent(); var aspectPosition = BottomLeft.GetComponent(); @@ -209,10 +166,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.enabled = false; - } + foreach (var renderer in fakeButton.GetComponentsInChildren()) renderer.enabled = false; fakeButton.buttonLabelText.Destroy(); fakeButton.cooldownTimerText.Destroy(); @@ -222,8 +176,6 @@ public static class HudManagerPatches fakeButton.Destroy(); } - ModdedKeybindIcons = []; - foreach (var button in CustomButtonManager.CustomButtons) { var location = button.Location switch @@ -233,10 +185,7 @@ public static class HudManagerPatches _ => null, }; - if (location is null) - { - continue; - } + if (location is null) continue; try { @@ -272,25 +221,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(), actionId); if (!MiraApiPlugin.IsMobile) { var comp = buttonObj.GetComponent(); 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(), actionId); } - MiraApiSettings.OldButtonScaleFactor = - LocalSettingsTabSingleton.Instance.ButtonUIFactorSlider.Value; - MiraApiSettings.OldUiButtonScaleFactor = - LocalSettingsTabSingleton.Instance.TopRightButtonsFactorSlider.Value; + MiraApiSettings.OldButtonScaleFactor = LocalSettingsTabSingleton.Instance.ButtonUIFactorSlider.Value; + MiraApiSettings.OldUiButtonScaleFactor = LocalSettingsTabSingleton.Instance.TopRightButtonsFactorSlider.Value; Coroutines.Start(CoResizeUI()); } @@ -306,10 +251,7 @@ 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) { @@ -328,8 +270,7 @@ public static class HudManagerPatches [HarmonyPostfix] public static void UpdatePostfix() { - var canSeeBinds = ActiveInputManager.currentControlType == ActiveInputManager.InputType.Keyboard && - LocalSettingsTabSingleton.Instance.ShowKeybinds.Value; + var canSeeBinds = ActiveInputManager.currentControlType == ActiveInputManager.InputType.Keyboard && LocalSettingsTabSingleton.Instance.ShowKeybinds.Value; foreach (var btnIcon in vanillaKeybindIcons) { @@ -337,44 +278,19 @@ 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(); - } } } } diff --git a/MiraAPI/Patches/Keybinds/ControlMapperPatches.cs b/MiraAPI/Patches/Keybinds/ControlMapperPatches.cs index 6666a2c..df2179a 100644 --- a/MiraAPI/Patches/Keybinds/ControlMapperPatches.cs +++ b/MiraAPI/Patches/Keybinds/ControlMapperPatches.cs @@ -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,53 +11,71 @@ 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(); - if (doneButton == null) - { - return; - } + __instance.showControllers = true; + __instance.showGlyphs = false; + } - var toggle = Object.Instantiate(doneButton, doneButton.transform.parent); - var text = toggle.GetComponentInChildren(); - var entry = LocalSettingsTabSingleton.Instance.ShowKeybinds; - toggle.gameObject.name = "KeybindsVisibleToggle"; - text.text = $"Show Keybinds: {(entry.Value ? "On" : "Off")}"; - toggle.onClick.RemoveAllListeners(); - toggle.GetComponent().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(); - 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(); + 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().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(); } } } diff --git a/MiraAPI/Patches/Keybinds/KeybindsPatch.cs b/MiraAPI/Patches/Keybinds/KeybindsPatch.cs index 9c1034f..e7acf89 100644 --- a/MiraAPI/Patches/Keybinds/KeybindsPatch.cs +++ b/MiraAPI/Patches/Keybinds/KeybindsPatch.cs @@ -1,3 +1,5 @@ +using System; +using System.Collections.Generic; using HarmonyLib; using MiraAPI.Keybinds; using Rewired; @@ -10,19 +12,18 @@ 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 { { typeof(KillButton), new VanillaKeybind(8) }, { typeof(UseButton), new VanillaKeybind(6) }, diff --git a/MiraAPI/Patches/Keybinds/OptionsMenuBehaviourPatch.cs b/MiraAPI/Patches/Keybinds/OptionsMenuBehaviourPatch.cs index 584d6cc..ddd7a62 100644 --- a/MiraAPI/Patches/Keybinds/OptionsMenuBehaviourPatch.cs +++ b/MiraAPI/Patches/Keybinds/OptionsMenuBehaviourPatch.cs @@ -1,4 +1,5 @@ -using HarmonyLib; +using System.Linq; +using HarmonyLib; using MiraAPI.Keybinds; using UnityEngine; @@ -10,40 +11,42 @@ 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))] - private static void OpenPostfix() + private static void OpenPostfix(OptionsMenuBehaviour __instance) { - _remapRollover = GameObject.Find("Remap Controls")?.GetComponent()!; - try - { - _remapBackground = _remapRollover.transform.FindChild("Background").GetComponent(); - _remapBackground.color = Conflicts ? Color.red : Color.white; - } - catch - { - // ignored, this normally breaks when on mobile since there's no remap button screen. - } + _remapRollover = __instance.GetComponentsInChildren(true).FirstOrDefault(x => x.name == "Remap Controls"); + _remapBackground = _remapRollover?.transform.FindChild("Background")?.GetComponent(); + if (_remapBackground != null) _remapBackground.color = Conflicts ? Color.red : Color.white; + } + + [HarmonyPrefix] + [HarmonyPatch(nameof(OptionsMenuBehaviour.Update))] + private static bool UpdatePrefix(OptionsMenuBehaviour __instance) + { + if (MiraApiPlugin.IsMobile || ActiveInputManager.currentControlType != ActiveInputManager.InputType.Joystick) return true; + + if (Input.GetKeyUp(KeyCode.Escape) && !__instance.KeyboardOptions.activeSelf) __instance.Close(); + + __instance.KeyboardOptions.SetActive(true); + __instance.MouseAndKeyboardOptions.SetActive(true); + return false; } [HarmonyPostfix] [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; } } diff --git a/MiraAPI/Patches/Keybinds/PlayerKeybindPatches.cs b/MiraAPI/Patches/Keybinds/PlayerKeybindPatches.cs new file mode 100644 index 0000000..bc9b8c4 --- /dev/null +++ b/MiraAPI/Patches/Keybinds/PlayerKeybindPatches.cs @@ -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(__instance.GetCurrentInputSources(__0).Cast>()).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(__instance.GetCurrentInputSources(custom.RewiredInputAction.id).Cast>()).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); + } +} diff --git a/MiraAPI/Utilities/Extensions.cs b/MiraAPI/Utilities/Extensions.cs index fd2b591..21112f6 100644 --- a/MiraAPI/Utilities/Extensions.cs +++ b/MiraAPI/Utilities/Extensions.cs @@ -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))); } /// @@ -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)]; } /// @@ -201,10 +188,7 @@ public static class Extensions // ReSharper disable once InconsistentNaming (Justification: Acronym.) var il2cppList = new CppCollections.List(); - 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.) @@ -383,10 +366,7 @@ public static class Extensions /// An that contains s. public static IEnumerable 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; } /// @@ -396,10 +376,7 @@ public static class Extensions /// An that contains s. public static IEnumerable 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; } /// @@ -410,17 +387,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); } /// @@ -430,10 +402,7 @@ public static class Extensions /// The scale for the sprite to be adjusted to. public static void SetSizeLimit(this GameObject spriteObj, float pixelSize) { - if (!spriteObj.TryGetComponent(out var sprite)) - { - return; - } + if (!spriteObj.TryGetComponent(out var sprite)) return; sprite.SetSizeLimit(pixelSize); } @@ -450,16 +419,10 @@ public static class Extensions /// A if there is one, otherwise. 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(); - 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; @@ -525,12 +488,7 @@ public static class Extensions /// The best constructor. 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(); } /// @@ -552,14 +510,11 @@ public static class Extensions /// A collection of valid constructors. public static IEnumerable 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])); + }); } /// @@ -571,10 +526,7 @@ public static class Extensions /// The distance between the types. 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; @@ -648,9 +600,7 @@ public static class Extensions /// A truncated string of maxLength with the attached suffix. 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; } /// @@ -686,10 +636,7 @@ public static class Extensions count += length; } - if (current.Count > 0) - { - chunks.Enqueue([.. current]); - } + if (current.Count > 0) chunks.Enqueue([.. current]); return chunks; } @@ -701,9 +648,7 @@ public static class Extensions /// if the is for a custom options, otherwise. 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); } /// @@ -756,9 +701,7 @@ public static class Extensions /// The if it is found, or there is none within the radius. 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); } /// @@ -771,16 +714,10 @@ public static class Extensions /// Optional predicate to test if the is valid. /// The type of the object. /// The if it was found, or if there is none within the radius. - public static T? GetNearestObjectOfType( - this PlayerControl playerControl, - float radius, - ContactFilter2D filter, - string? colliderTag = null, - Predicate? predicate = null) + public static T? GetNearestObjectOfType(this PlayerControl playerControl, float radius, ContactFilter2D filter, string? colliderTag = null, Predicate? predicate = null) where T : Component { - return Helpers.GetNearestObjectsOfType(playerControl.GetTruePosition(), radius, filter, colliderTag) - .Find(predicate ?? (component => component)); + return Helpers.GetNearestObjectsOfType(playerControl.GetTruePosition(), radius, filter, colliderTag).Find(predicate ?? (component => component)); } /// @@ -793,21 +730,9 @@ public static class Extensions /// Determines if Ghosts are included. /// Optional predicate to test if the is valid. /// The closest if there is one, otherwise. - public static PlayerControl? GetClosestPlayer( - this PlayerControl playerControl, - bool includeImpostors, - float distance, - bool ignoreColliders = false, - bool includeGhosts = false, - Predicate? predicate = null) + public static PlayerControl? GetClosestPlayer(this PlayerControl playerControl, bool includeImpostors, float distance, bool ignoreColliders = false, bool includeGhosts = false, Predicate? 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(); } @@ -844,9 +769,7 @@ public static class Extensions var action = userData.GetAction(userData.actions.Count - 1)!; action.name = id; - action.descriptiveName = group != null - ? $"{Palette.CrewmateRoleHeaderDarkBlue.ToTextColor()}{group.ReplaceLineEndings(" ")}\n{name}" - : name; + action.descriptiveName = group != null ? $"{Palette.CrewmateRoleHeaderDarkBlue.ToTextColor()}{group.ReplaceLineEndings(" ")}\n{name}" : name; action.categoryId = category; action.type = type; action.userAssignable = true; @@ -868,7 +791,6 @@ public static class Extensions } userData.keyboardMaps[0].actionElementMaps.Add(map); - userData.joystickMaps[0].actionElementMaps.Add(map); return action; }