create watcher core (#310)
This commit is contained in:
parent
e27ada0f61
commit
235d67623d
|
@ -36,5 +36,12 @@ namespace StardewModdingAPI.Framework
|
||||||
this.Tile = tile;
|
this.Tile = tile;
|
||||||
this.GrabTile = grabTile;
|
this.GrabTile = grabTile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Get whether the current object is equal to another object of the same type.</summary>
|
||||||
|
/// <param name="other">An object to compare with this object.</param>
|
||||||
|
public bool Equals(ICursorPosition other)
|
||||||
|
{
|
||||||
|
return other != null && this.ScreenPixels == other.ScreenPixels;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,6 @@ using StardewModdingAPI.Framework.Events;
|
||||||
using StardewModdingAPI.Framework.Input;
|
using StardewModdingAPI.Framework.Input;
|
||||||
using StardewModdingAPI.Framework.Reflection;
|
using StardewModdingAPI.Framework.Reflection;
|
||||||
using StardewModdingAPI.Framework.StateTracking;
|
using StardewModdingAPI.Framework.StateTracking;
|
||||||
using StardewModdingAPI.Framework.StateTracking.FieldWatchers;
|
|
||||||
using StardewModdingAPI.Framework.Utilities;
|
using StardewModdingAPI.Framework.Utilities;
|
||||||
using StardewValley;
|
using StardewValley;
|
||||||
using StardewValley.BellsAndWhistles;
|
using StardewValley.BellsAndWhistles;
|
||||||
|
@ -85,38 +84,8 @@ namespace StardewModdingAPI.Framework
|
||||||
/****
|
/****
|
||||||
** Game state
|
** Game state
|
||||||
****/
|
****/
|
||||||
/// <summary>The underlying watchers for convenience. These are accessible individually as separate properties.</summary>
|
/// <summary>Monitors the entire game state for changes.</summary>
|
||||||
private readonly List<IWatcher> Watchers = new List<IWatcher>();
|
private WatcherCore Watchers;
|
||||||
|
|
||||||
/// <summary>Tracks changes to the window size.</summary>
|
|
||||||
private IValueWatcher<Point> WindowSizeWatcher;
|
|
||||||
|
|
||||||
/// <summary>Tracks changes to the current player.</summary>
|
|
||||||
private PlayerTracker CurrentPlayerTracker;
|
|
||||||
|
|
||||||
/// <summary>Tracks changes to the time of day (in 24-hour military format).</summary>
|
|
||||||
private IValueWatcher<int> TimeWatcher;
|
|
||||||
|
|
||||||
/// <summary>Tracks changes to the save ID.</summary>
|
|
||||||
private IValueWatcher<ulong> SaveIdWatcher;
|
|
||||||
|
|
||||||
/// <summary>Tracks changes to the game's locations.</summary>
|
|
||||||
private WorldLocationsTracker LocationsWatcher;
|
|
||||||
|
|
||||||
/// <summary>Tracks changes to <see cref="Game1.activeClickableMenu"/>.</summary>
|
|
||||||
private IValueWatcher<IClickableMenu> ActiveMenuWatcher;
|
|
||||||
|
|
||||||
/// <summary>Tracks changes to the cursor position.</summary>
|
|
||||||
private IValueWatcher<Vector2> CursorWatcher;
|
|
||||||
|
|
||||||
/// <summary>Tracks changes to the mouse wheel scroll.</summary>
|
|
||||||
private IValueWatcher<int> MouseWheelScrollWatcher;
|
|
||||||
|
|
||||||
/// <summary>The previous content locale.</summary>
|
|
||||||
private LocalizedContentManager.LanguageCode? PreviousLocale;
|
|
||||||
|
|
||||||
/// <summary>The previous cursor position.</summary>
|
|
||||||
private ICursorPosition PreviousCursorPosition;
|
|
||||||
|
|
||||||
/// <summary>An index incremented on every tick and reset every 60th tick (0–59).</summary>
|
/// <summary>An index incremented on every tick and reset every 60th tick (0–59).</summary>
|
||||||
private int CurrentUpdateTick;
|
private int CurrentUpdateTick;
|
||||||
|
@ -186,23 +155,7 @@ namespace StardewModdingAPI.Framework
|
||||||
this.Input.TrueUpdate();
|
this.Input.TrueUpdate();
|
||||||
|
|
||||||
// init watchers
|
// init watchers
|
||||||
this.CursorWatcher = WatcherFactory.ForEquatable(() => this.Input.CursorPosition.ScreenPixels);
|
this.Watchers = new WatcherCore(this.Input);
|
||||||
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<GameLocation>)Game1.locations);
|
|
||||||
this.Watchers.AddRange(new IWatcher[]
|
|
||||||
{
|
|
||||||
this.CursorWatcher,
|
|
||||||
this.MouseWheelScrollWatcher,
|
|
||||||
this.SaveIdWatcher,
|
|
||||||
this.WindowSizeWatcher,
|
|
||||||
this.TimeWatcher,
|
|
||||||
this.ActiveMenuWatcher,
|
|
||||||
this.LocationsWatcher
|
|
||||||
});
|
|
||||||
|
|
||||||
// raise callback
|
// raise callback
|
||||||
this.OnGameInitialised();
|
this.OnGameInitialised();
|
||||||
|
@ -372,44 +325,20 @@ namespace StardewModdingAPI.Framework
|
||||||
/*********
|
/*********
|
||||||
** Update watchers
|
** Update watchers
|
||||||
*********/
|
*********/
|
||||||
// reset player
|
this.Watchers.Update();
|
||||||
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();
|
|
||||||
|
|
||||||
/*********
|
/*********
|
||||||
** Locale changed events
|
** Locale changed events
|
||||||
*********/
|
*********/
|
||||||
if (this.PreviousLocale != LocalizedContentManager.CurrentLanguageCode)
|
if (this.Watchers.LocaleWatcher.IsChanged)
|
||||||
{
|
{
|
||||||
var oldValue = this.PreviousLocale;
|
var was = this.Watchers.LocaleWatcher.PreviousValue;
|
||||||
var newValue = LocalizedContentManager.CurrentLanguageCode;
|
var now = this.Watchers.LocaleWatcher.CurrentValue;
|
||||||
|
|
||||||
this.Monitor.Log($"Context: locale set to {newValue}.", LogLevel.Trace);
|
this.Monitor.Log($"Context: locale set to {now}.", LogLevel.Trace);
|
||||||
|
this.Events.Content_LocaleChanged.Raise(new EventArgsValueChanged<string>(was.ToString(), now.ToString()));
|
||||||
|
|
||||||
if (oldValue != null)
|
this.Watchers.LocaleWatcher.Reset();
|
||||||
this.Events.Content_LocaleChanged.Raise(new EventArgsValueChanged<string>(oldValue.ToString(), newValue.ToString()));
|
|
||||||
|
|
||||||
this.PreviousLocale = newValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*********
|
/*********
|
||||||
|
@ -450,12 +379,12 @@ namespace StardewModdingAPI.Framework
|
||||||
// event because we need to notify mods after the game handles the resize, so the
|
// 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
|
// game's metadata (like Game1.viewport) are updated. That's a bit complicated
|
||||||
// since the game adds & removes its own handler on the fly.
|
// since the game adds & removes its own handler on the fly.
|
||||||
if (this.WindowSizeWatcher.IsChanged)
|
if (this.Watchers.WindowSizeWatcher.IsChanged)
|
||||||
{
|
{
|
||||||
if (this.VerboseLogging)
|
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.Events.Graphics_Resize.Raise();
|
||||||
this.WindowSizeWatcher.Reset();
|
this.Watchers.WindowSizeWatcher.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*********
|
/*********
|
||||||
|
@ -470,21 +399,23 @@ namespace StardewModdingAPI.Framework
|
||||||
ICursorPosition cursor = this.Input.CursorPosition;
|
ICursorPosition cursor = this.Input.CursorPosition;
|
||||||
|
|
||||||
// raise cursor moved event
|
// raise cursor moved event
|
||||||
if (this.CursorWatcher.IsChanged && this.PreviousCursorPosition != null)
|
if (this.Watchers.CursorWatcher.IsChanged)
|
||||||
{
|
{
|
||||||
this.CursorWatcher.Reset();
|
ICursorPosition was = this.Watchers.CursorWatcher.PreviousValue;
|
||||||
this.Events.Input_CursorMoved.Raise(new InputCursorMovedArgsInput(this.PreviousCursorPosition, cursor));
|
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
|
// raise mouse wheel scrolled
|
||||||
if (this.MouseWheelScrollWatcher.IsChanged)
|
if (this.Watchers.MouseWheelScrollWatcher.IsChanged)
|
||||||
{
|
{
|
||||||
int oldValue = this.MouseWheelScrollWatcher.PreviousValue;
|
int was = this.Watchers.MouseWheelScrollWatcher.PreviousValue;
|
||||||
int newValue = this.MouseWheelScrollWatcher.CurrentValue;
|
int now = this.Watchers.MouseWheelScrollWatcher.CurrentValue;
|
||||||
this.MouseWheelScrollWatcher.Reset();
|
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
|
// raise input button events
|
||||||
|
@ -544,20 +475,20 @@ namespace StardewModdingAPI.Framework
|
||||||
/*********
|
/*********
|
||||||
** Menu events
|
** Menu events
|
||||||
*********/
|
*********/
|
||||||
if (this.ActiveMenuWatcher.IsChanged)
|
if (this.Watchers.ActiveMenuWatcher.IsChanged)
|
||||||
{
|
{
|
||||||
IClickableMenu previousMenu = this.ActiveMenuWatcher.PreviousValue;
|
IClickableMenu was = this.Watchers.ActiveMenuWatcher.PreviousValue;
|
||||||
IClickableMenu newMenu = this.ActiveMenuWatcher.CurrentValue;
|
IClickableMenu now = this.Watchers.ActiveMenuWatcher.CurrentValue;
|
||||||
this.ActiveMenuWatcher.Reset(); // reset here so a mod changing the menu will be raised as a new event afterwards
|
this.Watchers.ActiveMenuWatcher.Reset(); // reset here so a mod changing the menu will be raised as a new event afterwards
|
||||||
|
|
||||||
if (this.VerboseLogging)
|
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
|
// raise menu events
|
||||||
if (newMenu != null)
|
if (now != null)
|
||||||
this.Events.Menu_Changed.Raise(new EventArgsClickableMenuChanged(previousMenu, newMenu));
|
this.Events.Menu_Changed.Raise(new EventArgsClickableMenuChanged(was, now));
|
||||||
else
|
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)
|
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
|
// raise location changes
|
||||||
if (this.LocationsWatcher.IsChanged)
|
if (this.Watchers.LocationsWatcher.IsChanged)
|
||||||
{
|
{
|
||||||
// location list changes
|
// location list changes
|
||||||
if (this.LocationsWatcher.IsLocationListChanged)
|
if (this.Watchers.LocationsWatcher.IsLocationListChanged)
|
||||||
{
|
{
|
||||||
GameLocation[] added = this.LocationsWatcher.Added.ToArray();
|
GameLocation[] added = this.Watchers.LocationsWatcher.Added.ToArray();
|
||||||
GameLocation[] removed = this.LocationsWatcher.Removed.ToArray();
|
GameLocation[] removed = this.Watchers.LocationsWatcher.Removed.ToArray();
|
||||||
this.LocationsWatcher.ResetLocationList();
|
this.Watchers.LocationsWatcher.ResetLocationList();
|
||||||
|
|
||||||
if (this.VerboseLogging)
|
if (this.VerboseLogging)
|
||||||
{
|
{
|
||||||
string addedText = this.LocationsWatcher.Added.Any() ? string.Join(", ", added.Select(p => p.Name)) : "none";
|
string addedText = this.Watchers.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 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);
|
this.Monitor.Log($"Context: location list changed (added {addedText}; removed {removedText}).", LogLevel.Trace);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -591,7 +522,7 @@ namespace StardewModdingAPI.Framework
|
||||||
// raise location contents changed
|
// raise location contents changed
|
||||||
if (raiseWorldEvents)
|
if (raiseWorldEvents)
|
||||||
{
|
{
|
||||||
foreach (LocationTracker watcher in this.LocationsWatcher.Locations)
|
foreach (LocationTracker watcher in this.Watchers.LocationsWatcher.Locations)
|
||||||
{
|
{
|
||||||
// buildings changed
|
// buildings changed
|
||||||
if (watcher.BuildingsWatcher.IsChanged)
|
if (watcher.BuildingsWatcher.IsChanged)
|
||||||
|
@ -652,15 +583,15 @@ namespace StardewModdingAPI.Framework
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.LocationsWatcher.Reset();
|
this.Watchers.LocationsWatcher.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
// raise time changed
|
// raise time changed
|
||||||
if (raiseWorldEvents && this.TimeWatcher.IsChanged)
|
if (raiseWorldEvents && this.Watchers.TimeWatcher.IsChanged)
|
||||||
{
|
{
|
||||||
int was = this.TimeWatcher.PreviousValue;
|
int was = this.Watchers.TimeWatcher.PreviousValue;
|
||||||
int now = this.TimeWatcher.CurrentValue;
|
int now = this.Watchers.TimeWatcher.CurrentValue;
|
||||||
this.TimeWatcher.Reset();
|
this.Watchers.TimeWatcher.Reset();
|
||||||
|
|
||||||
if (this.VerboseLogging)
|
if (this.VerboseLogging)
|
||||||
this.Monitor.Log($"Context: time changed from {was} to {now}.", LogLevel.Trace);
|
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));
|
this.Events.Time_TimeOfDayChanged.Raise(new EventArgsIntChanged(was, now));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.TimeWatcher.Reset();
|
this.Watchers.TimeWatcher.Reset();
|
||||||
|
|
||||||
// raise player events
|
// raise player events
|
||||||
if (raiseWorldEvents)
|
if (raiseWorldEvents)
|
||||||
{
|
{
|
||||||
PlayerTracker curPlayer = this.CurrentPlayerTracker;
|
PlayerTracker curPlayer = this.Watchers.CurrentPlayerTracker;
|
||||||
|
|
||||||
// raise current location changed
|
// raise current location changed
|
||||||
if (curPlayer.TryGetNewLocation(out GameLocation newLocation))
|
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.Events.Mine_LevelChanged.Raise(new EventArgsMineLevelChanged(curPlayer.MineLevelWatcher.PreviousValue, mineLevel));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.CurrentPlayerTracker?.Reset();
|
this.Watchers.CurrentPlayerTracker?.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
// update save ID watcher
|
// update save ID watcher
|
||||||
this.SaveIdWatcher.Reset();
|
this.Watchers.SaveIdWatcher.Reset();
|
||||||
|
|
||||||
/*********
|
/*********
|
||||||
** Game update
|
** Game update
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace StardewModdingAPI.Framework.StateTracking.Comparers
|
||||||
|
{
|
||||||
|
/// <summary>Compares values using their <see cref="object.Equals(object)"/> method. This should only be used when <see cref="EquatableComparer{T}"/> won't work, since this doesn't validate whether they're comparable.</summary>
|
||||||
|
/// <typeparam name="T">The value type.</typeparam>
|
||||||
|
internal class GenericEqualsComparer<T> : IEqualityComparer<T>
|
||||||
|
{
|
||||||
|
/*********
|
||||||
|
** Public methods
|
||||||
|
*********/
|
||||||
|
/// <summary>Determines whether the specified objects are equal.</summary>
|
||||||
|
/// <returns>true if the specified objects are equal; otherwise, false.</returns>
|
||||||
|
/// <param name="x">The first object to compare.</param>
|
||||||
|
/// <param name="y">The second object to compare.</param>
|
||||||
|
public bool Equals(T x, T y)
|
||||||
|
{
|
||||||
|
if (x == null)
|
||||||
|
return y == null;
|
||||||
|
return x.Equals(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Get a hash code for the specified object.</summary>
|
||||||
|
/// <param name="obj">The value.</param>
|
||||||
|
public int GetHashCode(T obj)
|
||||||
|
{
|
||||||
|
return RuntimeHelpers.GetHashCode(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,6 +12,14 @@ namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers
|
||||||
/*********
|
/*********
|
||||||
** Public methods
|
** Public methods
|
||||||
*********/
|
*********/
|
||||||
|
/// <summary>Get a watcher which compares values using their <see cref="object.Equals(object)"/> method. This method should only be used when <see cref="ForEquatable{T}"/> won't work, since this doesn't validate whether they're comparable.</summary>
|
||||||
|
/// <typeparam name="T">The value type.</typeparam>
|
||||||
|
/// <param name="getValue">Get the current value.</param>
|
||||||
|
public static ComparableWatcher<T> ForGenericEquality<T>(Func<T> getValue) where T : struct
|
||||||
|
{
|
||||||
|
return new ComparableWatcher<T>(getValue, new GenericEqualsComparer<T>());
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Get a watcher for an <see cref="IEquatable{T}"/> value.</summary>
|
/// <summary>Get a watcher for an <see cref="IEquatable{T}"/> value.</summary>
|
||||||
/// <typeparam name="T">The value type.</typeparam>
|
/// <typeparam name="T">The value type.</typeparam>
|
||||||
/// <param name="getValue">Get the current value.</param>
|
/// <param name="getValue">Get the current value.</param>
|
||||||
|
|
|
@ -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
|
||||||
|
{
|
||||||
|
/// <summary>Monitors the entire game state for changes, virally spreading watchers into any new entities that get created.</summary>
|
||||||
|
internal class WatcherCore
|
||||||
|
{
|
||||||
|
/*********
|
||||||
|
** Public methods
|
||||||
|
*********/
|
||||||
|
/// <summary>The underlying watchers for convenience. These are accessible individually as separate properties.</summary>
|
||||||
|
private readonly List<IWatcher> Watchers = new List<IWatcher>();
|
||||||
|
|
||||||
|
|
||||||
|
/*********
|
||||||
|
** Accessors
|
||||||
|
*********/
|
||||||
|
/// <summary>Tracks changes to the window size.</summary>
|
||||||
|
public readonly IValueWatcher<Point> WindowSizeWatcher;
|
||||||
|
|
||||||
|
/// <summary>Tracks changes to the current player.</summary>
|
||||||
|
public PlayerTracker CurrentPlayerTracker;
|
||||||
|
|
||||||
|
/// <summary>Tracks changes to the time of day (in 24-hour military format).</summary>
|
||||||
|
public readonly IValueWatcher<int> TimeWatcher;
|
||||||
|
|
||||||
|
/// <summary>Tracks changes to the save ID.</summary>
|
||||||
|
public readonly IValueWatcher<ulong> SaveIdWatcher;
|
||||||
|
|
||||||
|
/// <summary>Tracks changes to the game's locations.</summary>
|
||||||
|
public readonly WorldLocationsTracker LocationsWatcher;
|
||||||
|
|
||||||
|
/// <summary>Tracks changes to <see cref="Game1.activeClickableMenu"/>.</summary>
|
||||||
|
public readonly IValueWatcher<IClickableMenu> ActiveMenuWatcher;
|
||||||
|
|
||||||
|
/// <summary>Tracks changes to the cursor position.</summary>
|
||||||
|
public readonly IValueWatcher<ICursorPosition> CursorWatcher;
|
||||||
|
|
||||||
|
/// <summary>Tracks changes to the mouse wheel scroll.</summary>
|
||||||
|
public readonly IValueWatcher<int> MouseWheelScrollWatcher;
|
||||||
|
|
||||||
|
/// <summary>Tracks changes to the content locale.</summary>
|
||||||
|
public readonly IValueWatcher<LocalizedContentManager.LanguageCode> LocaleWatcher;
|
||||||
|
|
||||||
|
|
||||||
|
/*********
|
||||||
|
** Public methods
|
||||||
|
*********/
|
||||||
|
/// <summary>Construct an instance.</summary>
|
||||||
|
/// <param name="inputState">Manages input visible to the game.</param>
|
||||||
|
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<GameLocation>)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
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Update the watchers and adjust for added or removed entities.</summary>
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Reset the current values as the baseline.</summary>
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
foreach (IWatcher watcher in this.Watchers)
|
||||||
|
watcher.Reset();
|
||||||
|
this.CurrentPlayerTracker?.Reset();
|
||||||
|
this.LocationsWatcher.Reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,10 @@
|
||||||
|
using System;
|
||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
|
|
||||||
namespace StardewModdingAPI
|
namespace StardewModdingAPI
|
||||||
{
|
{
|
||||||
/// <summary>Represents a cursor position in the different coordinate systems.</summary>
|
/// <summary>Represents a cursor position in the different coordinate systems.</summary>
|
||||||
public interface ICursorPosition
|
public interface ICursorPosition : IEquatable<ICursorPosition>
|
||||||
{
|
{
|
||||||
/// <summary>The pixel position relative to the top-left corner of the visible screen.</summary>
|
/// <summary>The pixel position relative to the top-left corner of the visible screen.</summary>
|
||||||
Vector2 ScreenPixels { get; }
|
Vector2 ScreenPixels { get; }
|
||||||
|
|
|
@ -129,6 +129,8 @@
|
||||||
<Compile Include="Framework\Models\ManifestDependency.cs" />
|
<Compile Include="Framework\Models\ManifestDependency.cs" />
|
||||||
<Compile Include="Framework\ModHelpers\InputHelper.cs" />
|
<Compile Include="Framework\ModHelpers\InputHelper.cs" />
|
||||||
<Compile Include="Framework\Serialisation\SemanticVersionConverter.cs" />
|
<Compile Include="Framework\Serialisation\SemanticVersionConverter.cs" />
|
||||||
|
<Compile Include="Framework\StateTracking\Comparers\GenericEqualsComparer.cs" />
|
||||||
|
<Compile Include="Framework\WatcherCore.cs" />
|
||||||
<Compile Include="IInputHelper.cs" />
|
<Compile Include="IInputHelper.cs" />
|
||||||
<Compile Include="Framework\Input\SInputState.cs" />
|
<Compile Include="Framework\Input\SInputState.cs" />
|
||||||
<Compile Include="Framework\Input\InputStatus.cs" />
|
<Compile Include="Framework\Input\InputStatus.cs" />
|
||||||
|
|
Loading…
Reference in New Issue