summaryrefslogtreecommitdiff
path: root/src/SMAPI/Utilities/PerScreen.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/SMAPI/Utilities/PerScreen.cs')
-rw-r--r--src/SMAPI/Utilities/PerScreen.cs79
1 files changed, 79 insertions, 0 deletions
diff --git a/src/SMAPI/Utilities/PerScreen.cs b/src/SMAPI/Utilities/PerScreen.cs
new file mode 100644
index 00000000..55dae0d8
--- /dev/null
+++ b/src/SMAPI/Utilities/PerScreen.cs
@@ -0,0 +1,79 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace StardewModdingAPI.Utilities
+{
+ /// <summary>Manages a separate value for each player in split-screen mode. This can safely be used in non-split-screen mode too, it'll just have a single state in that case.</summary>
+ /// <typeparam name="T">The state class.</typeparam>
+ public class PerScreen<T>
+ {
+ /*********
+ ** Fields
+ *********/
+ /// <summary>Create the initial value for a player.</summary>
+ private readonly Func<T> CreateNewState;
+
+ /// <summary>The tracked values for each player.</summary>
+ private readonly IDictionary<int, T> States = new Dictionary<int, T>();
+
+ /// <summary>The last <see cref="Context.LastRemovedScreenId"/> value for which this instance was updated.</summary>
+ private int LastRemovedScreenId;
+
+
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>The value for the current player.</summary>
+ /// <remarks>The value is initialized the first time it's requested for that player, unless it's set manually first.</remarks>
+ public T Value
+ {
+ get
+ {
+ this.RemoveDeadPlayers();
+ return this.States.TryGetValue(Context.ScreenId, out T state)
+ ? state
+ : this.States[Context.ScreenId] = this.CreateNewState();
+ }
+ set
+ {
+ this.RemoveDeadPlayers();
+ this.States[Context.ScreenId] = value;
+ }
+ }
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ public PerScreen()
+ : this(null) { }
+
+ /// <summary>Construct an instance.</summary>
+ /// <param name="createNewState">Create the initial state for a player screen.</param>
+ public PerScreen(Func<T> createNewState)
+ {
+ this.CreateNewState = createNewState ?? (() => default);
+ }
+
+
+ /*********
+ ** Private methods
+ *********/
+ /// <summary>Remove players who are no longer have a split-screen index.</summary>
+ /// <returns>Returns whether any players were removed.</returns>
+ private void RemoveDeadPlayers()
+ {
+ if (this.LastRemovedScreenId == Context.LastRemovedScreenId)
+ return;
+
+ this.LastRemovedScreenId = Context.LastRemovedScreenId;
+ foreach (int id in this.States.Keys.ToArray())
+ {
+ if (!Context.HasScreenId(id))
+ this.States.Remove(id);
+ }
+ }
+ }
+}