summaryrefslogtreecommitdiff
path: root/src/SMAPI/Framework/WatcherCore.cs
blob: 35f27cc7d7b6d8cf0178c51e89f468796aa6a4af (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
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
{
    /// <summary>Monitors the entire game state for changes, virally spreading watchers into any new entities that get created.</summary>
    internal class WatcherCore
    {
        /*********
        ** Fields
        *********/
        /// <summary>The underlying watchers for convenience. These are accessible individually as separate properties.</summary>
        private readonly List<IWatcher> Watchers = new();


        /*********
        ** 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>
        /// <param name="gameLocations">The observable list of game locations.</param>
        public WatcherCore(SInputState inputState, ObservableCollection<GameLocation> 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
            });
        }

        /// <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();
        }
    }
}