Our inventory needs the following functions:
Let's also define our item structure for this example. A separate article will be added to explain possible item requirements and structure.
struct Item
{
public int ID; //Can be used to gather information about item
public int Durability;
public int Quantity;
}
Since this example is using an inventory with a fixed size and items can be moved from slot to slot, an array will be used. Let's lay out the outline of our Inventory class.
public class Inventory
{
private Item[] ItemSlots;
public Inventory(int slotCount)
{
//validate parameters
ItemSlots = new Item[slotCount];
}
public bool AddItem(Item item)
{
//if stackable item and stack is not full
// add to existing stack
//else if empty slot available
// add to empty slot
//else
// we failed to add
return false;
}
public bool RemoveItem(Item item)
{
//if the inventory contains the item
// remove requested number
//else
// we failed to remove
return false;
}
public ReadOnlyCollection<Item> GetItems()
{
//return items while preventing modification of calling code
return Array.AsReadOnly(ItemSlots);
}
}
Some basic code has been added to allow it to build (hopefully). Functions have been stubbed out and return false or null until they are implemented. For further information on Array.AsReadOnly, see this link.
This should be straightforward using an array. If stackable, iterate until the first non-full slot containing the same item is found. If not stackable, then find the first empty slot.
public bool AddItem(Item item)
{
bool isStackable = ItemTable.IsItemStackable(item);
int firstEmptySlot = -1;
for (int i = 0; i < ItemSlots.Length; ++i)
{
//Let's use ID of 0 to signify an empty slot
if (ItemSlots[i].ID == 0)
{
if (firstEmptySlot == -1)
{
if (!isStackable)
{
ItemSlots[i] = item;
return true;
}
firstEmptySlot = i;
}
}
else if (ItemSlots[i].ID == item.ID)
{
//found our item
if (isStackable)
{
ItemSlots[i].Quantity += item.Quantity;
return true;
}
}
}
//Iterated all slots
if (firstEmptySlot > -1)
{
ItemSlots[firstEmptySlot] = item;
}
}
For this function slots should be iterated until the requested count of items have been found. Once they are all located they can be removed. Otherwise the function will return false to signal that it has failed.
After we locate the requested quantity of items, we will have to go through the slots once more to remove the items. To prevent unnecessary iteration, let's just go in reverse once we are ready.
public bool RemoveItem(Item item)
{
int i = 0;
int numFound = 0;
while (i < ItemSlots.Length)
{
//Assume Item equality operator has been overidden
if (ItemSlots[i] == item)
{
numFound += ItemSlots[i].Quantity;
}
if (numFound >= item.Quantity)
{
//Start moving backwards, removing the previously found items
while (i > -1)
{
if (ItemSlots[i] == item)
{
if (ItemSlots[i].Quantity > item.Quantity)
{
ItemSlots[i].Quantity -= item.Quantity;
return true;
}
else
{
item.Quantity -= ItemSlots[i].Quantity;
//Clear out item slot
ItemSlots[i].ID = 0;
ItemSlots[i].Durability = 0;
ItemSlots[i].Quantity = 0;
if (item.Quantity == 0) return true;
}
}
--i;
}
//Error in code if this point is reached
}
++i;
}
return false;
}
For multi-threaded context, locking in the AddItem and RemoveItem functions is necessary.
For multi-player context, this code would run on the server to prevent cheating.
Item equality operator and ItemTable.IsItemStackable will be implemented in future posts.
Please experiment with your own implementations.