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.Locations; 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 { /********* ** Fields *********/ /// The underlying watchers for convenience. These are accessible individually as separate properties. private readonly List Watchers = new(); /********* ** 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. /// The observable list of game locations. public WatcherCore(SInputState inputState, ObservableCollection gameLocations) { // init watchers this.CursorWatcher = WatcherFactory.ForEquatable(nameof(inputState.CursorPosition), () => inputState.CursorPosition); this.MouseWheelScrollWatcher = WatcherFactory.ForEquatable(nameof(inputState.MouseState.ScrollWheelValue), () => inputState.MouseState.ScrollWheelValue); this.SaveIdWatcher = WatcherFactory.ForEquatable(nameof(Game1.uniqueIDForThisGame), () => Game1.hasLoadedGame ? Game1.uniqueIDForThisGame : 0); this.WindowSizeWatcher = WatcherFactory.ForEquatable(nameof(Game1.viewport), () => new Point(Game1.viewport.Width, Game1.viewport.Height)); this.TimeWatcher = WatcherFactory.ForEquatable(nameof(Game1.timeOfDay), () => Game1.timeOfDay); this.ActiveMenuWatcher = WatcherFactory.ForReference(nameof(Game1.activeClickableMenu), () => Game1.activeClickableMenu); this.LocationsWatcher = new WorldLocationsTracker(gameLocations, MineShaft.activeMines, VolcanoDungeon.activeLevels); this.LocaleWatcher = WatcherFactory.ForGenericEquality(nameof(LocalizedContentManager.CurrentLanguageCode), () => 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(); } } }