Inventory System Tutorial
This page gives an overview of the factors needed to design a player inventory system with HeroEngine.
Inventory is a fundamental concept in MMOs. Still, HeroEngine does not implement the mechanics of Inventory for you. This may seem odd, or perhaps an oversight but there is in fact a very good reason for it: How any given game handles inventory can vary quite a bit in both detail and approach. There are as many ways to deal with items and inventory in a game as there are games. In fact, some games like City of Heroes (tm) originally did not have any notion of inventory at all! Essentially, there is no "one size fits all" implementation of Inventory (or combat, or skills, or anything else in a MMO game design). Therefore, HeroEngine makes no attempt to implement a game design. Rather, it provides the foundation, tools and capabilities to allow your team to implement whatever your designers imagine.
HeroEngine, using the power of the DOM and HSL, lets you define exactly how your inventory system works. But, make no mistake, this is a complex system to develop and relies on careful planning to make sure it is done right. In most game designs, most game systems rely heavily on the details of how inventory and items work.
It is impossible to discuss the nature of inventory in a vacuum. Inventory touches on the details of items, GUI controls, client/server data exchange and more. So when discussing inventory, we will also be discussing the nature of items and how they are represented at all levels. This will, by necessity, branch to other tutorials from time to time.
Hero's Journey example
We will use Hero's Journey as an reference implementation. Hero's Journey has, in many ways, a fairly typical inventory system: Your character acquires items (from loot, purchase or trade) and these appear in the inventory. The items can be used to enhance the character, can be bought and sold and more. In other ways, HJ's inventory is atypical: we don't have items that represent containers in the traditional sense. This was a design decision based on a desire to simplify the inventory management process for the user. However, nothing about HeroEngine prevents the implementation of a full container-based inventory system either. In fact, it would be easy to extend Hero's Journey's implementation of items and inventory to support this, should our design team change their minds.
Note: Hero's Journey has evolved quite a bit since this documentation was written, so the actual code may not match precisely but the concepts are generally valid.
Items and Inventory can be very complex, depending on your design. A typical MMO (such as EQ or WoW) has what at first may appear to be a simple system for items; but don't be fooled, because there is a lot going on. Here is an annotated screen shot from Hero's Journey that shows a bit of the complexity we must master:
In this image, we see that our character has an inventory window with many items in it; these are represented by icons. He is also holding a Battle Axe which is represented in 3D space by a model. The Axe also appears both in the character portrait window, and a separate item detail window that has been "torn off" from another window.
So this Axe is now represented visually multiple times in this one scene, both as icons and as a 3D model. But looking closer, the icons themselves may not even be equivalent. Our user, the player, probably expects that at a minimum, the icon in the character portrait can be manipulated (drag-n-drop to move back into inventory or get rid of it). But what about the icon on the detail window? Can that one be manipulated? So, is there a right-click action that makes sense on the icons? And is it the same for all icons?
And that's not all: Notice the ability bar in the lower right. It has icons as well. Are these icons the same as the item icons? Can the battle axe be dragged to the ability bar? Can the ability bar icons be dragged to inventory? Probably not, at least in Hero's Journey's game design.
Also in the lower left side of the screen, we can see that the player has started to create an in-game email to someone, and has dragged an item from his inventory into the email. This feature, common to many MMOs, allows a player to email an item to someone else. But in terms of the engine infrastructure, where does this item go when it is emailed? Where is it when it is in the GUI for the email that is being composed, but before it is actually sent? Is it still in inventory? Does it still affect the character? What happens if the email is canceled? Does the item return to inventory? But what if the inventory has filled up before the email was canceled and the item will no longer fit?
Can your inventory be represented in other ways? Such as a link that shows up in chat that opens a detail window on an item? Can it have 3D representation in the world other than being a held weapon? Clothing on a character uses an entirely different system too, so somehow your clothing and armor must drive your character parts in a well defined manner.
These are just a small sampling of the issues that have to be addressed in an item and inventory system. It is essential that the design team carefully maps out the user interactions that are expected, in order for the implementation teams to properly build suitable DOM representations that will achieve the design. Pay particular attention to the "edge conditions" where issues arise.
To have inventory at all, we need the notion of an Item. An item is something the player character can acquire. A character can have many items (up to some limit) and they persist (are saved with) with the character.
In Hero's Journey, as in most MMOs, there are many types of items. Therefore, it may be helpful to use a class hierarchy to represent items. For example:
- Things you can wear (armor)
- Things you can use (wands, scrolls, potions)
- Things that enhance your character ("Wyr" in the case of HJ) and more.
- There may also be items that are "special citizens" in that they behave differently, such as "quest items".
Some common steps in the design process:
- Define your item types
- Identify common elements
- Identify anything that is exceptional or special
- Establish your game rules for how items work (in general)
It is also possible that you may wish to identify every exact item that can exist in your game (rather than just their nature as outlined here). This is a valid design choice. Our own philosophy, however, is that our games are always adding new content, so we choose to define systems that allow GameMasters to add new items to the game frequently. So we do not set out to design each item separately, but instead a rather prototypical selection of them.
Data representation of items (server)
Clearly the concept of an item has to be represented in many ways. It exists on the server and the client. We'll focus on the server for the moment.
The first decision is how to structure the data object model for your items. There are several options to choose from:
- A class hierarchy
- A composited class (See: Glomming)
- A hybrid of a class hierarchy and composition
Class hierarchy representation for items
If your design is such that you have fully planned out the items and the capabilities they possess for your game, then representing your game's items in a rigid class hierarchy may be perfectly suitable. The representation of items by a class hierarchy is advantageous in that items are well defined, and the code you write can more often make valid assumptions about an item.
Ok, let's talk about the simple hierarchy illustrated here. At first glance, it looks like it might be something with which we could build our game. There is a reassuring structure to it where we can tell exactly what an item is and is capable of doing. Knowing something about Object Oriented Programming, we choose to create an
abstractItem class which all of our items will have as a parent class. Since we are talking about items for an MMO, we know we need to have some items that players equip, giving rise to the
equipableItem class which inherits from
abstractItem. Alrighty, lets see what else do we need...weapons and armor (
armorItem respectively) and now we are starting to have what looks like a game's items.
Now we start getting to the meat of the hierarchy which will be used to make actual items. We break down
armorItem into the various locations in which our characters can have armor.
HandArmor obviously is equipped on the hands, probably knows how to visualize itself on the character, and has some sort of armor rating. Things are looking pretty good.
But let's look at what happens when your designers want to create a type of armor that can also be used as a weapon(see right diagram). Hrmm, that looks a little odd doesn't it? What looked like a pretty sensible hierarchy is now folding back on itself. Now for HeroEngine this isn't a problem as long as we are just talking about field data, but if you are implementing code in the class methods scripts for your classes, then you are entering the territory of a classic problem in Object Oriented Programming, where multiple inheritance is supported. This is called the Diamond Problem.
Dealing with the Diamond Problem is generally manageable on a small scale, but in MMOs which typically undergo years of additional development after release, the problem grows as designers add more and more capabilities, which create more and more conflicts!
Another issue to consider with a class hierarchy for your item architecture, is that over time, the hierarchy tends to become extremely complex as designers create new items that require blending several existing items together. What starts as a reassuringly structured design, devolves into a complex structure in which it is difficult to determine where a new item belongs.
Our experience with our text-based MMOs (that have been running for over twenty years) is that a rigid class hierarchy tends to be less suited for an MMO with regular content updates as the expected lifespan of the product increases. The structure itself tends to either: a) limit what designers may create; or b) increase code complexity to handle the edge conditions (aka, the oddball cases).
Items and the Diamond Problem
Well, so what? Why do we care that
WeaponArmorItem inherits from
Let's assume you implement a equip() method in
equipableItem. When we attempt to call equip() and a
WeaponArmorItem, it is not clear if we mean to use the equip() method of the
ArmorItem or the
There are many different approaches taken by computer languages to deal with this issue. All have their limitations.
The approach we choose for this problem in HeroEngine, is that the DOM allows for multiple inheritance, but attempts to prevent the creation of method conflicts. It does this by checking the class method scripts for the classes prior to modifying inheritance, and the DOM disallows changes that would give rise to the Diamond Problem. For example, if a child class does not implement the methods to resolve the ambiguity, the change is rolled back. Additionally, fields are stored in a flat namespace so that the name field exists only once on a given object no matter how many parent classes have name as a member field. A consequence of the flat namespace for field names, is that your developers must either agree on exactly what the name field should represent for an object, or they create more verbose fieldnames to handle their more particular needs.
It is possible in HeroEngine to deliberately create a class hierarchy in which a diamond occurs. This can be done by establishing the hierarchy prior to implementing code for the classes, or implementing the conflicting method in a parent class at a later date. This way a runtime error will occur if the ambiguity is not explicitly handled, for example by implementing the equip() method in the child's class methods script.
Class composition representation for items
To understand how one might use class composition in your game's representation of items, lets consider a typical bicycle as an example. In order for bicycle to function it requires several major components; wheels, frame, pedals, chain, handle bars and a seat. Individually the components are not bicycles, it is only when you add them all together that you have a functioning machine. In Object Oriented Programming, we call the process of assembling several simple objects to form a more complex one object composition.
For those of you familiar with Design Patterns, you should be thinking Decorator Pattern. If you are not familiar with patterns, we recommend you read the original book on the topic Design Patterns: Elements of Reusable Object-Oriented Software or the more approachable Head First Design Patterns).
In an MMO, you might represent your items as a baseItem which are decorated/composited extending their functionality (GLOMing additional classes is HeroEngine's paradigm). Lets examine at what a sword in a prototypical MMO might look like; it must know how to be a "weapon", it must be able to be equiped, and often we want to bond it to a particular character. So instantiate an object from our fundamental item class baseItem, and composite/GLOM on three decorator classes (equipable, bonded and weapon) which combine to provide the functionality we want for the sword.
If at some later date the designers create a spell that when cast on a sword gives it a "use" ability to cast a fireball once per hour, we simply GLOM on the useable or a child class of useable that handles this new capability.
Object composition is often favored by many experienced MMO architects in their design of a game's items because of its ability to be flexible, easily adjusting to design changes that introduce or remove behaviors. This great flexibility is also the major disadvantage, items represented in this way can be rather amorphous in nature potentially requiring your game's developers adjust the way they approach their design of some game systems that interact with items. (For example, what does it mean if Inventory has an item "equipped" that subsequently has the equipable behavior removed?).
Additionally, the way you factory an item changes under this representation as you can no longer simply instantiate a weapon to get a functioning item, typically requiring a more complex process such as an implementation of the Factory Method Pattern.
Hybrid representation for items
A hybrid representation combines some variation of a minimalist class hierarchy, providing structure with a composition/decorator style for the more specific/unique behaviors for flexibility. The hybrid approach has some of the advantages and disadvantages of the two representations it uses.
In order to avoid the disadvantages of the class hierarchy approach, your designers should make some decisions up front that will not be subject to change. For example, deciding that "no item shall ever be both a weapon and armor", allows the developers to establish a hierarchy where weapon and armor are in different branches, with the assurance that they will not need to make a child that is derived from both weapon and armor.
Mutable vs. immutable data
Another key decision is to decide if your items will be entirely mutable data or not. In other words, will you maintain all of the information about "a broadsword" on the item's instance in the GOM itself. Say, this item's class ("Weapon") ultimately has 10 different fields (Name, damage, bonus, spelltype, etc.). Then every field of that exact item could be modified to make it different from every other "broadsword" out there. Very powerful and convenient, but also a lot of bloat.
This represents a "heavyweight" design for items, where each is entirely unique: The entire item is mutable data (data that can change) and so every item carries around the weight of all this data. Some MMOs use this approach, but it has consequences: Heavyweight items consume a lot of memory on the server (sometimes the client too), and cause problems with how quickly that items can be loaded and persisted to the database.
At the opposite end of the spectrum, is the "lightweight" item: In this system, somewhere there is a specification for what a broadsword is, how much damage they do, etc. And the inventory item itself merely "points to" that specification (or, in our parlance, "Spec"). This approach is very light, so it uses the minimum amount of memory and has the least impact on the database. These items are said to be made up completely of immutable data (data that can't change on a per item instance basis). As a further bonus, if it is determined at some point that a "broadsword" does too much damage, the designers can change a single field in the broadsword's spec, and all broadswords instantly decrease to the new '"nerf'd" amount of damage. With an entirely mutable heavyweight version, this is much more difficult to accomplish: somehow all broadswords' damage values have to be individually adjusted, such as when each character owner logs into the game.
But lightweight also has issues: What if your game design requires that you keep track of a certain amount of damage each weapon has taken, and after a certain amount of time the item breaks? If the item has no mutable data, there is no way to keep track of that.
So, one answer to this is to use a mixture of the two approaches:
- Identify data about items that is mutable, vs. data that is immutable.
- Establish a spec system to keep track of the immutable data, and from each spec, instance items that can hold mutable data, but these items also maintain a "pointer" back to their originating spec for the immutable information.
This is, in fact, the design used by Hero's Journey. And much of the underlying mechanisms for how these "Specs" are managed, is provided as a Required System called the Spec Oracle System. The Spec System allows you to define your own specification model (and you can have any number of different spec models for different game systems). It also provides a generic GUI framework so that your team can add/delete/modify specs. There are also facilities for transmission of spec data to clients (we'll deal with this in a bit).
The Spec System does not impose any particular structure on the data in your specs, nor how you manage the data of your items in particular. It also does not provide a mechanism for the items themselves. It only manages the specifications themselves, and the instancing of items from those specs. Nevertheless, the Spec System does provide a convenient mechanism to manage the immutable data and the relationships to the actual items derived from them. Thus, we will explore the Spec System as a primary building block for any inventory system.
- See also: Spec Oracle System
Data representation of items (client)
Once the server's representation has been designed, it is time to consider how you will represent items on the client. Some of the decisions you made about the data representation for an item may be reevaluated for the client, in light of its different requirements. For example, on the server the memory required to represent a single item is of significantly greater concern, since the server needs to be able to have thousands (tens of thousands) of them in memory at any one time. Whereas on the client, we probably need only to represent a fraction of those items, which allows for more memory per item.
For Hero's Journey, the server vs client representation of items is nearly identical with their immutable data represented by specs in our item spec system. For example, even if we wanted to be able to delay the loading of an item's spec until such a time as the user actually needed the immutable information for that item, we still needed some information immediately for the client's purposes. For the purposes of display, information such as the iconID was needed. So by including the iconID (which is normally stored in the spec, and not on the item itself) in the transmission of an inventory item to the client's cache, we were able to avoid having to immediately request the full specification be loaded from disc for every item received, whether or not the information was actually needed.
Some of the questions your design will need to answer are:
- When will item information be transmitted to the client? Or do you want to just make a new request each time you need the information?
- How can we make this transmission as efficient as possible?
- How is item information cached by the client?
- If you choose to cache item information, how/when does that cache get updated?
- How is the server notified when a client performs some action on its local representation of an item?
Useful background information to help you answer these questions is found in the Character Data Tutorial, which discusses one way of implementing a cache for character information...what items are in your inventory is something that logically should be cached with whatever character information you send the client.
By now you may be sick of hearing about Design Patterns, but there is another one that provides guidance on how you might choose to implement your client mechanics called the Model-View-Controller Pattern.
Let's examine how the Spec Oracle System is used in Hero's Journey to implement items:
Item Specification Oracles
- Main page: Spec Oracle System
|Caution: This page may contain information which is Hero's Journey-specific!|
Note: "Oracle" is used here to signify "all knowing", and has no relationship to the database product of the same name.
The design goals for Hero's Journey's Item Spec System were:
- Flexible system capable of growing with the design
- Efficient representation of items (memory/bandwidth)
- Ability to construct items via parameter driven UI
- Low technical coding requirement for the implementation of new behaviors
To accomplish these goals, we created the Spec Oracle System and implemented the first of many oracles choosing a hybrid class hierarchy/composition-based representation for our items.
itemSpec. Item instances ultimately derive from the parent class
item. These common parent classes serve an important role in providing behaviors that all item specifications or item instances have in common, but may need to override.
From the common parent class
itemSpec, we created a very minimalist class hierarchy consisting of;
currencySpec. They mainly serve as a convenience at the request of our designers, allowing them to create a new
weaponSpec which will start with the decorators we have determined as the minimum set for a functioning weapon specification. As a factory for instances, our specifications instantiate instances from a class specific to the specification.
You will probably notice that the class hierarchy portion of our item representation is very shallow. The shallow hierarchy is a deliberate architectural choice; only the most fundamental behaviors are handled by these classes beyond which we depend entirely on decorators to extend the specification/instantiation with new functionality.
- See also: Spec Decorators
Prop bucket asset visualization
For items which are game world assets, instantiated from a prop bucket, the following information is necessary:
- The Icon to use
- The file to load
- How to attach the item to each character model
- Animation inputs to use when displaying the visualization spec
The other type of visualization spec is that for geometry-based items, such as clothing. Initially all clothing items are created and displayed using information from a spec in the Item Visualization Spec Oracle. However, players have the ability to tailor the item's color palette to better match their desired outfit. This is done with GLOMming on classes which override the mutable spec-provided display information, with a custom immutable set of data. Clothing specs are set up with the following information:
- The Icon to use
- The Slots, subslots, and textures to use
Notice that this user interface uses components from the character creation user interface (which is included with Clean Engine). This is an example of how, through careful architecture, you can make your tools reusable as components in other tools. This greatly increases your team's productivity down the line. If the character creation-type user interface had to be recreated for this visualization user interface, it would have taken a lot longer to develop and be much harder to maintain!
Item Spec OracleOnce the Item Visualization Specs are set up, it is then time to begin work in the ItemSpecOracle. This Oracle contains all of the item specs for each and every item that will exist within Hero's Journey. Using the spec system provides a single place for all items to be located regardless of their specific function.
In the case of weapons, we use several decorators including HasValue, Equippable, Holdable, HasDurability, and Visualizable. Visualizable is the decorator that tells the systems that this item will be held by players and needs to reference a spec from the Item Visualization Spec Oracle.
In the above example, the Item Visualization List references only a single Item Visualization Spec. This indicates that the item is not upgradable.
Decorator classes are a fundamental part of the way in which Hero's Journey (and even HeroEngine) is designed. The key advantage is that new Decorator Classes can be added at any point to extend the system further. We use this same method for compositing together the aspects of, say, Abilities. And in each case, a Spec Oracle is involved. Notable here is that the default Spec Oracle user interface allows a user to GLOM another class onto a spec already.