Now that we’ve got a few entities that the player will be able to pick up and collect, we need a way for the player (and others) to store them. The simplest method to do this is to just give whatever needs to store items a few arrays (or std::vectors, more likely) and let them manage the entities. The problem with this is there’ll be a lot of repeated code, and it isn’t very elegant. Instead what we’ll do is create an Inventory class that manages any number of entities derived from and including Item—henceforth all called items—in a manner similar to the EntityManager. Instead of using an std::map, we will use an std::list of std::pairs, where each pair contains a pointer to the item (in the EntityManager) and a quantity. We’ll also create a selection of helper functions which make it easier to manage the Inventory instead of working directly with the std::list.

Every time we need to represent multiple items, we’ll use the Inventory class; the items scattered about a room, in a treasure chest, or held by the player. We’ll want a few functions to make life easier for us when dealing with the Inventorys, namely functions to add and remove items, check if an inventory contains and item and if so how many, and a function to merge two inventories together.

Now brace yourself, there are a lot of functions in this class and I’m going to give all their declarations to you in one go!

The comments should make most things clear, but basic usage of the Inventory class is as follows; first either the empty constructor or the JSON constructor are called. If the JSON constructor is called, the value is taken to have three keys—"items", "weapons", and "armor"—which are then loaded by the load function. The Inventory is then queried using the get, and count functions, and modified using add, remove, and merge. It’s contents can be displayed on a per-type basis using the print function template, or all together using the print function, and then the Inventory can be converted back to a JSON representation using getJson (which calls jsonArray on each item type).

Firstly we have the load function and the headers.

Before we understand the load function it makes sense to see the syntax of a JSON representation of an Inventory.

The load function operates on an individual key—either "items", "weapons", or "armor"—and so assumes that the JsonBox::Value is an array, where each element of that is another array containing the item id and its quantity, in that order. load therefore converts v to an array and iterates over each of its elements (which will be JsonBox::Values). For each element it converts it to an array and extracts the itemId and quantity from the relevant positions. In one step it then uses std::make_pair and EntityManager::getEntity to obtain a pointer to the item with that id and construct a new std::pair<Item*, int> containing that pointer and the quantity. This std::pair is then appended to the end of the item list.

To go in the opposite direction and convert the inventory to a JsonBox::Array we use the jsonArray function, which iterates over all the items in the item list and for each item constructs a new array which it stores the id and quantity of that item in. This array is then appended to the main array, which is returned when all items have been added. Note that because of the structure of the JSON file, if we want to properly output an Inventory we have to be able to output Items, Weapons, and Armor separately. Hence this function accepts a template argument T which it compares each item’s id against in order to confirm that they are the correct type (like the EntityManager does). If they aren’t it ignores then and continues to the next element.

To output the entire inventory we use the getJson function, which simply calls jsonArray for each each item type and creates a new JsonBox::Object from them.

add takes a pointer to an Item and a quantity and checks to see if the inventory already contains an item with the same id as the given Item pointer. If it does it increases that item’s quantity by the given value and then returns, otherwise the inventory doesn’t contain the item yet and so it creates a new std::pair<Item*, int> and adds it to the item list. C++ implicitly converts pointers to derived pointers to pointers to base classes (no need for dynamic_cast) and so making this function accept Item* instead of T* and converting it is perfectly fine.

remove is slightly more complicated, because in order to delete an element of an std::list we have to use the std::list::erase function, which takes an iterator as an argument. Iterators are similar to pointers in that they represent a location, but they’re restricted to a single data structure—in this case the item list—instead of an arbitrary location.

Whilst a range-for loop like in the add function has a nice syntax, it doesn’t give us an iterator, and so we use an uglier for loop which does. Iterators also don’t give us direct element access via -> like pointers do, so we have to use the indirection operator * and then . instead. If the id matches then we decrease the quantity of that item by the count and remove it from the list if its new quantity is non-positive. Note that we aren’t using unsigned ints for the item quantity (because JsonBox only supports int), and so we don’t have any risk of overflow here.

The get function returns a T* pointer to the nth element in the item list that is of the same type as T. This function is quite complicated because we’ve used an std::list—which doesn’t have random element access—instead of an std::vector—which does. We therefore can’t just access the nth element, and must step through using iterators instead. Having said that, because we want the nthe element of a given type, and not the nth element overall, there’s no escaping this even with an std::vector.

We iterate over each element in the list, and increment i every time we find a item of type T. When we have found n such items, we break from the loop, at which point it will be an iterator to the nth element, or will be an iterator past the end of the list (std::list::end) if no element was found. We then return either the pointer to the item or nullptr accordingly.

The two count functions are much simpler. The first returns the quantity of item that the inventory contains by finding the first (and only) std::pair in the item list whose first element has the same id as item. The second uses the get function to find a pointer of type T* to the nth item, which is then implicitly converted to an Item* pointer before being sent to the first count function. Note the difference here; the first count returns the quantity for any item, including those that aren’t in the inventory, whereas the second returns the count for the nth element of a given type, not the nth element overall. You could easily overload the function to do that though.

Next we have the two print functions which format and output the names and descriptions of the items in the inventory. The first (templated) function only prints an item if it has the same type as T, and has an optional feature to numerically label each item. By using this label in conjunction with the templated get we’ll be able to create a simple user interface for managing items, where the player selects an item based on its number. The second print function prints all the different item types in sequence, or "Nothing" if the inventory is empty. Both functions also return the number of items outputted, which will come in handy later.

Finally we have the clear and merge functions, and the JSON constructor. All of these are pretty self-explanatory in how they work; clear removes all the items from the inventory, merge takes the items in one inventory and adds (not moves) them to the calling one, and the constructor uses load to load the inventory from the JsonBox::Value.

Right at the end of the file we have the explicit instantiations of the template functions for each item type, and with that the inventory system is done!