diff options
Diffstat (limited to 'src/SMAPI/Framework')
-rw-r--r-- | src/SMAPI/Framework/CursorPosition.cs | 7 | ||||
-rw-r--r-- | src/SMAPI/Framework/Input/SInputState.cs | 28 | ||||
-rw-r--r-- | src/SMAPI/Framework/ModHelpers/InputHelper.cs | 54 | ||||
-rw-r--r-- | src/SMAPI/Framework/ModHelpers/ModHelper.cs | 8 | ||||
-rw-r--r-- | src/SMAPI/Framework/SGame.cs | 25 |
5 files changed, 96 insertions, 26 deletions
diff --git a/src/SMAPI/Framework/CursorPosition.cs b/src/SMAPI/Framework/CursorPosition.cs index db02b3d1..6f716746 100644 --- a/src/SMAPI/Framework/CursorPosition.cs +++ b/src/SMAPI/Framework/CursorPosition.cs @@ -8,6 +8,9 @@ namespace StardewModdingAPI.Framework /********* ** Accessors *********/ + /// <summary>The raw pixel position, not adjusted for the game zoom.</summary> + public Vector2 RawPixels { get; } + /// <summary>The pixel position relative to the top-left corner of the visible screen.</summary> public Vector2 ScreenPixels { get; } @@ -22,11 +25,13 @@ namespace StardewModdingAPI.Framework ** Public methods *********/ /// <summary>Construct an instance.</summary> + /// <param name="rawPixels">The raw pixel position, not adjusted for the game zoom.</param> /// <param name="screenPixels">The pixel position relative to the top-left corner of the visible screen.</param> /// <param name="tile">The tile position relative to the top-left corner of the map.</param> /// <param name="grabTile">The tile position that the game considers under the cursor for purposes of clicking actions.</param> - public CursorPosition(Vector2 screenPixels, Vector2 tile, Vector2 grabTile) + public CursorPosition(Vector2 rawPixels, Vector2 screenPixels, Vector2 tile, Vector2 grabTile) { + this.RawPixels = rawPixels; this.ScreenPixels = screenPixels; this.Tile = tile; this.GrabTile = grabTile; diff --git a/src/SMAPI/Framework/Input/SInputState.cs b/src/SMAPI/Framework/Input/SInputState.cs index 27e40ab4..44fd0618 100644 --- a/src/SMAPI/Framework/Input/SInputState.cs +++ b/src/SMAPI/Framework/Input/SInputState.cs @@ -8,7 +8,7 @@ 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> + /// <summary>Manages the game's input state.</summary> internal sealed class SInputState : InputState { /********* @@ -17,6 +17,9 @@ namespace StardewModdingAPI.Framework.Input /// <summary>The maximum amount of direction to ignore for the left thumbstick.</summary> private const float LeftThumbstickDeadZone = 0.2f; + /// <summary>The cursor position on the screen adjusted for the zoom level.</summary> + private CursorPosition CursorPositionImpl; + /********* ** Accessors @@ -39,8 +42,8 @@ namespace StardewModdingAPI.Framework.Input /// <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 cursor position on the screen adjusted for the zoom level.</summary> + public ICursorPosition CursorPosition => this.CursorPositionImpl; /// <summary>The buttons which were pressed, held, or released.</summary> public IDictionary<SButton, InputStatus> ActiveButtons { get; private set; } = new Dictionary<SButton, InputStatus>(); @@ -61,7 +64,7 @@ namespace StardewModdingAPI.Framework.Input RealController = this.RealController, RealKeyboard = this.RealKeyboard, RealMouse = this.RealMouse, - MousePosition = this.MousePosition + CursorPositionImpl = this.CursorPositionImpl }; } @@ -78,15 +81,16 @@ namespace StardewModdingAPI.Framework.Input 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); + Vector2 cursorRawPixelPos = new Vector2(this.RealMouse.X, this.RealMouse.Y); // update real states this.ActiveButtons = activeButtons; this.RealController = realController; this.RealKeyboard = realKeyboard; this.RealMouse = realMouse; - this.MousePosition = mousePosition; + if (this.CursorPositionImpl?.RawPixels != cursorRawPixelPos) + this.CursorPositionImpl = this.GetCursorPosition(cursorRawPixelPos); // update suppressed states this.SuppressButtons.RemoveWhere(p => !this.GetStatus(activeButtons, p).IsDown()); @@ -157,6 +161,18 @@ namespace StardewModdingAPI.Framework.Input /********* ** Private methods *********/ + /// <summary>Get the current cursor position.</summary> + /// <remarks>The raw pixel position from the mouse state.</remarks> + private CursorPosition GetCursorPosition(Vector2 rawPixelPos) + { + Vector2 screenPixels = new Vector2((int)(rawPixelPos.X * (1.0 / Game1.options.zoomLevel)), (int)(rawPixelPos.Y * (1.0 / Game1.options.zoomLevel))); // derived from Game1::getMouseX + Vector2 tile = new Vector2((int)((Game1.viewport.X + screenPixels.X) / Game1.tileSize), (int)((Game1.viewport.Y + screenPixels.Y) / Game1.tileSize)); + Vector2 grabTile = (Game1.mouseCursorTransparency > 0 && Utility.tileWithinRadiusOfPlayer((int)tile.X, (int)tile.Y, 1, Game1.player)) // derived from Game1.pressActionButton + ? tile + : Game1.player.GetGrabTile(); + return new CursorPosition(rawPixelPos, screenPixels, tile, grabTile); + } + /// <summary>Whether input should be suppressed in the current context.</summary> private bool ShouldSuppressNow() { diff --git a/src/SMAPI/Framework/ModHelpers/InputHelper.cs b/src/SMAPI/Framework/ModHelpers/InputHelper.cs new file mode 100644 index 00000000..f4cd12b6 --- /dev/null +++ b/src/SMAPI/Framework/ModHelpers/InputHelper.cs @@ -0,0 +1,54 @@ +using StardewModdingAPI.Framework.Input; + +namespace StardewModdingAPI.Framework.ModHelpers +{ + /// <summary>Provides an API for checking and changing input state.</summary> + internal class InputHelper : BaseHelper, IInputHelper + { + /********* + ** Accessors + *********/ + /// <summary>Manages the game's input state.</summary> + private readonly SInputState InputState; + + + /********* + ** Public methods + *********/ + /// <summary>Construct an instance.</summary> + /// <param name="modID">The unique ID of the relevant mod.</param> + /// <param name="inputState">Manages the game's input state.</param> + public InputHelper(string modID, SInputState inputState) + : base(modID) + { + this.InputState = inputState; + } + + /// <summary>Get the current cursor position.</summary> + public ICursorPosition GetCursorPosition() + { + return this.InputState.CursorPosition; + } + + /// <summary>Get whether a button is currently pressed.</summary> + /// <param name="button">The button.</param> + public bool IsDown(SButton button) + { + return this.InputState.IsDown(button); + } + + /// <summary>Get whether a button is currently suppressed, so the game won't see it.</summary> + /// <param name="button">The button.</param> + public bool IsSuppressed(SButton button) + { + return this.InputState.SuppressButtons.Contains(button); + } + + /// <summary>Prevent the game from handling a button press. This doesn't prevent other mods from receiving the event.</summary> + /// <param name="button">The button to suppress.</param> + public void Suppress(SButton button) + { + this.InputState.SuppressButtons.Add(button); + } + } +} diff --git a/src/SMAPI/Framework/ModHelpers/ModHelper.cs b/src/SMAPI/Framework/ModHelpers/ModHelper.cs index 92cb9d94..1e07dafa 100644 --- a/src/SMAPI/Framework/ModHelpers/ModHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/ModHelper.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using StardewModdingAPI.Events; +using StardewModdingAPI.Framework.Input; using StardewModdingAPI.Framework.Models; using StardewModdingAPI.Framework.Serialisation; using StardewModdingAPI.Framework.Utilities; @@ -40,6 +41,9 @@ namespace StardewModdingAPI.Framework.ModHelpers /// <summary>An API for loading content assets.</summary> public IContentHelper Content { get; } + /// <summary>An API for checking and changing input state.</summary> + public IInputHelper Input { get; } + /// <summary>An API for accessing private game code.</summary> public IReflectionHelper Reflection { get; } @@ -63,6 +67,7 @@ namespace StardewModdingAPI.Framework.ModHelpers /// <param name="modID">The mod's unique ID.</param> /// <param name="modDirectory">The full path to the mod's folder.</param> /// <param name="jsonHelper">Encapsulate SMAPI's JSON parsing.</param> + /// <param name="inputState">Manages the game's input state.</param> /// <param name="events">Manages access to events raised by SMAPI.</param> /// <param name="contentHelper">An API for loading content assets.</param> /// <param name="commandHelper">An API for managing console commands.</param> @@ -75,7 +80,7 @@ namespace StardewModdingAPI.Framework.ModHelpers /// <param name="deprecationManager">Manages deprecation warnings.</param> /// <exception cref="ArgumentNullException">An argument is null or empty.</exception> /// <exception cref="InvalidOperationException">The <paramref name="modDirectory"/> path does not exist on disk.</exception> - public ModHelper(string modID, string modDirectory, JsonHelper jsonHelper, IModEvents events, IContentHelper contentHelper, ICommandHelper commandHelper, IModRegistry modRegistry, IReflectionHelper reflectionHelper, IMultiplayerHelper multiplayer, ITranslationHelper translationHelper, IEnumerable<IContentPack> contentPacks, Func<string, IManifest, IContentPack> createContentPack, DeprecationManager deprecationManager) + public ModHelper(string modID, string modDirectory, JsonHelper jsonHelper, SInputState inputState, IModEvents events, IContentHelper contentHelper, ICommandHelper commandHelper, IModRegistry modRegistry, IReflectionHelper reflectionHelper, IMultiplayerHelper multiplayer, ITranslationHelper translationHelper, IEnumerable<IContentPack> contentPacks, Func<string, IManifest, IContentPack> createContentPack, DeprecationManager deprecationManager) : base(modID) { // validate directory @@ -88,6 +93,7 @@ namespace StardewModdingAPI.Framework.ModHelpers this.DirectoryPath = modDirectory; this.JsonHelper = jsonHelper ?? throw new ArgumentNullException(nameof(jsonHelper)); this.Content = contentHelper ?? throw new ArgumentNullException(nameof(contentHelper)); + this.Input = new InputHelper(modID, inputState); this.ModRegistry = modRegistry ?? throw new ArgumentNullException(nameof(modRegistry)); this.ConsoleCommands = commandHelper ?? throw new ArgumentNullException(nameof(commandHelper)); this.Reflection = reflectionHelper ?? throw new ArgumentNullException(nameof(reflectionHelper)); diff --git a/src/SMAPI/Framework/SGame.cs b/src/SMAPI/Framework/SGame.cs index 18529728..560b54a4 100644 --- a/src/SMAPI/Framework/SGame.cs +++ b/src/SMAPI/Framework/SGame.cs @@ -54,9 +54,6 @@ namespace StardewModdingAPI.Framework /// <summary>Manages SMAPI events for mods.</summary> private readonly EventManager Events; - /// <summary>Manages input visible to the game.</summary> - private SInputState Input => (SInputState)Game1.input; - /// <summary>The maximum number of consecutive attempts SMAPI should make to recover from a draw error.</summary> private readonly Countdown DrawCrashTimer = new Countdown(60); // 60 ticks = roughly one second @@ -101,7 +98,7 @@ namespace StardewModdingAPI.Framework private readonly IValueWatcher<IClickableMenu> ActiveMenuWatcher; /// <summary>Tracks changes to the cursor position.</summary> - private readonly IValueWatcher<Point> CursorWatcher; + private readonly IValueWatcher<Vector2> CursorWatcher; /// <summary>Tracks changes to the mouse wheel scroll.</summary> private readonly IValueWatcher<int> MouseWheelScrollWatcher; @@ -137,6 +134,9 @@ namespace StardewModdingAPI.Framework /// <summary>SMAPI's content manager.</summary> public ContentCoordinator ContentCore { get; private set; } + /// <summary>Manages input visible to the game.</summary> + public SInputState Input => (SInputState)Game1.input; + /// <summary>The game's core multiplayer utility.</summary> public SMultiplayer Multiplayer => (SMultiplayer)Game1.multiplayer; @@ -174,7 +174,7 @@ namespace StardewModdingAPI.Framework // init watchers Game1.locations = new ObservableCollection<GameLocation>(); - this.CursorWatcher = WatcherFactory.ForEquatable(() => this.Input.MousePosition); + this.CursorWatcher = WatcherFactory.ForEquatable(() => this.Input.CursorPosition.ScreenPixels); this.MouseWheelScrollWatcher = WatcherFactory.ForEquatable(() => this.Input.RealMouse.ScrollWheelValue); this.SaveIdWatcher = WatcherFactory.ForEquatable(() => Game1.hasLoadedGame ? Game1.uniqueIDForThisGame : 0); this.WindowSizeWatcher = WatcherFactory.ForEquatable(() => new Point(Game1.viewport.Width, Game1.viewport.Height)); @@ -452,18 +452,7 @@ namespace StardewModdingAPI.Framework bool isChatInput = Game1.IsChatting || (Context.IsMultiplayer && Context.IsWorldReady && Game1.activeClickableMenu == null && Game1.currentMinigame == null && inputState.IsAnyDown(Game1.options.chatButton)); if (!isChatInput) { - // get cursor position - ICursorPosition cursor = this.PreviousCursorPosition; - if (this.CursorWatcher.IsChanged) - { - // cursor position - Vector2 screenPixels = new Vector2(this.CursorWatcher.CurrentValue.X, this.CursorWatcher.CurrentValue.Y); - Vector2 tile = new Vector2((int)((Game1.viewport.X + screenPixels.X) / Game1.tileSize), (int)((Game1.viewport.Y + screenPixels.Y) / Game1.tileSize)); - Vector2 grabTile = (Game1.mouseCursorTransparency > 0 && Utility.tileWithinRadiusOfPlayer((int)tile.X, (int)tile.Y, 1, Game1.player)) // derived from Game1.pressActionButton - ? tile - : Game1.player.GetGrabTile(); - cursor = new CursorPosition(screenPixels, tile, grabTile); - } + ICursorPosition cursor = this.Input.CursorPosition; // raise cursor moved event if (this.CursorWatcher.IsChanged && this.PreviousCursorPosition != null) @@ -533,7 +522,7 @@ namespace StardewModdingAPI.Framework if (inputState.RealKeyboard != previousInputState.RealKeyboard) this.Events.Legacy_Control_KeyboardChanged.Raise(new EventArgsKeyboardStateChanged(previousInputState.RealKeyboard, inputState.RealKeyboard)); if (inputState.RealMouse != previousInputState.RealMouse) - this.Events.Legacy_Control_MouseChanged.Raise(new EventArgsMouseStateChanged(previousInputState.RealMouse, inputState.RealMouse, previousInputState.MousePosition, inputState.MousePosition)); + this.Events.Legacy_Control_MouseChanged.Raise(new EventArgsMouseStateChanged(previousInputState.RealMouse, inputState.RealMouse, new Point((int)previousInputState.CursorPosition.ScreenPixels.X, (int)previousInputState.CursorPosition.ScreenPixels.Y), new Point((int)inputState.CursorPosition.ScreenPixels.X, (int)inputState.CursorPosition.ScreenPixels.Y))); } } |