diff --git a/Dalamud/Game/Inventory/GameInventory.cs b/Dalamud/Game/Inventory/GameInventory.cs
new file mode 100644
index 000000000..7cd2556e2
--- /dev/null
+++ b/Dalamud/Game/Inventory/GameInventory.cs
@@ -0,0 +1,268 @@
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+
+using Dalamud.IoC;
+using Dalamud.IoC.Internal;
+using Dalamud.Logging.Internal;
+using Dalamud.Plugin.Services;
+using FFXIVClientStructs.FFXIV.Client.Game;
+
+namespace Dalamud.Game.Inventory;
+
+///
+/// This class provides events for the players in-game inventory.
+///
+[InterfaceVersion("1.0")]
+[ServiceManager.EarlyLoadedService]
+internal class GameInventory : IDisposable, IServiceType, IGameInventory
+{
+ private static readonly ModuleLog Log = new("GameInventory");
+
+ [ServiceManager.ServiceDependency]
+ private readonly Framework framework = Service.Get();
+
+ private readonly Dictionary> inventoryCache;
+
+ [ServiceManager.ServiceConstructor]
+ private GameInventory()
+ {
+ this.inventoryCache = new Dictionary>();
+
+ foreach (var inventoryType in Enum.GetValues())
+ {
+ this.inventoryCache.Add(inventoryType, new Dictionary());
+ }
+
+ this.framework.Update += this.OnFrameworkUpdate;
+ }
+
+ ///
+ public event IGameInventory.OnItemMovedDelegate? ItemMoved;
+
+ ///
+ public event IGameInventory.OnItemRemovedDelegate? ItemRemoved;
+
+ ///
+ public event IGameInventory.OnItemAddedDelegate? ItemAdded;
+
+ ///
+ public event IGameInventory.OnItemChangedDelegate? ItemChanged;
+
+ ///
+ public void Dispose()
+ {
+ this.framework.Update -= this.OnFrameworkUpdate;
+ }
+
+ private void OnFrameworkUpdate(IFramework framework1)
+ {
+ // If no one is listening for event's then we don't need to track anything.
+ if (!this.AnyListeners()) return;
+
+ var performanceMonitor = Stopwatch.StartNew();
+
+ var changelog = new List();
+
+ foreach (var (inventoryType, cachedInventoryItems) in this.inventoryCache)
+ {
+ foreach (var item in this.GetItemsForInventory(inventoryType))
+ {
+ if (cachedInventoryItems.TryGetValue(item.Slot, out var inventoryItem))
+ {
+ // Gained Item
+ // If the item we have cached has an item id of 0, then we expect it to be an empty slot.
+ // However, if the item we see in the game data has an item id that is not 0, then it now has an item.
+ if (inventoryItem.ItemID is 0 && item.ItemID is not 0)
+ {
+ var gameInventoryItem = new GameInventoryItem(item);
+ this.ItemAdded?.Invoke(inventoryType, (uint)item.Slot, gameInventoryItem);
+ changelog.Add(new GameInventoryItemChangelog(GameInventoryChangelogState.Added, gameInventoryItem));
+
+ Log.Verbose($"New Item Added to {inventoryType}: {item.ItemID}");
+ this.inventoryCache[inventoryType][item.Slot] = item;
+ }
+
+ // Removed Item
+ // If the item we have cached has an item id of not 0, then we expect it to have an item.
+ // However, if the item we see in the game data has an item id that is 0, then it was removed from this inventory.
+ if (inventoryItem.ItemID is not 0 && item.ItemID is 0)
+ {
+ var gameInventoryItem = new GameInventoryItem(inventoryItem);
+ this.ItemRemoved?.Invoke(inventoryType, (uint)item.Slot, gameInventoryItem);
+ changelog.Add(new GameInventoryItemChangelog(GameInventoryChangelogState.Removed, gameInventoryItem));
+
+ Log.Verbose($"Item Removed from {inventoryType}: {inventoryItem.ItemID}");
+ this.inventoryCache[inventoryType][item.Slot] = item;
+ }
+
+ // Changed Item
+ // If the item we have cached, does not match the item that we see in the game data
+ // AND if neither item is empty, then the item has been changed.
+ if (this.IsItemChanged(inventoryItem, item) && inventoryItem.ItemID is not 0 && item.ItemID is not 0)
+ {
+ var gameInventoryItem = new GameInventoryItem(inventoryItem);
+ this.ItemChanged?.Invoke(inventoryType, (uint)item.Slot, gameInventoryItem);
+
+ Log.Verbose($"Item Changed {inventoryType}: {inventoryItem.ItemID}");
+ this.inventoryCache[inventoryType][item.Slot] = item;
+ }
+ }
+ else
+ {
+ cachedInventoryItems.Add(item.Slot, item);
+ }
+ }
+ }
+
+ // Resolve changelog for item moved
+ // Group all changelogs that have the same itemId, and check if there was an add and a remove event for that item.
+ foreach (var itemGroup in changelog.GroupBy(log => log.Item.ItemId))
+ {
+ var hasAdd = false;
+ var hasRemove = false;
+
+ foreach (var log in itemGroup)
+ {
+ switch (log.State)
+ {
+ case GameInventoryChangelogState.Added:
+ hasAdd = true;
+ break;
+
+ case GameInventoryChangelogState.Removed:
+ hasRemove = true;
+ break;
+
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ var itemMoved = hasAdd && hasRemove;
+ if (itemMoved)
+ {
+ var added = itemGroup.FirstOrDefault(log => log.State == GameInventoryChangelogState.Added);
+ var removed = itemGroup.FirstOrDefault(log => log.State == GameInventoryChangelogState.Removed);
+ if (added is null || removed is null) continue;
+
+ this.ItemMoved?.Invoke(removed.Item.ContainerType, removed.Item.InventorySlot, added.Item.ContainerType, added.Item.InventorySlot, added.Item);
+
+ Log.Verbose($"Item Moved {removed.Item.ContainerType}:{removed.Item.InventorySlot} -> {added.Item.ContainerType}:{added.Item.InventorySlot}: {added.Item.ItemId}");
+ }
+ }
+
+ var elapsed = performanceMonitor.Elapsed;
+
+ Log.Verbose($"Processing Time: {elapsed.Ticks}ticks :: {elapsed.TotalMilliseconds}ms");
+ }
+
+ private bool AnyListeners()
+ {
+ if (this.ItemMoved is not null) return true;
+ if (this.ItemRemoved is not null) return true;
+ if (this.ItemAdded is not null) return true;
+ if (this.ItemChanged is not null) return true;
+
+ return false;
+ }
+
+ private unsafe ReadOnlySpan GetItemsForInventory(GameInventoryType type)
+ {
+ var inventoryManager = InventoryManager.Instance();
+ if (inventoryManager is null) return ReadOnlySpan.Empty;
+
+ var inventory = inventoryManager->GetInventoryContainer((InventoryType)type);
+ if (inventory is null) return ReadOnlySpan.Empty;
+
+ return new ReadOnlySpan(inventory->Items, (int)inventory->Size);
+ }
+
+ private bool IsItemChanged(InventoryItem a, InventoryItem b)
+ {
+ if (a.Container != b.Container) return true; // Shouldn't be possible, but shouldn't hurt.
+ if (a.Slot != b.Slot) return true; // Shouldn't be possible, but shouldn't hurt.
+ if (a.ItemID != b.ItemID) return true;
+ if (a.Quantity != b.Quantity) return true;
+ if (a.Spiritbond != b.Spiritbond) return true;
+ if (a.Condition != b.Condition) return true;
+ if (a.Flags != b.Flags) return true;
+ if (a.CrafterContentID != b.CrafterContentID) return true;
+ if (this.IsMateriaChanged(a, b)) return true;
+ if (this.IsMateriaGradeChanged(a, b)) return true;
+ if (a.Stain != b.Stain) return true;
+ if (a.GlamourID != b.GlamourID) return true;
+
+ return false;
+ }
+
+ private unsafe bool IsMateriaChanged(InventoryItem a, InventoryItem b)
+ => new ReadOnlySpan(a.Materia, 5) == new ReadOnlySpan(b.Materia, 5);
+
+ private unsafe bool IsMateriaGradeChanged(InventoryItem a, InventoryItem b)
+ => new ReadOnlySpan(a.MateriaGrade, 5) == new ReadOnlySpan(b.MateriaGrade, 5);
+}
+
+///
+/// Plugin-scoped version of a GameInventory service.
+///
+[PluginInterface]
+[InterfaceVersion("1.0")]
+[ServiceManager.ScopedService]
+#pragma warning disable SA1015
+[ResolveVia]
+#pragma warning restore SA1015
+internal class GameInventoryPluginScoped : IDisposable, IServiceType, IGameInventory
+{
+ [ServiceManager.ServiceDependency]
+ private readonly GameInventory gameInventoryService = Service.Get();
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public GameInventoryPluginScoped()
+ {
+ this.gameInventoryService.ItemMoved += this.OnItemMovedForward;
+ this.gameInventoryService.ItemRemoved += this.OnItemRemovedForward;
+ this.gameInventoryService.ItemAdded += this.OnItemAddedForward;
+ this.gameInventoryService.ItemChanged += this.OnItemChangedForward;
+ }
+
+ ///
+ public event IGameInventory.OnItemMovedDelegate? ItemMoved;
+
+ ///
+ public event IGameInventory.OnItemRemovedDelegate? ItemRemoved;
+
+ ///
+ public event IGameInventory.OnItemAddedDelegate? ItemAdded;
+
+ ///
+ public event IGameInventory.OnItemChangedDelegate? ItemChanged;
+
+ ///
+ public void Dispose()
+ {
+ this.gameInventoryService.ItemMoved -= this.OnItemMovedForward;
+ this.gameInventoryService.ItemRemoved -= this.OnItemRemovedForward;
+ this.gameInventoryService.ItemAdded -= this.OnItemAddedForward;
+ this.gameInventoryService.ItemChanged -= this.OnItemChangedForward;
+
+ this.ItemMoved = null;
+ this.ItemRemoved = null;
+ this.ItemAdded = null;
+ this.ItemChanged = null;
+ }
+
+ private void OnItemMovedForward(GameInventoryType source, uint sourceSlot, GameInventoryType destination, uint destinationSlot, GameInventoryItem item)
+ => this.ItemMoved?.Invoke(source, sourceSlot, destination, destinationSlot, item);
+
+ private void OnItemRemovedForward(GameInventoryType source, uint sourceSlot, GameInventoryItem item)
+ => this.ItemRemoved?.Invoke(source, sourceSlot, item);
+
+ private void OnItemAddedForward(GameInventoryType destination, uint destinationSlot, GameInventoryItem item)
+ => this.ItemAdded?.Invoke(destination, destinationSlot, item);
+
+ private void OnItemChangedForward(GameInventoryType inventory, uint slot, GameInventoryItem item)
+ => this.ItemChanged?.Invoke(inventory, slot, item);
+}
diff --git a/Dalamud/Game/Inventory/GameInventoryChangelog.cs b/Dalamud/Game/Inventory/GameInventoryChangelog.cs
new file mode 100644
index 000000000..52ada81e0
--- /dev/null
+++ b/Dalamud/Game/Inventory/GameInventoryChangelog.cs
@@ -0,0 +1,28 @@
+namespace Dalamud.Game.Inventory;
+
+///
+/// Class representing an inventory item change event.
+///
+internal class GameInventoryItemChangelog
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Item state.
+ /// Item.
+ internal GameInventoryItemChangelog(GameInventoryChangelogState state, GameInventoryItem item)
+ {
+ this.State = state;
+ this.Item = item;
+ }
+
+ ///
+ /// Gets the state of this changelog event.
+ ///
+ internal GameInventoryChangelogState State { get; }
+
+ ///
+ /// Gets the item for this changelog event.
+ ///
+ internal GameInventoryItem Item { get; }
+}
diff --git a/Dalamud/Game/Inventory/GameInventoryChangelogState.cs b/Dalamud/Game/Inventory/GameInventoryChangelogState.cs
new file mode 100644
index 000000000..23e972419
--- /dev/null
+++ b/Dalamud/Game/Inventory/GameInventoryChangelogState.cs
@@ -0,0 +1,17 @@
+namespace Dalamud.Game.Inventory;
+
+///
+/// Class representing a item's changelog state.
+///
+internal enum GameInventoryChangelogState
+{
+ ///
+ /// Item was added to an inventory.
+ ///
+ Added,
+
+ ///
+ /// Item was removed from an inventory.
+ ///
+ Removed,
+}
diff --git a/Dalamud/Game/Inventory/GameInventoryItem.cs b/Dalamud/Game/Inventory/GameInventoryItem.cs
new file mode 100644
index 000000000..286104c43
--- /dev/null
+++ b/Dalamud/Game/Inventory/GameInventoryItem.cs
@@ -0,0 +1,98 @@
+using System.Runtime.CompilerServices;
+
+using FFXIVClientStructs.FFXIV.Client.Game;
+
+namespace Dalamud.Game.Inventory;
+
+///
+/// Dalamud wrapper around a ClientStructs InventoryItem.
+///
+public unsafe class GameInventoryItem
+{
+ private InventoryItem internalItem;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Inventory item to wrap.
+ internal GameInventoryItem(InventoryItem item)
+ {
+ this.internalItem = item;
+ }
+
+ ///
+ /// Gets the container inventory type.
+ ///
+ public GameInventoryType ContainerType => (GameInventoryType)this.internalItem.Container;
+
+ ///
+ /// Gets the inventory slot index this item is in.
+ ///
+ public uint InventorySlot => (uint)this.internalItem.Slot;
+
+ ///
+ /// Gets the item id.
+ ///
+ public uint ItemId => this.internalItem.ItemID;
+
+ ///
+ /// Gets the quantity of items in this item stack.
+ ///
+ public uint Quantity => this.internalItem.Quantity;
+
+ ///
+ /// Gets the spiritbond of this item.
+ ///
+ public uint Spiritbond => this.internalItem.Spiritbond;
+
+ ///
+ /// Gets the repair condition of this item.
+ ///
+ public uint Condition => this.internalItem.Condition;
+
+ ///
+ /// Gets a value indicating whether the item is High Quality.
+ ///
+ public bool IsHq => this.internalItem.Flags.HasFlag(InventoryItem.ItemFlags.HQ);
+
+ ///
+ /// Gets a value indicating whether the item has a company crest applied.
+ ///
+ public bool IsCompanyCrestApplied => this.internalItem.Flags.HasFlag(InventoryItem.ItemFlags.CompanyCrestApplied);
+
+ ///
+ /// Gets a value indicating whether the item is a relic.
+ ///
+ public bool IsRelic => this.internalItem.Flags.HasFlag(InventoryItem.ItemFlags.Relic);
+
+ ///
+ /// Gets a value indicating whether the is a collectable.
+ ///
+ public bool IsCollectable => this.internalItem.Flags.HasFlag(InventoryItem.ItemFlags.Collectable);
+
+ ///
+ /// Gets the array of materia types.
+ ///
+ public ReadOnlySpan Materia => new(Unsafe.AsPointer(ref this.internalItem.Materia[0]), 5);
+
+ ///
+ /// Gets the array of materia grades.
+ ///
+ public ReadOnlySpan MateriaGrade => new(Unsafe.AsPointer(ref this.internalItem.MateriaGrade[0]), 5);
+
+ ///
+ /// Gets the color used for this item.
+ ///
+ public byte Stain => this.internalItem.Stain;
+
+ ///
+ /// Gets the glamour id for this item.
+ ///
+ public uint GlmaourId => this.internalItem.GlamourID;
+
+ ///
+ /// Gets the items crafter's content id.
+ /// NOTE: I'm not sure if this is a good idea to include or not in the dalamud api. Marked internal for now.
+ ///
+ internal ulong CrafterContentId => this.internalItem.CrafterContentID;
+}
diff --git a/Dalamud/Game/Inventory/GameInventoryType.cs b/Dalamud/Game/Inventory/GameInventoryType.cs
new file mode 100644
index 000000000..733af32d3
--- /dev/null
+++ b/Dalamud/Game/Inventory/GameInventoryType.cs
@@ -0,0 +1,351 @@
+namespace Dalamud.Game.Inventory;
+
+///
+/// Enum representing various player inventories.
+///
+public enum GameInventoryType : uint
+{
+ ///
+ /// First panel of main player inventory.
+ ///
+ Inventory1 = 0,
+
+ ///
+ /// Second panel of main player inventory.
+ ///
+ Inventory2 = 1,
+
+ ///
+ /// Third panel of main player inventory.
+ ///
+ Inventory3 = 2,
+
+ ///
+ /// Fourth panel of main player inventory.
+ ///
+ Inventory4 = 3,
+
+ ///
+ /// Items that are currently equipped by the player.
+ ///
+ EquippedItems = 1000,
+
+ ///
+ /// Player currency container.
+ /// ie, gil, serpent seals, sacks of nuts.
+ ///
+ Currency = 2000,
+
+ ///
+ /// Crystal container.
+ ///
+ Crystals = 2001,
+
+ ///
+ /// Mail container.
+ ///
+ Mail = 2003,
+
+ ///
+ /// Key item container.
+ ///
+ KeyItems = 2004,
+
+ ///
+ /// Quest item hand-in inventory.
+ ///
+ HandIn = 2005,
+
+ ///
+ /// DamagedGear container.
+ ///
+ DamagedGear = 2007,
+
+ ///
+ /// Examine window container.
+ ///
+ Examine = 2009,
+
+ ///
+ /// Doman Enclave Reconstruction Reclamation Box.
+ ///
+ ReconstructionBuyback = 2013,
+
+ ///
+ /// Armory off-hand weapon container.
+ ///
+ ArmoryOffHand = 3200,
+
+ ///
+ /// Armory head container.
+ ///
+ ArmoryHead = 3201,
+
+ ///
+ /// Armory body container.
+ ///
+ ArmoryBody = 3202,
+
+ ///
+ /// Armory hand/gloves container.
+ ///
+ ArmoryHands = 3203,
+
+ ///
+ /// Armory waist container.
+ ///
+ /// This container should be unused as belt items were removed from the game in Shadowbringers.
+ ///
+ ///
+ ArmoryWaist = 3204,
+
+ ///
+ /// Armory legs/pants/skirt container.
+ ///
+ ArmoryLegs = 3205,
+
+ ///
+ /// Armory feet/boots/shoes container.
+ ///
+ ArmoryFeets = 3206,
+
+ ///
+ /// Armory earring container.
+ ///
+ ArmoryEar = 3207,
+
+ ///
+ /// Armory necklace container.
+ ///
+ ArmoryNeck = 3208,
+
+ ///
+ /// Armory bracelet container.
+ ///
+ ArmoryWrist = 3209,
+
+ ///
+ /// Armory ring container.
+ ///
+ ArmoryRings = 3300,
+
+ ///
+ /// Armory soul crystal container.
+ ///
+ ArmorySoulCrystal = 3400,
+
+ ///
+ /// Armory main-hand weapon container.
+ ///
+ ArmoryMainHand = 3500,
+
+ ///
+ /// First panel of saddelbag inventory.
+ ///
+ SaddleBag1 = 4000,
+
+ ///
+ /// Second panel of Saddlebag inventory.
+ ///
+ SaddleBag2 = 4001,
+
+ ///
+ /// First panel of premium saddlebag inventory.
+ ///
+ PremiumSaddleBag1 = 4100,
+
+ ///
+ /// Second panel of premium saddlebag inventory.
+ ///
+ PremiumSaddleBag2 = 4101,
+
+ ///
+ /// First panel of retainer inventory.
+ ///
+ RetainerPage1 = 10000,
+
+ ///
+ /// Second panel of retainer inventory.
+ ///
+ RetainerPage2 = 10001,
+
+ ///
+ /// Third panel of retainer inventory.
+ ///
+ RetainerPage3 = 10002,
+
+ ///
+ /// Fourth panel of retainer inventory.
+ ///
+ RetainerPage4 = 10003,
+
+ ///
+ /// Fifth panel of retainer inventory.
+ ///
+ RetainerPage5 = 10004,
+
+ ///
+ /// Sixth panel of retainer inventory.
+ ///
+ RetainerPage6 = 10005,
+
+ ///
+ /// Seventh panel of retainer inventory.
+ ///
+ RetainerPage7 = 10006,
+
+ ///
+ /// Retainer equipment container.
+ ///
+ RetainerEquippedItems = 11000,
+
+ ///
+ /// Retainer currency container.
+ ///
+ RetainerGil = 12000,
+
+ ///
+ /// Retainer crystal container.
+ ///
+ RetainerCrystals = 12001,
+
+ ///
+ /// Retainer market item container.
+ ///
+ RetainerMarket = 12002,
+
+ ///
+ /// First panel of Free Company inventory.
+ ///
+ FreeCompanyPage1 = 20000,
+
+ ///
+ /// Second panel of Free Company inventory.
+ ///
+ FreeCompanyPage2 = 20001,
+
+ ///
+ /// Third panel of Free Company inventory.
+ ///
+ FreeCompanyPage3 = 20002,
+
+ ///
+ /// Fourth panel of Free Company inventory.
+ ///
+ FreeCompanyPage4 = 20003,
+
+ ///
+ /// Fifth panel of Free Company inventory.
+ ///
+ FreeCompanyPage5 = 20004,
+
+ ///
+ /// Free Company currency container.
+ ///
+ FreeCompanyGil = 22000,
+
+ ///
+ /// Free Company crystal container.
+ ///
+ FreeCompanyCrystals = 22001,
+
+ ///
+ /// Housing exterior appearance container.
+ ///
+ HousingExteriorAppearance = 25000,
+
+ ///
+ /// Housing exterior placed items container.
+ ///
+ HousingExteriorPlacedItems = 25001,
+
+ ///
+ /// Housing interior appearance container.
+ ///
+ HousingInteriorAppearance = 25002,
+
+ ///
+ /// First panel of housing interior inventory.
+ ///
+ HousingInteriorPlacedItems1 = 25003,
+
+ ///
+ /// Second panel of housing interior inventory.
+ ///
+ HousingInteriorPlacedItems2 = 25004,
+
+ ///
+ /// Third panel of housing interior inventory.
+ ///
+ HousingInteriorPlacedItems3 = 25005,
+
+ ///
+ /// Fourth panel of housing interior inventory.
+ ///
+ HousingInteriorPlacedItems4 = 25006,
+
+ ///
+ /// Fifth panel of housing interior inventory.
+ ///
+ HousingInteriorPlacedItems5 = 25007,
+
+ ///
+ /// Sixth panel of housing interior inventory.
+ ///
+ HousingInteriorPlacedItems6 = 25008,
+
+ ///
+ /// Seventh panel of housing interior inventory.
+ ///
+ HousingInteriorPlacedItems7 = 25009,
+
+ ///
+ /// Eighth panel of housing interior inventory.
+ ///
+ HousingInteriorPlacedItems8 = 25010,
+
+ ///
+ /// Housing exterior storeroom inventory.
+ ///
+ HousingExteriorStoreroom = 27000,
+
+ ///
+ /// First panel of housing interior storeroom inventory.
+ ///
+ HousingInteriorStoreroom1 = 27001,
+
+ ///
+ /// Second panel of housing interior storeroom inventory.
+ ///
+ HousingInteriorStoreroom2 = 27002,
+
+ ///
+ /// Third panel of housing interior storeroom inventory.
+ ///
+ HousingInteriorStoreroom3 = 27003,
+
+ ///
+ /// Fourth panel of housing interior storeroom inventory.
+ ///
+ HousingInteriorStoreroom4 = 27004,
+
+ ///
+ /// Fifth panel of housing interior storeroom inventory.
+ ///
+ HousingInteriorStoreroom5 = 27005,
+
+ ///
+ /// Sixth panel of housing interior storeroom inventory.
+ ///
+ HousingInteriorStoreroom6 = 27006,
+
+ ///
+ /// Seventh panel of housing interior storeroom inventory.
+ ///
+ HousingInteriorStoreroom7 = 27007,
+
+ ///
+ /// Eighth panel of housing interior storeroom inventory.
+ ///
+ HousingInteriorStoreroom8 = 27008,
+}
diff --git a/Dalamud/Plugin/Services/IGameInventory.cs b/Dalamud/Plugin/Services/IGameInventory.cs
new file mode 100644
index 000000000..0e796e8d8
--- /dev/null
+++ b/Dalamud/Plugin/Services/IGameInventory.cs
@@ -0,0 +1,69 @@
+using Dalamud.Game.Inventory;
+
+namespace Dalamud.Plugin.Services;
+
+///
+/// This class provides events for the in-game inventory.
+///
+public interface IGameInventory
+{
+ ///
+ /// Delegate function for when an item is moved from one inventory to the next.
+ ///
+ /// Which inventory the item was moved from.
+ /// The slot this item was moved from.
+ /// Which inventory the item was moved to.
+ /// The slot this item was moved to.
+ /// The item moved.
+ public delegate void OnItemMovedDelegate(GameInventoryType source, uint sourceSlot, GameInventoryType destination, uint destinationSlot, GameInventoryItem item);
+
+ ///
+ /// Delegate function for when an item is removed from an inventory.
+ ///
+ /// Which inventory the item was removed from.
+ /// The slot this item was removed from.
+ /// The item removed.
+ public delegate void OnItemRemovedDelegate(GameInventoryType source, uint sourceSlot, GameInventoryItem item);
+
+ ///
+ /// Delegate function for when an item is added to an inventory.
+ ///
+ /// Which inventory the item was added to.
+ /// The slot this item was added to.
+ /// The item added.
+ public delegate void OnItemAddedDelegate(GameInventoryType destination, uint destinationSlot, GameInventoryItem item);
+
+ ///
+ /// Delegate function for when an items properties are changed.
+ ///
+ /// Which inventory the item that was changed is in.
+ /// The slot the item that was changed is in.
+ /// The item changed.
+ public delegate void OnItemChangedDelegate(GameInventoryType inventory, uint slot, GameInventoryItem item);
+
+ ///
+ /// Event that is fired when an item is moved from one inventory to another.
+ ///
+ public event OnItemMovedDelegate ItemMoved;
+
+ ///
+ /// Event that is fired when an item is removed from one inventory.
+ ///
+ ///
+ /// This event will also be fired when an item is moved from one inventory to another.
+ ///
+ public event OnItemRemovedDelegate ItemRemoved;
+
+ ///
+ /// Event that is fired when an item is added to one inventory.
+ ///
+ ///
+ /// This event will also be fired when an item is moved from one inventory to another.
+ ///
+ public event OnItemAddedDelegate ItemAdded;
+
+ ///
+ /// Event that is fired when an items properties are changed.
+ ///
+ public event OnItemChangedDelegate ItemChanged;
+}