diff options
Diffstat (limited to 'src/SMAPI/Utilities')
-rw-r--r-- | src/SMAPI/Utilities/PerScreen.cs | 79 |
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); + } + } + } +} |