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.

 

You may also like...

24 Responses

  1. parafusion says:

    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.

    • Hime says:

      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.

Leave a Reply

Your email address will not be published. Required fields are marked *