Understanding the Item Inventory and Design Considerations
This article examines the way the party inventory is implemented in the default engine. I look at the various functions that are provided, and then I discuss why it is not a very good design and how it can be improved.
Item Inventory Overview
From a general point-of-view, an item inventory will contain information regarding items, weapons, and armors that it holds. It should provide a set of methods for managing the inventory.
An inventory should provide the following methods
- Storing information about items in the inventory
- Get how many of an item that you have
- Check whether an item exists in the inventory
- Add an item to the inventory (eg: you bought something)
- Remove an item from the inventory (eg: you dropped it)
- Use an item in the inventory (eg: you drank a potion)
The Default Inventory
In the default engine, only the party has an item inventory, and so I call it the default inventory. Let’s look at how the default inventory handles each method that I’ve outlined above.
Item Containers
Each set of items is stored as a hash (or “container” I guess), where the key is the ID of the item and the value is an integer that represents how much of the item that the party has. You can see how the inventory is initialized in Game_Party
def init_all_items @items = {} @weapons = {} @armors = {} end
Suppose you have 5 potions (ID 1) and 3 Hi-Potions (ID 3). Your items hash would look like
{ 1 => 5, 3 => 3 }
Now, if you wanted to look up a particular item, you’d have to know which container to look in. This is why the following method is provided
def item_container(item_class) return @items if item_class == RPG::Item return @weapons if item_class == RPG::Weapon return @armors if item_class == RPG::Armor return nil end
This allows you to retrieve the appropriate container depending on the type of item you have. However, this is questionably implemented. Why not just check the class directly via is_a?
Anyways…
Counting Items
If you want to know how many potions you have, you simply grab the ID of the potion and then look it up in the items hash. The default inventory provides this as the item_number
method:
def item_number(item) container = item_container(item.class) container ? container[item.id] || 0 : 0 end
You can see the item_container
method being used here, which makes it pretty nice.
Of course this runs into the problem where it assumes all potions are the same, which we address using concepts like Instance Items. Granted, that script uses the same technique for counting items, so using a hash for the purposes of counting items is pretty effective.
Checking Item Exists in Inventory
You may want to know whether the party has an item or not. This is the same as asking “Do I have at least 1 of the item?” and you can simply use the item counting method shown previously to do this. In the default scripts, this is implemented as has_item?
:
def has_item?(item, include_equip = false) return true if item_number(item) > 0 return include_equip ? members_equip_include?(item) : false end
Now, something of interest is the include_equip
flag. Basically, there are two cases when it comes to checking if you have an item or not
- Is it in your bag?
- Are you holding it?
Depending on your requirements, it may be enough for you to hold it in your hands. In other cases, you only want to know whether it’s in your bag or not. This is what this flag is for.
Item Limits
Your inventory may have a limit. The default inventory has imposed a limit of 99 for every item. This is defined in the following method
def max_item_number(item) return 99 end
And you can check whether you’ve hit the limit or not using this method
def item_max?(item) item_number(item) >= max_item_number(item) end
Note that the default inventory does not have any concept of “total items”. If you had 5 Potions and 3 Hi-Potions, then that’s all you know; there isn’t a method that tells you that you have 8 items in total. Consequently, there is no support for preventing you from going over -capacity. This is custom logic that must be added.
Gaining and Losing Items
When you pick up a new potion, you want to add it to your inventory. The default inventory defines this as
def gain_item(item, amount, include_equip = false) container = item_container(item.class) return unless container last_number = item_number(item) new_number = last_number + amount container[item.id] = [[new_number, 0].max, max_item_number(item)].min container.delete(item.id) if container[item.id] == 0 if include_equip && new_number < 0 discard_members_equip(item, -new_number) end $game_map.need_refresh = true end
The same method is used for both gaining and losing items, which is why there’s logic for deleting items even though you are supposedly only gaining items. You can verify this
def lose_item(item, amount, include_equip = false) gain_item(item, -amount, include_equip) end
The implementation is straightforward: increase or decrease the item count by the given amount, and delete the entry if necessary. Again, removing an item from the inventory may involve removing items that the party members are currently holding, which is what the include_equip
flag is for.
Using Items
Lastly, if a party member takes a potion from the inventory and uses it, you want to update the item count. The default inventory provides this as follows:
def consume_item(item) lose_item(item, 1) if item.is_a?(RPG::Item) && item.consumable end
It assumes you only take out one item at a time, but I guess that assumption is ok for most purposes.
Summarizing the Default Inventory
You have seen all of the methods for working with the inventory. Here is a recap on each method:
- `item_number(item)` – get a count of the item
- `has_item?(item, include_equip=false)` – check whether item exists
- `item_max?(item)` – total you can have for the item
- `gain_item(item, amount, include_equip = false)` – gain an item
- `lose_item(item, amount, include_equip = false)` – lose an item
Analyzing the Default Inventory
The default inventory provides the bare minimum requirements for implementing an item inventory. As shown above, it holds information about all the items that your party has and provides various methods for managing the inventory. It even provides additional functionality such as limiting how many of each item you can have, although it assumes you can only have 99, which is an arbitrary number at best.
It would not be difficult to extend it to support additional functionality, such as getting the total of all items in the inventory which you can use to limit how many items you can have at any time, or implementing item weights.
Thinking Beyond the Party
I started this article by defining the item inventory as an abstract entity that simply stores information about items it holds. I then defined a number of methods that an inventory should provide for managing the inventory.
Now, an inventory itself has no tangible form. It can be represented as a bag, a chest, a warehouse, or anything that holds items, weapons, or armors. In our party, presumably it is something magical that they carry around with them, but other than that, it is just another inventory.
There is no reason why inventory methods should be implemented specifically in the party, besides the fact that the designers likely only intended the party to have an inventory. But this isn’t a very good reason either.
Applications of a Stand-alone Inventory
In object-oriented design, we can consider the party and the party’s inventory to be two separate objects. The party effectively contains an inventory. If we pulled all of the inventory management methods out of the party and into its own Game_Inventory
class, we can now simply initialize an inventory object in the party. This opens a number of new possibilities that would have been unnecessarily complex in the default design.
Multiple Inventories
Indeed, a party can have multiple inventories. If each inventory was stored as its own object, then switching inventories is a matter of switching objects.
Actor inventories
In some cases, it doesn’t make sense for everyone to access a shared inventory. Instead, each actor would have their own little bag of goods that they hold. With a general inventory object, we can simply give each actor their own inventory. And of course, they can swap inventories with each other if they want.
Chests
Now, when you open a chest, you don’t always have to take the items in the chest. Nor do you always have to take all of the items. If you could create a chest and give it an inventory, you could then look at the contents of the chest as if it were any other inventory and pick which ones you want to take.
Storages
A fairly common idea: you have a storage where you place excess items that you don’t want to hold on you (either because you can’t, or you want to keep it safe from pickpockets). With a stand-alone inventory object, implementing a storage is a matter of putting an inventory object somewhere and accessing it.
Unified Inventory Scene
All of the different use cases discussed above may involve an inventory at some point. Because the same inventory object is used in every case, we could simply create one inventory scene to handle every case. We might have child scenes that implement additional functionality or to change the look-and-feel, but for the most part, the amount of work that is required to work with different inventories is basically the same no matter how many different applications you have.
The Core Inventory
Given all of the information above, I have implemented my own stand-alone inventory in this script
I have also provided an implementation of actor inventories using this stand-alone inventory for reference. It is an example of how you can extend your inventory in your project without having to do too much work.
Closing
Hopefully at this point, you will understand how the inventory in RPG Maker works. If you project requires more specialized inventory functionality, you should be able to determine where the logic should be added.
Spread the Word
If you liked the post and feel that others could benefit from it, consider tweeting it or sharing it on whichever social media channels that you use. You can also follow @HimeWorks on Twitter or like my Facebook page to get the latest updates or suggest topics that you would like to read about.
Inventory has always been a weak point of the RPG Maker projects due to the limitations of the vanilla engine. As you have outlined above, character inventory, chests and storages are critical for increasing immersion into the in-game universe for the players, and it is these sort of little details that help a project go above and beyond. I have personally had the opportunity to browse through various scripts specifically pertaining to inventories. There are probably around 5 to 6 instance item scripts, as well as various scripts that provides the option of having storage containers and other custom inventory scene scripts. The problem here, the prospect of trying to implement various inventory script together is often seen as too daunting of a task for devs. I was just thinking, building on top of this core engine, if we could somehow develop a complete suite of inventory management system that includes the various functions you’ve outlined above, it would help many aspiring devs as they no longer need to search through various scripts and test them for compatibilities. That being said, I recognize that this is by no means an easy task, so I am just throwing ideas out there. Once again, excellent article, and definitely worth a read for RPG Maker developers.
Inventory management has always been an interesting problem for me. Especially since I have plans to develop various inventory related scripts. For example, I like the idea of having a grid inventory system, and I’d like to extend that to chests. With a unified inventory system, it would be much easier for me to create the scripts.