diff options
Diffstat (limited to 'src/SMAPI/Framework')
23 files changed, 1860 insertions, 848 deletions
diff --git a/src/SMAPI/Framework/ContentCore.cs b/src/SMAPI/Framework/ContentCore.cs index 9da18481..6e915f28 100644 --- a/src/SMAPI/Framework/ContentCore.cs +++ b/src/SMAPI/Framework/ContentCore.cs @@ -47,9 +47,6 @@ namespace StardewModdingAPI.Framework /// <summary>A lookup which indicates whether the asset is localisable (i.e. the filename contains the locale), if previously loaded.</summary> private readonly IDictionary<string, bool> IsLocalisableLookup; - /// <summary>The locale codes used in asset keys indexed by enum value.</summary> - private readonly IDictionary<LocalizedContentManager.LanguageCode, string> Locales; - /// <summary>The language enum values indexed by locale code.</summary> private readonly IDictionary<string, LocalizedContentManager.LanguageCode> LanguageCodes; @@ -94,21 +91,19 @@ namespace StardewModdingAPI.Framework /// <param name="serviceProvider">The service provider to use to locate services.</param> /// <param name="rootDirectory">The root directory to search for content.</param> /// <param name="currentCulture">The current culture for which to localise content.</param> - /// <param name="languageCodeOverride">The current language code for which to localise content.</param> /// <param name="monitor">Encapsulates monitoring and logging.</param> /// <param name="reflection">Simplifies access to private code.</param> - public ContentCore(IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, string languageCodeOverride, IMonitor monitor, Reflector reflection) + public ContentCore(IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, IMonitor monitor, Reflector reflection) { // init this.Monitor = monitor ?? throw new ArgumentNullException(nameof(monitor)); - this.Content = new LocalizedContentManager(serviceProvider, rootDirectory, currentCulture, languageCodeOverride); + this.Content = new LocalizedContentManager(serviceProvider, rootDirectory, currentCulture); this.Cache = new ContentCache(this.Content, reflection); this.ModContentPrefix = this.GetAssetNameFromFilePath(Constants.ModPath); // get asset data this.CoreAssets = new CoreAssetPropagator(this.NormaliseAssetName, reflection); - this.Locales = this.GetKeyLocales(reflection); - this.LanguageCodes = this.Locales.ToDictionary(p => p.Value, p => p.Key, StringComparer.InvariantCultureIgnoreCase); + this.LanguageCodes = this.GetKeyLocales().ToDictionary(p => p.Value, p => p.Key, StringComparer.InvariantCultureIgnoreCase); this.IsLocalisableLookup = reflection.GetField<IDictionary<string, bool>>(this.Content, "_localizedAsset").GetValue(); } @@ -117,7 +112,7 @@ namespace StardewModdingAPI.Framework /// <param name="rootDirectory">The root directory to search for content (or <c>null</c>. for the default)</param> public ContentManagerShim CreateContentManager(string name, string rootDirectory = null) { - return new ContentManagerShim(this, name, this.Content.ServiceProvider, rootDirectory ?? this.Content.RootDirectory, this.Content.CurrentCulture, this.Content.LanguageCodeOverride); + return new ContentManagerShim(this, name, this.Content.ServiceProvider, rootDirectory ?? this.Content.RootDirectory, this.Content.CurrentCulture); } /**** @@ -177,7 +172,7 @@ namespace StardewModdingAPI.Framework /// <param name="language">The language.</param> public string GetLocale(LocalizedContentManager.LanguageCode language) { - return this.Locales[language]; + return this.Content.LanguageCodeString(language); } /// <summary>Get whether the content manager has already loaded and cached the given asset.</summary> @@ -413,31 +408,14 @@ namespace StardewModdingAPI.Framework } /// <summary>Get the locale codes (like <c>ja-JP</c>) used in asset keys.</summary> - /// <param name="reflection">Simplifies access to private game code.</param> - private IDictionary<LocalizedContentManager.LanguageCode, string> GetKeyLocales(Reflector reflection) + private IDictionary<LocalizedContentManager.LanguageCode, string> GetKeyLocales() { - string previousOverride = this.Content.LanguageCodeOverride; - - try - { - // temporarily disable language override - this.Content.LanguageCodeOverride = null; - - // create locale => code map - IReflectedMethod languageCodeString = reflection.GetMethod(this.Content, "languageCodeString"); - IDictionary<LocalizedContentManager.LanguageCode, string> map = new Dictionary<LocalizedContentManager.LanguageCode, string>(); - foreach (LocalizedContentManager.LanguageCode code in Enum.GetValues(typeof(LocalizedContentManager.LanguageCode))) - { - map[code] = languageCodeString.Invoke<string>(code); - } + // create locale => code map + IDictionary<LocalizedContentManager.LanguageCode, string> map = new Dictionary<LocalizedContentManager.LanguageCode, string>(); + foreach (LocalizedContentManager.LanguageCode code in Enum.GetValues(typeof(LocalizedContentManager.LanguageCode))) + map[code] = this.Content.LanguageCodeString(code); - return map; - } - finally - { - // restore previous settings - this.Content.LanguageCodeOverride = previousOverride; - } + return map; } /// <summary>Get the asset name from a cache key.</summary> @@ -482,11 +460,15 @@ namespace StardewModdingAPI.Framework /// <param name="normalisedAssetName">The normalised asset name.</param> private bool IsNormalisedKeyLoaded(string normalisedAssetName) { + // default English + if (this.Language == LocalizedContentManager.LanguageCode.en) + return this.Cache.ContainsKey(normalisedAssetName); + + // translated if (!this.IsLocalisableLookup.TryGetValue(normalisedAssetName, out bool localisable)) return false; - return localisable - ? this.Cache.ContainsKey($"{normalisedAssetName}.{this.Locales[this.Content.GetCurrentLanguage()]}") + ? this.Cache.ContainsKey($"{normalisedAssetName}.{this.GetLocale(this.Content.GetCurrentLanguage())}") : this.Cache.ContainsKey(normalisedAssetName); } diff --git a/src/SMAPI/Framework/ContentManagerShim.cs b/src/SMAPI/Framework/ContentManagerShim.cs index 2791eb78..66754fd7 100644 --- a/src/SMAPI/Framework/ContentManagerShim.cs +++ b/src/SMAPI/Framework/ContentManagerShim.cs @@ -30,9 +30,8 @@ namespace StardewModdingAPI.Framework /// <param name="serviceProvider">The service provider to use to locate services.</param> /// <param name="rootDirectory">The root directory to search for content.</param> /// <param name="currentCulture">The current culture for which to localise content.</param> - /// <param name="languageCodeOverride">The current language code for which to localise content.</param> - public ContentManagerShim(ContentCore contentCore, string name, IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, string languageCodeOverride) - : base(serviceProvider, rootDirectory, currentCulture, languageCodeOverride) + public ContentManagerShim(ContentCore contentCore, string name, IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture) + : base(serviceProvider, rootDirectory, currentCulture) { this.ContentCore = contentCore; this.Name = name; diff --git a/src/SMAPI/Framework/IModMetadata.cs b/src/SMAPI/Framework/IModMetadata.cs index d1e8eb7d..b7972fe1 100644 --- a/src/SMAPI/Framework/IModMetadata.cs +++ b/src/SMAPI/Framework/IModMetadata.cs @@ -64,5 +64,11 @@ namespace StardewModdingAPI.Framework /// <summary>Set the mod-provided API instance.</summary> /// <param name="api">The mod-provided API.</param> IModMetadata SetApi(object api); + + /// <summary>Whether the mod manifest was loaded (regardless of whether the mod itself was loaded).</summary> + bool HasManifest(); + + /// <summary>Whether the mod has at least one update key set.</summary> + bool HasUpdateKeys(); } } diff --git a/src/SMAPI/Framework/Input/GamePadStateBuilder.cs b/src/SMAPI/Framework/Input/GamePadStateBuilder.cs new file mode 100644 index 00000000..5eeb7ef6 --- /dev/null +++ b/src/SMAPI/Framework/Input/GamePadStateBuilder.cs @@ -0,0 +1,153 @@ +using System.Collections.Generic; +using System.Linq; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Input; + +namespace StardewModdingAPI.Framework.Input +{ + /// <summary>An abstraction for manipulating controller state.</summary> + internal class GamePadStateBuilder + { + /********* + ** Properties + *********/ + /// <summary>The current button states.</summary> + private readonly IDictionary<SButton, ButtonState> ButtonStates; + + /// <summary>The left trigger value.</summary> + private float LeftTrigger; + + /// <summary>The right trigger value.</summary> + private float RightTrigger; + + /// <summary>The left thumbstick position.</summary> + private Vector2 LeftStickPos; + + /// <summary>The left thumbstick position.</summary> + private Vector2 RightStickPos; + + + /********* + ** Public methods + *********/ + /// <summary>Construct an instance.</summary> + /// <param name="state">The initial controller state.</param> + public GamePadStateBuilder(GamePadState state) + { + this.ButtonStates = new Dictionary<SButton, ButtonState> + { + [SButton.DPadUp] = state.DPad.Up, + [SButton.DPadDown] = state.DPad.Down, + [SButton.DPadLeft] = state.DPad.Left, + [SButton.DPadRight] = state.DPad.Right, + + [SButton.ControllerA] = state.Buttons.A, + [SButton.ControllerB] = state.Buttons.B, + [SButton.ControllerX] = state.Buttons.X, + [SButton.ControllerY] = state.Buttons.Y, + [SButton.LeftStick] = state.Buttons.LeftStick, + [SButton.RightStick] = state.Buttons.RightStick, + [SButton.LeftShoulder] = state.Buttons.LeftShoulder, + [SButton.RightShoulder] = state.Buttons.RightShoulder, + [SButton.ControllerBack] = state.Buttons.Back, + [SButton.ControllerStart] = state.Buttons.Start, + [SButton.BigButton] = state.Buttons.BigButton + }; + this.LeftTrigger = state.Triggers.Left; + this.RightTrigger = state.Triggers.Right; + this.LeftStickPos = state.ThumbSticks.Left; + this.RightStickPos = state.ThumbSticks.Right; + } + + /// <summary>Mark all matching buttons unpressed.</summary> + /// <param name="buttons">The buttons.</param> + public void SuppressButtons(IEnumerable<SButton> buttons) + { + foreach (SButton button in buttons) + this.SuppressButton(button); + } + + /// <summary>Mark a button unpressed.</summary> + /// <param name="button">The button.</param> + public void SuppressButton(SButton button) + { + switch (button) + { + // left thumbstick + case SButton.LeftThumbstickUp: + if (this.LeftStickPos.Y > 0) + this.LeftStickPos.Y = 0; + break; + case SButton.LeftThumbstickDown: + if (this.LeftStickPos.Y < 0) + this.LeftStickPos.Y = 0; + break; + case SButton.LeftThumbstickLeft: + if (this.LeftStickPos.X < 0) + this.LeftStickPos.X = 0; + break; + case SButton.LeftThumbstickRight: + if (this.LeftStickPos.X > 0) + this.LeftStickPos.X = 0; + break; + + // right thumbstick + case SButton.RightThumbstickUp: + if (this.RightStickPos.Y > 0) + this.RightStickPos.Y = 0; + break; + case SButton.RightThumbstickDown: + if (this.RightStickPos.Y < 0) + this.RightStickPos.Y = 0; + break; + case SButton.RightThumbstickLeft: + if (this.RightStickPos.X < 0) + this.RightStickPos.X = 0; + break; + case SButton.RightThumbstickRight: + if (this.RightStickPos.X > 0) + this.RightStickPos.X = 0; + break; + + // triggers + case SButton.LeftTrigger: + this.LeftTrigger = 0; + break; + case SButton.RightTrigger: + this.RightTrigger = 0; + break; + + // buttons + default: + if (this.ButtonStates.ContainsKey(button)) + this.ButtonStates[button] = ButtonState.Released; + break; + } + } + + /// <summary>Construct an equivalent gamepad state.</summary> + public GamePadState ToGamePadState() + { + return new GamePadState( + leftThumbStick: this.LeftStickPos, + rightThumbStick: this.RightStickPos, + leftTrigger: this.LeftTrigger, + rightTrigger: this.RightTrigger, + buttons: this.GetPressedButtons().ToArray() + ); + } + + /********* + ** Private methods + *********/ + /// <summary>Get all pressed buttons.</summary> + private IEnumerable<Buttons> GetPressedButtons() + { + foreach (var pair in this.ButtonStates) + { + if (pair.Value == ButtonState.Pressed && pair.Key.TryGetController(out Buttons button)) + yield return button; + } + } + } +} diff --git a/src/SMAPI/Framework/Input/InputState.cs b/src/SMAPI/Framework/Input/InputState.cs deleted file mode 100644 index 8b0108ae..00000000 --- a/src/SMAPI/Framework/Input/InputState.cs +++ /dev/null @@ -1,163 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Input; -using StardewValley; - -namespace StardewModdingAPI.Framework.Input -{ - /// <summary>A summary of input changes during an update frame.</summary> - internal class InputState - { - /********* - ** Accessors - *********/ - /// <summary>The underlying controller state.</summary> - public GamePadState ControllerState { get; } - - /// <summary>The underlying keyboard state.</summary> - public KeyboardState KeyboardState { get; } - - /// <summary>The underlying mouse state.</summary> - public MouseState MouseState { get; } - - /// <summary>The mouse position on the screen adjusted for the zoom level.</summary> - public Point MousePosition { get; } - - /// <summary>The buttons which were pressed, held, or released.</summary> - public IDictionary<SButton, InputStatus> ActiveButtons { get; } = new Dictionary<SButton, InputStatus>(); - - - /********* - ** Public methods - *********/ - /// <summary>Construct an empty instance.</summary> - public InputState() { } - - /// <summary>Construct an instance.</summary> - /// <param name="previousState">The previous input state.</param> - /// <param name="controllerState">The current controller state.</param> - /// <param name="keyboardState">The current keyboard state.</param> - /// <param name="mouseState">The current mouse state.</param> - public InputState(InputState previousState, GamePadState controllerState, KeyboardState keyboardState, MouseState mouseState) - { - // init properties - this.ControllerState = controllerState; - this.KeyboardState = keyboardState; - this.MouseState = mouseState; - this.MousePosition = new Point((int)(mouseState.X * (1.0 / Game1.options.zoomLevel)), (int)(mouseState.Y * (1.0 / Game1.options.zoomLevel))); // derived from Game1::getMouseX - - // get button states - SButton[] down = InputState.GetPressedButtons(keyboardState, mouseState, controllerState).ToArray(); - foreach (SButton button in down) - this.ActiveButtons[button] = this.GetStatus(previousState.GetStatus(button), isDown: true); - foreach (KeyValuePair<SButton, InputStatus> prev in previousState.ActiveButtons) - { - if (prev.Value.IsDown() && !this.ActiveButtons.ContainsKey(prev.Key)) - this.ActiveButtons[prev.Key] = InputStatus.Released; - } - } - - /// <summary>Get the status of a button.</summary> - /// <param name="button">The button to check.</param> - public InputStatus GetStatus(SButton button) - { - return this.ActiveButtons.TryGetValue(button, out InputStatus status) ? status : InputStatus.None; - } - - /// <summary>Get whether a given button was pressed or held.</summary> - /// <param name="button">The button to check.</param> - public bool IsDown(SButton button) - { - return this.GetStatus(button).IsDown(); - } - - /// <summary>Get the current input state.</summary> - /// <param name="previousState">The previous input state.</param> - public static InputState GetState(InputState previousState) - { - GamePadState controllerState = GamePad.GetState(PlayerIndex.One); - KeyboardState keyboardState = Keyboard.GetState(); - MouseState mouseState = Mouse.GetState(); - - return new InputState(previousState, controllerState, keyboardState, mouseState); - } - - /********* - ** Private methods - *********/ - /// <summary>Get the status of a button.</summary> - /// <param name="oldStatus">The previous button status.</param> - /// <param name="isDown">Whether the button is currently down.</param> - public InputStatus GetStatus(InputStatus oldStatus, bool isDown) - { - if (isDown && oldStatus.IsDown()) - return InputStatus.Held; - if (isDown) - return InputStatus.Pressed; - return InputStatus.Released; - } - - /// <summary>Get the buttons pressed in the given stats.</summary> - /// <param name="keyboard">The keyboard state.</param> - /// <param name="mouse">The mouse state.</param> - /// <param name="controller">The controller state.</param> - private static IEnumerable<SButton> GetPressedButtons(KeyboardState keyboard, MouseState mouse, GamePadState controller) - { - // keyboard - foreach (Keys key in keyboard.GetPressedKeys()) - yield return key.ToSButton(); - - // mouse - if (mouse.LeftButton == ButtonState.Pressed) - yield return SButton.MouseLeft; - if (mouse.RightButton == ButtonState.Pressed) - yield return SButton.MouseRight; - if (mouse.MiddleButton == ButtonState.Pressed) - yield return SButton.MouseMiddle; - if (mouse.XButton1 == ButtonState.Pressed) - yield return SButton.MouseX1; - if (mouse.XButton2 == ButtonState.Pressed) - yield return SButton.MouseX2; - - // controller - if (controller.IsConnected) - { - if (controller.Buttons.A == ButtonState.Pressed) - yield return SButton.ControllerA; - if (controller.Buttons.B == ButtonState.Pressed) - yield return SButton.ControllerB; - if (controller.Buttons.Back == ButtonState.Pressed) - yield return SButton.ControllerBack; - if (controller.Buttons.BigButton == ButtonState.Pressed) - yield return SButton.BigButton; - if (controller.Buttons.LeftShoulder == ButtonState.Pressed) - yield return SButton.LeftShoulder; - if (controller.Buttons.LeftStick == ButtonState.Pressed) - yield return SButton.LeftStick; - if (controller.Buttons.RightShoulder == ButtonState.Pressed) - yield return SButton.RightShoulder; - if (controller.Buttons.RightStick == ButtonState.Pressed) - yield return SButton.RightStick; - if (controller.Buttons.Start == ButtonState.Pressed) - yield return SButton.ControllerStart; - if (controller.Buttons.X == ButtonState.Pressed) - yield return SButton.ControllerX; - if (controller.Buttons.Y == ButtonState.Pressed) - yield return SButton.ControllerY; - if (controller.DPad.Up == ButtonState.Pressed) - yield return SButton.DPadUp; - if (controller.DPad.Down == ButtonState.Pressed) - yield return SButton.DPadDown; - if (controller.DPad.Left == ButtonState.Pressed) - yield return SButton.DPadLeft; - if (controller.DPad.Right == ButtonState.Pressed) - yield return SButton.DPadRight; - if (controller.Triggers.Left > 0.2f) - yield return SButton.LeftTrigger; - if (controller.Triggers.Right > 0.2f) - yield return SButton.RightTrigger; - } - } - } -} diff --git a/src/SMAPI/Framework/Input/SInputState.cs b/src/SMAPI/Framework/Input/SInputState.cs new file mode 100644 index 00000000..62defa9f --- /dev/null +++ b/src/SMAPI/Framework/Input/SInputState.cs @@ -0,0 +1,359 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Input; +using StardewValley; + +#pragma warning disable 809 // obsolete override of non-obsolete method (this is deliberate) +namespace StardewModdingAPI.Framework.Input +{ + /// <summary>A summary of input changes during an update frame.</summary> + internal sealed class SInputState : InputState + { + /********* + ** Accessors + *********/ + /// <summary>The maximum amount of direction to ignore for the left thumbstick.</summary> + private const float LeftThumbstickDeadZone = 0.2f; + + + /********* + ** Accessors + *********/ + /// <summary>The controller state as of the last update.</summary> + public GamePadState RealController { get; private set; } + + /// <summary>The keyboard state as of the last update.</summary> + public KeyboardState RealKeyboard { get; private set; } + + /// <summary>The mouse state as of the last update.</summary> + public MouseState RealMouse { get; private set; } + + /// <summary>A derivative of <see cref="RealController"/> which suppresses the buttons in <see cref="SuppressButtons"/>.</summary> + public GamePadState SuppressedController { get; private set; } + + /// <summary>A derivative of <see cref="RealKeyboard"/> which suppresses the buttons in <see cref="SuppressButtons"/>.</summary> + public KeyboardState SuppressedKeyboard { get; private set; } + + /// <summary>A derivative of <see cref="RealMouse"/> which suppresses the buttons in <see cref="SuppressButtons"/>.</summary> + public MouseState SuppressedMouse { get; private set; } + + /// <summary>The mouse position on the screen adjusted for the zoom level.</summary> + public Point MousePosition { get; private set; } + + /// <summary>The buttons which were pressed, held, or released.</summary> + public IDictionary<SButton, InputStatus> ActiveButtons { get; private set; } = new Dictionary<SButton, InputStatus>(); + + /// <summary>The buttons to suppress when the game next handles input. Each button is suppressed until it's released.</summary> + public HashSet<SButton> SuppressButtons { get; } = new HashSet<SButton>(); + + + /********* + ** Public methods + *********/ + /// <summary>Get a copy of the current state.</summary> + public SInputState Clone() + { + return new SInputState + { + ActiveButtons = this.ActiveButtons, + RealController = this.RealController, + RealKeyboard = this.RealKeyboard, + RealMouse = this.RealMouse, + MousePosition = this.MousePosition + }; + } + + /// <summary>This method is called by the game, and does nothing since SMAPI will already have updated by that point.</summary> + [Obsolete("This method should only be called by the game itself.")] + public override void Update() { } + + /// <summary>Update the current button statuses for the given tick.</summary> + public void TrueUpdate() + { + try + { + // get new states + GamePadState realController = GamePad.GetState(PlayerIndex.One); + KeyboardState realKeyboard = Keyboard.GetState(); + MouseState realMouse = Mouse.GetState(); + Point mousePosition = new Point((int)(this.RealMouse.X * (1.0 / Game1.options.zoomLevel)), (int)(this.RealMouse.Y * (1.0 / Game1.options.zoomLevel))); // derived from Game1::getMouseX + var activeButtons = this.DeriveStatuses(this.ActiveButtons, realKeyboard, realMouse, realController); + + // get suppressed states + GamePadState suppressedController = realController; + KeyboardState suppressedKeyboard = realKeyboard; + MouseState suppressedMouse = realMouse; + if (this.SuppressButtons.Count > 0) + this.UpdateSuppression(activeButtons, ref suppressedKeyboard, ref suppressedMouse, ref suppressedController); + + // update + this.ActiveButtons = activeButtons; + this.RealController = realController; + this.RealKeyboard = realKeyboard; + this.RealMouse = realMouse; + this.SuppressedController = suppressedController; + this.SuppressedKeyboard = suppressedKeyboard; + this.SuppressedMouse = suppressedMouse; + this.MousePosition = mousePosition; + } + catch (InvalidOperationException) + { + // GetState() may crash for some players if window doesn't have focus but game1.IsActive == true + } + } + + /// <summary>Get the gamepad state visible to the game.</summary> + [Obsolete("This method should only be called by the game itself.")] + public override GamePadState GetGamePadState() + { + return this.ShouldSuppressNow() + ? this.SuppressedController + : this.RealController; + } + + /// <summary>Get the keyboard state visible to the game.</summary> + [Obsolete("This method should only be called by the game itself.")] + public override KeyboardState GetKeyboardState() + { + return this.ShouldSuppressNow() + ? this.SuppressedKeyboard + : this.RealKeyboard; + } + + /// <summary>Get the keyboard state visible to the game.</summary> + [Obsolete("This method should only be called by the game itself.")] + public override MouseState GetMouseState() + { + return this.ShouldSuppressNow() + ? this.SuppressedMouse + : this.RealMouse; + } + + /// <summary>Get whether a given button was pressed or held.</summary> + /// <param name="button">The button to check.</param> + public bool IsDown(SButton button) + { + return this.GetStatus(this.ActiveButtons, button).IsDown(); + } + + /// <summary>Get whether any of the given buttons were pressed or held.</summary> + /// <param name="buttons">The buttons to check.</param> + public bool IsAnyDown(InputButton[] buttons) + { + return buttons.Any(button => this.IsDown(button.ToSButton())); + } + + /// <summary>Apply input suppression for the given input states.</summary> + /// <param name="activeButtons">The current button states to check.</param> + /// <param name="keyboardState">The game's keyboard state for the current tick.</param> + /// <param name="mouseState">The game's mouse state for the current tick.</param> + /// <param name="gamePadState">The game's controller state for the current tick.</param> + public void UpdateSuppression(IDictionary<SButton, InputStatus> activeButtons, ref KeyboardState keyboardState, ref MouseState mouseState, ref GamePadState gamePadState) + { + // stop suppressing buttons once released + if (this.SuppressButtons.Count != 0) + this.SuppressButtons.RemoveWhere(p => !this.GetStatus(activeButtons, p).IsDown()); + if (this.SuppressButtons.Count == 0) + return; + + // gather info + HashSet<Keys> keyboardButtons = new HashSet<Keys>(); + HashSet<SButton> controllerButtons = new HashSet<SButton>(); + HashSet<SButton> mouseButtons = new HashSet<SButton>(); + foreach (SButton button in this.SuppressButtons) + { + if (button == SButton.MouseLeft || button == SButton.MouseMiddle || button == SButton.MouseRight || button == SButton.MouseX1 || button == SButton.MouseX2) + mouseButtons.Add(button); + else if (button.TryGetKeyboard(out Keys key)) + keyboardButtons.Add(key); + else if (gamePadState.IsConnected && button.TryGetController(out Buttons _)) + controllerButtons.Add(button); + } + + // suppress keyboard keys + if (keyboardState.GetPressedKeys().Any() && keyboardButtons.Any()) + keyboardState = new Keyboar |
