using System; using System.Collections.Generic; using System.Linq; using StardewModdingAPI.Events; using StardewModdingAPI.Framework.StateTracking.Comparers; using StardewModdingAPI.Framework.StateTracking.FieldWatchers; using StardewValley; using StardewValley.Objects; using ChangeType = StardewModdingAPI.Events.ChangeType; namespace StardewModdingAPI.Framework.StateTracking { /// Tracks changes to a chest's items. internal class ChestTracker : IDisposable { /********* ** Fields *********/ /// The item stack sizes as of the last update. private readonly IDictionary StackSizes; /// Items added since the last update. private readonly HashSet Added = new HashSet(new ObjectReferenceComparer()); /// Items removed since the last update. private readonly HashSet Removed = new HashSet(new ObjectReferenceComparer()); /// The underlying inventory watcher. private readonly ICollectionWatcher InventoryWatcher; /********* ** Accessors *********/ /// The chest being tracked. public Chest Chest { get; } /********* ** Public methods *********/ /// Construct an instance. /// The chest being tracked. public ChestTracker(Chest chest) { this.Chest = chest; this.InventoryWatcher = WatcherFactory.ForNetList(chest.items); this.StackSizes = this.Chest.items .Where(n => n != null) .Distinct() .ToDictionary(n => n, n => n.Stack); } /// Update the current values if needed. public void Update() { // update watcher this.InventoryWatcher.Update(); foreach (Item item in this.InventoryWatcher.Added.Where(p => p != null)) this.Added.Add(item); foreach (Item item in this.InventoryWatcher.Removed.Where(p => p != null)) { if (!this.Added.Remove(item)) // item didn't change if it was both added and removed, so remove it from both lists this.Removed.Add(item); } // stop tracking removed stacks foreach (Item item in this.Removed) this.StackSizes.Remove(item); } /// Reset all trackers so their current values are the baseline. public void Reset() { // update stack sizes foreach (Item item in this.StackSizes.Keys.ToArray().Concat(this.Added)) this.StackSizes[item] = item.Stack; // update watcher this.InventoryWatcher.Reset(); this.Added.Clear(); this.Removed.Clear(); } /// Get the inventory changes since the last update. public IEnumerable GetInventoryChanges() { // removed foreach (Item item in this.Removed) yield return new ItemStackChange { Item = item, StackChange = -item.Stack, ChangeType = ChangeType.Removed }; // added foreach (Item item in this.Added) yield return new ItemStackChange { Item = item, StackChange = item.Stack, ChangeType = ChangeType.Added }; // stack size changed foreach (var entry in this.StackSizes) { Item item = entry.Key; int prevStack = entry.Value; if (item.Stack != prevStack) yield return new ItemStackChange { Item = item, StackChange = item.Stack - prevStack, ChangeType = ChangeType.StackChange }; } } /// Release watchers and resources. public void Dispose() { this.StackSizes.Clear(); this.Added.Clear(); this.Removed.Clear(); this.InventoryWatcher.Dispose(); } } }