From d41fe6ff88b569f991f219c5f348d3688fba956f Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 2 Jun 2018 16:00:16 -0400 Subject: add input API --- src/SMAPI/Framework/CursorPosition.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src/SMAPI/Framework/CursorPosition.cs') 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 *********/ + /// The raw pixel position, not adjusted for the game zoom. + public Vector2 RawPixels { get; } + /// The pixel position relative to the top-left corner of the visible screen. public Vector2 ScreenPixels { get; } @@ -22,11 +25,13 @@ namespace StardewModdingAPI.Framework ** Public methods *********/ /// Construct an instance. + /// The raw pixel position, not adjusted for the game zoom. /// The pixel position relative to the top-left corner of the visible screen. /// The tile position relative to the top-left corner of the map. /// The tile position that the game considers under the cursor for purposes of clicking actions. - 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; -- cgit From 235d67623de648499db521606e4b9033d35388e5 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 10 Jun 2018 12:06:29 -0400 Subject: create watcher core (#310) --- src/SMAPI/Framework/CursorPosition.cs | 7 + src/SMAPI/Framework/SGame.cs | 169 ++++++--------------- .../Comparers/GenericEqualsComparer.cs | 31 ++++ .../StateTracking/FieldWatchers/WatcherFactory.cs | 8 + src/SMAPI/Framework/WatcherCore.cs | 119 +++++++++++++++ src/SMAPI/ICursorPosition.cs | 3 +- src/SMAPI/StardewModdingAPI.csproj | 2 + 7 files changed, 219 insertions(+), 120 deletions(-) create mode 100644 src/SMAPI/Framework/StateTracking/Comparers/GenericEqualsComparer.cs create mode 100644 src/SMAPI/Framework/WatcherCore.cs (limited to 'src/SMAPI/Framework/CursorPosition.cs') diff --git a/src/SMAPI/Framework/CursorPosition.cs b/src/SMAPI/Framework/CursorPosition.cs index 6f716746..aaf089d3 100644 --- a/src/SMAPI/Framework/CursorPosition.cs +++ b/src/SMAPI/Framework/CursorPosition.cs @@ -36,5 +36,12 @@ namespace StardewModdingAPI.Framework this.Tile = tile; this.GrabTile = grabTile; } + + /// Get whether the current object is equal to another object of the same type. + /// An object to compare with this object. + public bool Equals(ICursorPosition other) + { + return other != null && this.ScreenPixels == other.ScreenPixels; + } } } diff --git a/src/SMAPI/Framework/SGame.cs b/src/SMAPI/Framework/SGame.cs index a4d149f3..588d30c8 100644 --- a/src/SMAPI/Framework/SGame.cs +++ b/src/SMAPI/Framework/SGame.cs @@ -14,7 +14,6 @@ using StardewModdingAPI.Framework.Events; using StardewModdingAPI.Framework.Input; using StardewModdingAPI.Framework.Reflection; using StardewModdingAPI.Framework.StateTracking; -using StardewModdingAPI.Framework.StateTracking.FieldWatchers; using StardewModdingAPI.Framework.Utilities; using StardewValley; using StardewValley.BellsAndWhistles; @@ -85,38 +84,8 @@ namespace StardewModdingAPI.Framework /**** ** Game state ****/ - /// The underlying watchers for convenience. These are accessible individually as separate properties. - private readonly List Watchers = new List(); - - /// Tracks changes to the window size. - private IValueWatcher WindowSizeWatcher; - - /// Tracks changes to the current player. - private PlayerTracker CurrentPlayerTracker; - - /// Tracks changes to the time of day (in 24-hour military format). - private IValueWatcher TimeWatcher; - - /// Tracks changes to the save ID. - private IValueWatcher SaveIdWatcher; - - /// Tracks changes to the game's locations. - private WorldLocationsTracker LocationsWatcher; - - /// Tracks changes to . - private IValueWatcher ActiveMenuWatcher; - - /// Tracks changes to the cursor position. - private IValueWatcher CursorWatcher; - - /// Tracks changes to the mouse wheel scroll. - private IValueWatcher MouseWheelScrollWatcher; - - /// The previous content locale. - private LocalizedContentManager.LanguageCode? PreviousLocale; - - /// The previous cursor position. - private ICursorPosition PreviousCursorPosition; + /// Monitors the entire game state for changes. + private WatcherCore Watchers; /// An index incremented on every tick and reset every 60th tick (0–59). private int CurrentUpdateTick; @@ -186,23 +155,7 @@ namespace StardewModdingAPI.Framework this.Input.TrueUpdate(); // init watchers - 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)); - this.TimeWatcher = WatcherFactory.ForEquatable(() => Game1.timeOfDay); - this.ActiveMenuWatcher = WatcherFactory.ForReference(() => Game1.activeClickableMenu); - this.LocationsWatcher = new WorldLocationsTracker((ObservableCollection)Game1.locations); - this.Watchers.AddRange(new IWatcher[] - { - this.CursorWatcher, - this.MouseWheelScrollWatcher, - this.SaveIdWatcher, - this.WindowSizeWatcher, - this.TimeWatcher, - this.ActiveMenuWatcher, - this.LocationsWatcher - }); + this.Watchers = new WatcherCore(this.Input); // raise callback this.OnGameInitialised(); @@ -372,44 +325,20 @@ namespace StardewModdingAPI.Framework /********* ** Update watchers *********/ - // reset player - if (Context.IsWorldReady) - { - if (this.CurrentPlayerTracker == null || this.CurrentPlayerTracker.Player != Game1.player) - { - this.CurrentPlayerTracker?.Dispose(); - this.CurrentPlayerTracker = new PlayerTracker(Game1.player); - } - } - else - { - if (this.CurrentPlayerTracker != null) - { - this.CurrentPlayerTracker.Dispose(); - this.CurrentPlayerTracker = null; - } - } - - // update values - foreach (IWatcher watcher in this.Watchers) - watcher.Update(); - this.CurrentPlayerTracker?.Update(); - this.LocationsWatcher.Update(); + this.Watchers.Update(); /********* ** Locale changed events *********/ - if (this.PreviousLocale != LocalizedContentManager.CurrentLanguageCode) + if (this.Watchers.LocaleWatcher.IsChanged) { - var oldValue = this.PreviousLocale; - var newValue = LocalizedContentManager.CurrentLanguageCode; - - this.Monitor.Log($"Context: locale set to {newValue}.", LogLevel.Trace); + var was = this.Watchers.LocaleWatcher.PreviousValue; + var now = this.Watchers.LocaleWatcher.CurrentValue; - if (oldValue != null) - this.Events.Content_LocaleChanged.Raise(new EventArgsValueChanged(oldValue.ToString(), newValue.ToString())); + this.Monitor.Log($"Context: locale set to {now}.", LogLevel.Trace); + this.Events.Content_LocaleChanged.Raise(new EventArgsValueChanged(was.ToString(), now.ToString())); - this.PreviousLocale = newValue; + this.Watchers.LocaleWatcher.Reset(); } /********* @@ -450,12 +379,12 @@ namespace StardewModdingAPI.Framework // event because we need to notify mods after the game handles the resize, so the // game's metadata (like Game1.viewport) are updated. That's a bit complicated // since the game adds & removes its own handler on the fly. - if (this.WindowSizeWatcher.IsChanged) + if (this.Watchers.WindowSizeWatcher.IsChanged) { if (this.VerboseLogging) - this.Monitor.Log($"Context: window size changed to {this.WindowSizeWatcher.CurrentValue}.", LogLevel.Trace); + this.Monitor.Log($"Context: window size changed to {this.Watchers.WindowSizeWatcher.CurrentValue}.", LogLevel.Trace); this.Events.Graphics_Resize.Raise(); - this.WindowSizeWatcher.Reset(); + this.Watchers.WindowSizeWatcher.Reset(); } /********* @@ -470,21 +399,23 @@ namespace StardewModdingAPI.Framework ICursorPosition cursor = this.Input.CursorPosition; // raise cursor moved event - if (this.CursorWatcher.IsChanged && this.PreviousCursorPosition != null) + if (this.Watchers.CursorWatcher.IsChanged) { - this.CursorWatcher.Reset(); - this.Events.Input_CursorMoved.Raise(new InputCursorMovedArgsInput(this.PreviousCursorPosition, cursor)); + ICursorPosition was = this.Watchers.CursorWatcher.PreviousValue; + ICursorPosition now = this.Watchers.CursorWatcher.CurrentValue; + this.Watchers.CursorWatcher.Reset(); + + this.Events.Input_CursorMoved.Raise(new InputCursorMovedArgsInput(was, now)); } - this.PreviousCursorPosition = cursor; // raise mouse wheel scrolled - if (this.MouseWheelScrollWatcher.IsChanged) + if (this.Watchers.MouseWheelScrollWatcher.IsChanged) { - int oldValue = this.MouseWheelScrollWatcher.PreviousValue; - int newValue = this.MouseWheelScrollWatcher.CurrentValue; - this.MouseWheelScrollWatcher.Reset(); + int was = this.Watchers.MouseWheelScrollWatcher.PreviousValue; + int now = this.Watchers.MouseWheelScrollWatcher.CurrentValue; + this.Watchers.MouseWheelScrollWatcher.Reset(); - this.Events.Input_MouseWheelScrolled.Raise(new InputMouseWheelScrolledEventArgs(cursor, oldValue, newValue)); + this.Events.Input_MouseWheelScrolled.Raise(new InputMouseWheelScrolledEventArgs(cursor, was, now)); } // raise input button events @@ -544,20 +475,20 @@ namespace StardewModdingAPI.Framework /********* ** Menu events *********/ - if (this.ActiveMenuWatcher.IsChanged) + if (this.Watchers.ActiveMenuWatcher.IsChanged) { - IClickableMenu previousMenu = this.ActiveMenuWatcher.PreviousValue; - IClickableMenu newMenu = this.ActiveMenuWatcher.CurrentValue; - this.ActiveMenuWatcher.Reset(); // reset here so a mod changing the menu will be raised as a new event afterwards + IClickableMenu was = this.Watchers.ActiveMenuWatcher.PreviousValue; + IClickableMenu now = this.Watchers.ActiveMenuWatcher.CurrentValue; + this.Watchers.ActiveMenuWatcher.Reset(); // reset here so a mod changing the menu will be raised as a new event afterwards if (this.VerboseLogging) - this.Monitor.Log($"Context: menu changed from {previousMenu?.GetType().FullName ?? "none"} to {newMenu?.GetType().FullName ?? "none"}.", LogLevel.Trace); + this.Monitor.Log($"Context: menu changed from {was?.GetType().FullName ?? "none"} to {now?.GetType().FullName ?? "none"}.", LogLevel.Trace); // raise menu events - if (newMenu != null) - this.Events.Menu_Changed.Raise(new EventArgsClickableMenuChanged(previousMenu, newMenu)); + if (now != null) + this.Events.Menu_Changed.Raise(new EventArgsClickableMenuChanged(was, now)); else - this.Events.Menu_Closed.Raise(new EventArgsClickableMenuClosed(previousMenu)); + this.Events.Menu_Closed.Raise(new EventArgsClickableMenuClosed(was)); } /********* @@ -565,22 +496,22 @@ namespace StardewModdingAPI.Framework *********/ if (Context.IsWorldReady) { - bool raiseWorldEvents = !this.SaveIdWatcher.IsChanged; // don't report changes from unloaded => loaded + bool raiseWorldEvents = !this.Watchers.SaveIdWatcher.IsChanged; // don't report changes from unloaded => loaded // raise location changes - if (this.LocationsWatcher.IsChanged) + if (this.Watchers.LocationsWatcher.IsChanged) { // location list changes - if (this.LocationsWatcher.IsLocationListChanged) + if (this.Watchers.LocationsWatcher.IsLocationListChanged) { - GameLocation[] added = this.LocationsWatcher.Added.ToArray(); - GameLocation[] removed = this.LocationsWatcher.Removed.ToArray(); - this.LocationsWatcher.ResetLocationList(); + GameLocation[] added = this.Watchers.LocationsWatcher.Added.ToArray(); + GameLocation[] removed = this.Watchers.LocationsWatcher.Removed.ToArray(); + this.Watchers.LocationsWatcher.ResetLocationList(); if (this.VerboseLogging) { - string addedText = this.LocationsWatcher.Added.Any() ? string.Join(", ", added.Select(p => p.Name)) : "none"; - string removedText = this.LocationsWatcher.Removed.Any() ? string.Join(", ", removed.Select(p => p.Name)) : "none"; + string addedText = this.Watchers.LocationsWatcher.Added.Any() ? string.Join(", ", added.Select(p => p.Name)) : "none"; + string removedText = this.Watchers.LocationsWatcher.Removed.Any() ? string.Join(", ", removed.Select(p => p.Name)) : "none"; this.Monitor.Log($"Context: location list changed (added {addedText}; removed {removedText}).", LogLevel.Trace); } @@ -591,7 +522,7 @@ namespace StardewModdingAPI.Framework // raise location contents changed if (raiseWorldEvents) { - foreach (LocationTracker watcher in this.LocationsWatcher.Locations) + foreach (LocationTracker watcher in this.Watchers.LocationsWatcher.Locations) { // buildings changed if (watcher.BuildingsWatcher.IsChanged) @@ -652,15 +583,15 @@ namespace StardewModdingAPI.Framework } } else - this.LocationsWatcher.Reset(); + this.Watchers.LocationsWatcher.Reset(); } // raise time changed - if (raiseWorldEvents && this.TimeWatcher.IsChanged) + if (raiseWorldEvents && this.Watchers.TimeWatcher.IsChanged) { - int was = this.TimeWatcher.PreviousValue; - int now = this.TimeWatcher.CurrentValue; - this.TimeWatcher.Reset(); + int was = this.Watchers.TimeWatcher.PreviousValue; + int now = this.Watchers.TimeWatcher.CurrentValue; + this.Watchers.TimeWatcher.Reset(); if (this.VerboseLogging) this.Monitor.Log($"Context: time changed from {was} to {now}.", LogLevel.Trace); @@ -668,12 +599,12 @@ namespace StardewModdingAPI.Framework this.Events.Time_TimeOfDayChanged.Raise(new EventArgsIntChanged(was, now)); } else - this.TimeWatcher.Reset(); + this.Watchers.TimeWatcher.Reset(); // raise player events if (raiseWorldEvents) { - PlayerTracker curPlayer = this.CurrentPlayerTracker; + PlayerTracker curPlayer = this.Watchers.CurrentPlayerTracker; // raise current location changed if (curPlayer.TryGetNewLocation(out GameLocation newLocation)) @@ -708,11 +639,11 @@ namespace StardewModdingAPI.Framework this.Events.Mine_LevelChanged.Raise(new EventArgsMineLevelChanged(curPlayer.MineLevelWatcher.PreviousValue, mineLevel)); } } - this.CurrentPlayerTracker?.Reset(); + this.Watchers.CurrentPlayerTracker?.Reset(); } // update save ID watcher - this.SaveIdWatcher.Reset(); + this.Watchers.SaveIdWatcher.Reset(); /********* ** Game update diff --git a/src/SMAPI/Framework/StateTracking/Comparers/GenericEqualsComparer.cs b/src/SMAPI/Framework/StateTracking/Comparers/GenericEqualsComparer.cs new file mode 100644 index 00000000..cc1d6553 --- /dev/null +++ b/src/SMAPI/Framework/StateTracking/Comparers/GenericEqualsComparer.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace StardewModdingAPI.Framework.StateTracking.Comparers +{ + /// Compares values using their method. This should only be used when won't work, since this doesn't validate whether they're comparable. + /// The value type. + internal class GenericEqualsComparer : IEqualityComparer + { + /********* + ** Public methods + *********/ + /// Determines whether the specified objects are equal. + /// true if the specified objects are equal; otherwise, false. + /// The first object to compare. + /// The second object to compare. + public bool Equals(T x, T y) + { + if (x == null) + return y == null; + return x.Equals(y); + } + + /// Get a hash code for the specified object. + /// The value. + public int GetHashCode(T obj) + { + return RuntimeHelpers.GetHashCode(obj); + } + } +} diff --git a/src/SMAPI/Framework/StateTracking/FieldWatchers/WatcherFactory.cs b/src/SMAPI/Framework/StateTracking/FieldWatchers/WatcherFactory.cs index 4f1ac9f4..d7a02668 100644 --- a/src/SMAPI/Framework/StateTracking/FieldWatchers/WatcherFactory.cs +++ b/src/SMAPI/Framework/StateTracking/FieldWatchers/WatcherFactory.cs @@ -12,6 +12,14 @@ namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers /********* ** Public methods *********/ + /// Get a watcher which compares values using their method. This method should only be used when won't work, since this doesn't validate whether they're comparable. + /// The value type. + /// Get the current value. + public static ComparableWatcher ForGenericEquality(Func getValue) where T : struct + { + return new ComparableWatcher(getValue, new GenericEqualsComparer()); + } + /// Get a watcher for an value. /// The value type. /// Get the current value. diff --git a/src/SMAPI/Framework/WatcherCore.cs b/src/SMAPI/Framework/WatcherCore.cs new file mode 100644 index 00000000..64b063cf --- /dev/null +++ b/src/SMAPI/Framework/WatcherCore.cs @@ -0,0 +1,119 @@ +using System.Collections.Generic; +using System.Collections.ObjectModel; +using Microsoft.Xna.Framework; +using StardewModdingAPI.Framework.Input; +using StardewModdingAPI.Framework.StateTracking; +using StardewModdingAPI.Framework.StateTracking.FieldWatchers; +using StardewValley; +using StardewValley.Menus; + +namespace StardewModdingAPI.Framework +{ + /// Monitors the entire game state for changes, virally spreading watchers into any new entities that get created. + internal class WatcherCore + { + /********* + ** Public methods + *********/ + /// The underlying watchers for convenience. These are accessible individually as separate properties. + private readonly List Watchers = new List(); + + + /********* + ** Accessors + *********/ + /// Tracks changes to the window size. + public readonly IValueWatcher WindowSizeWatcher; + + /// Tracks changes to the current player. + public PlayerTracker CurrentPlayerTracker; + + /// Tracks changes to the time of day (in 24-hour military format). + public readonly IValueWatcher TimeWatcher; + + /// Tracks changes to the save ID. + public readonly IValueWatcher SaveIdWatcher; + + /// Tracks changes to the game's locations. + public readonly WorldLocationsTracker LocationsWatcher; + + /// Tracks changes to . + public readonly IValueWatcher ActiveMenuWatcher; + + /// Tracks changes to the cursor position. + public readonly IValueWatcher CursorWatcher; + + /// Tracks changes to the mouse wheel scroll. + public readonly IValueWatcher MouseWheelScrollWatcher; + + /// Tracks changes to the content locale. + public readonly IValueWatcher LocaleWatcher; + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// Manages input visible to the game. + public WatcherCore(SInputState inputState) + { + // init watchers + this.CursorWatcher = WatcherFactory.ForEquatable(() => inputState.CursorPosition); + this.MouseWheelScrollWatcher = WatcherFactory.ForEquatable(() => inputState.RealMouse.ScrollWheelValue); + this.SaveIdWatcher = WatcherFactory.ForEquatable(() => Game1.hasLoadedGame ? Game1.uniqueIDForThisGame : 0); + this.WindowSizeWatcher = WatcherFactory.ForEquatable(() => new Point(Game1.viewport.Width, Game1.viewport.Height)); + this.TimeWatcher = WatcherFactory.ForEquatable(() => Game1.timeOfDay); + this.ActiveMenuWatcher = WatcherFactory.ForReference(() => Game1.activeClickableMenu); + this.LocationsWatcher = new WorldLocationsTracker((ObservableCollection)Game1.locations); + this.LocaleWatcher = WatcherFactory.ForGenericEquality(() => LocalizedContentManager.CurrentLanguageCode); + this.Watchers.AddRange(new IWatcher[] + { + this.CursorWatcher, + this.MouseWheelScrollWatcher, + this.SaveIdWatcher, + this.WindowSizeWatcher, + this.TimeWatcher, + this.ActiveMenuWatcher, + this.LocationsWatcher, + this.LocaleWatcher + }); + } + + /// Update the watchers and adjust for added or removed entities. + public void Update() + { + // reset player + if (Context.IsWorldReady) + { + if (this.CurrentPlayerTracker == null || this.CurrentPlayerTracker.Player != Game1.player) + { + this.CurrentPlayerTracker?.Dispose(); + this.CurrentPlayerTracker = new PlayerTracker(Game1.player); + } + } + else + { + if (this.CurrentPlayerTracker != null) + { + this.CurrentPlayerTracker.Dispose(); + this.CurrentPlayerTracker = null; + } + } + + // update values + foreach (IWatcher watcher in this.Watchers) + watcher.Update(); + this.CurrentPlayerTracker?.Update(); + this.LocationsWatcher.Update(); + } + + /// Reset the current values as the baseline. + public void Reset() + { + foreach (IWatcher watcher in this.Watchers) + watcher.Reset(); + this.CurrentPlayerTracker?.Reset(); + this.LocationsWatcher.Reset(); + } + } +} diff --git a/src/SMAPI/ICursorPosition.cs b/src/SMAPI/ICursorPosition.cs index ddb8eb49..78f4fc21 100644 --- a/src/SMAPI/ICursorPosition.cs +++ b/src/SMAPI/ICursorPosition.cs @@ -1,9 +1,10 @@ +using System; using Microsoft.Xna.Framework; namespace StardewModdingAPI { /// Represents a cursor position in the different coordinate systems. - public interface ICursorPosition + public interface ICursorPosition : IEquatable { /// The pixel position relative to the top-left corner of the visible screen. Vector2 ScreenPixels { get; } diff --git a/src/SMAPI/StardewModdingAPI.csproj b/src/SMAPI/StardewModdingAPI.csproj index 8e3ad83b..67c48a57 100644 --- a/src/SMAPI/StardewModdingAPI.csproj +++ b/src/SMAPI/StardewModdingAPI.csproj @@ -129,6 +129,8 @@ + + -- cgit From 08e9c7e7d36cbf0720c93b395e688aeb5c86b1dc Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Tue, 19 Jun 2018 22:59:38 -0400 Subject: add absolute pixels to ICursorPosition, fix tile not updated if screen-relative pos didn't change (#546) --- docs/release-notes.md | 1 + src/SMAPI/Framework/CursorPosition.cs | 12 ++++++------ src/SMAPI/Framework/Input/SInputState.cs | 16 +++++++++------- src/SMAPI/ICursorPosition.cs | 3 +++ 4 files changed, 19 insertions(+), 13 deletions(-) (limited to 'src/SMAPI/Framework/CursorPosition.cs') diff --git a/docs/release-notes.md b/docs/release-notes.md index b3ab2481..fa6501a3 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -42,6 +42,7 @@ * Added support for custom seasonal tilesheets when loading an unpacked `.tbin` map. * Added Harmony DLL for internal use by SMAPI. (Mods should still include their own copy for backwards compatibility, and in case it's removed later. SMAPI will always load its own version though.) * Added option to suppress update checks for a specific mod in `StardewModdingAPI.config.json`. + * Added absolute pixels to `ICursorPosition`. * Update checks now use the update key order when deciding which to link to. * Fixed error if a mod loads a PNG while the game is loading (e.g. custom map tilesheets via `IAssetLoader`). * Fixed assets loaded by temporary content managers not being editable by mods. diff --git a/src/SMAPI/Framework/CursorPosition.cs b/src/SMAPI/Framework/CursorPosition.cs index aaf089d3..079917f2 100644 --- a/src/SMAPI/Framework/CursorPosition.cs +++ b/src/SMAPI/Framework/CursorPosition.cs @@ -8,8 +8,8 @@ namespace StardewModdingAPI.Framework /********* ** Accessors *********/ - /// The raw pixel position, not adjusted for the game zoom. - public Vector2 RawPixels { get; } + /// The pixel position relative to the top-left corner of the in-game map. + public Vector2 AbsolutePixels { get; } /// The pixel position relative to the top-left corner of the visible screen. public Vector2 ScreenPixels { get; } @@ -25,13 +25,13 @@ namespace StardewModdingAPI.Framework ** Public methods *********/ /// Construct an instance. - /// The raw pixel position, not adjusted for the game zoom. + /// The pixel position relative to the top-left corner of the in-game map. /// The pixel position relative to the top-left corner of the visible screen. /// The tile position relative to the top-left corner of the map. /// The tile position that the game considers under the cursor for purposes of clicking actions. - public CursorPosition(Vector2 rawPixels, Vector2 screenPixels, Vector2 tile, Vector2 grabTile) + public CursorPosition(Vector2 absolutePixels, Vector2 screenPixels, Vector2 tile, Vector2 grabTile) { - this.RawPixels = rawPixels; + this.AbsolutePixels = absolutePixels; this.ScreenPixels = screenPixels; this.Tile = tile; this.GrabTile = grabTile; @@ -41,7 +41,7 @@ namespace StardewModdingAPI.Framework /// An object to compare with this object. public bool Equals(ICursorPosition other) { - return other != null && this.ScreenPixels == other.ScreenPixels; + return other != null && this.AbsolutePixels == other.AbsolutePixels; } } } diff --git a/src/SMAPI/Framework/Input/SInputState.cs b/src/SMAPI/Framework/Input/SInputState.cs index 44fd0618..0228db0d 100644 --- a/src/SMAPI/Framework/Input/SInputState.cs +++ b/src/SMAPI/Framework/Input/SInputState.cs @@ -82,15 +82,15 @@ namespace StardewModdingAPI.Framework.Input KeyboardState realKeyboard = Keyboard.GetState(); MouseState realMouse = Mouse.GetState(); var activeButtons = this.DeriveStatuses(this.ActiveButtons, realKeyboard, realMouse, realController); - Vector2 cursorRawPixelPos = new Vector2(this.RealMouse.X, this.RealMouse.Y); + Vector2 cursorAbsolutePos = new Vector2(realMouse.X + Game1.viewport.X, realMouse.Y + Game1.viewport.Y); // update real states this.ActiveButtons = activeButtons; this.RealController = realController; this.RealKeyboard = realKeyboard; this.RealMouse = realMouse; - if (this.CursorPositionImpl?.RawPixels != cursorRawPixelPos) - this.CursorPositionImpl = this.GetCursorPosition(cursorRawPixelPos); + if (this.CursorPositionImpl?.AbsolutePixels != cursorAbsolutePos) + this.CursorPositionImpl = this.GetCursorPosition(realMouse, cursorAbsolutePos); // update suppressed states this.SuppressButtons.RemoveWhere(p => !this.GetStatus(activeButtons, p).IsDown()); @@ -162,15 +162,17 @@ namespace StardewModdingAPI.Framework.Input ** Private methods *********/ /// Get the current cursor position. - /// The raw pixel position from the mouse state. - private CursorPosition GetCursorPosition(Vector2 rawPixelPos) + /// The current mouse state. + /// The absolute pixel position relative to the map. + private CursorPosition GetCursorPosition(MouseState mouseState, Vector2 absolutePixels) { - 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 rawPixels = new Vector2(mouseState.X, mouseState.Y); + Vector2 screenPixels = rawPixels * new Vector2((float)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); + return new CursorPosition(absolutePixels, screenPixels, tile, grabTile); } /// Whether input should be suppressed in the current context. diff --git a/src/SMAPI/ICursorPosition.cs b/src/SMAPI/ICursorPosition.cs index 78f4fc21..21c57db0 100644 --- a/src/SMAPI/ICursorPosition.cs +++ b/src/SMAPI/ICursorPosition.cs @@ -6,6 +6,9 @@ namespace StardewModdingAPI /// Represents a cursor position in the different coordinate systems. public interface ICursorPosition : IEquatable { + /// The pixel position relative to the top-left corner of the in-game map. + Vector2 AbsolutePixels { get; } + /// The pixel position relative to the top-left corner of the visible screen. Vector2 ScreenPixels { get; } -- cgit