Items

[ LiB ]

Between the Entity , EntityDatabase , EntityDatabaseVector , databasepointer , Attribute , and AttributeSet classes, there was quite a bit of code. The good news is that because I spent so much time developing the groundwork , the job of developing the rest of the game suddenly becomes easier. You'll see how this works throughout the rest of this chapter.

Now that the groundwork is done, it's time to move on to the first type of entity that will be used within the gameitems.

Item Class

Items, as I described in Chapter 7, are simply physical objects that you can pick up and carry around in the game. Since items will have names and IDs, they are inherited from the Entity class.

Item Types

As I also mentioned in Chapter 7, items come in three flavors: weapons, armor , and healing items. Because of this, I created an enumerated type (found in Attributes.h):

 enum ItemType {     WEAPON,     ARMOR,     HEALING }; 

In addition to this enumeration, I've created two functions to help convert types to strings and vice versa. First is the GetItemType function, which gets a type from a string, and the second is the GetItemTypeString function, which does the opposite . These functions make reading and writing enumerations to streams easier and make your output files more legible. However, since the way they work isn't that important to MUD programming in general, I hope you'll forgive me if I move along without showing you the code. Don't worry though; you can see it if you're still interested in the Attributes.h file on the CD.

Money: It's What I Want

For SimpleMUD, I use a simple typedef to represent the money type:

 typedef unsigned long int money; 

This uses an unsigned 32-bit integer, so that means that the money type can represent anything from 0 to 4 billion dollars. This can be changed at any later date if you wish.

Item Attributes

Items have two types of attributes: the attributes of the item itself, and the attribute modifiers, which will be added to a player's attributes when that particular item is used.

Here's a skeleton of the Item class, with the function declarations removed:

 class Item : public Entity {     ItemType m_type;     int m_min;     int m_max;     int m_speed;     money m_price;     AttributeSet m_attributes; }; 

Obviously, each item has its own type to identify to the game what kind of item it is. Based on the item type, the three variables m_min , m_max , and m_speed have different meanings. Table 8.4 lists the uses for each of those attributes for each item type.

Table 8.4. Item Attribute Uses

Type

m_min

m_max

m_speed

Weapon

min damage caused

max damage caused

pause between swings in seconds

Armor

not used

not used

not used

Healing

min damage healed

max damage healed

not used


In addition to those three attributes, every item also has a money type, representing how much money the item is worth, and an AttributeSet , corresponding to the nine attributes that players have. This attribute set contains deltas , which determine how much of each attribute to add or subtract to a player whenever that item is used.

For example, a weapon with an accuracy attribute of 10 would add 10 to the player's accuracy whenever that weapon was armed and remove 10 whenever it was disarmed.

Writing and Reading Items from Disk

I haven't really discussed how items are read and written to disk yet.

When designing a MUD, you have many options. Many older MUDs store their data in binary format, since in binary form data is packed tightly, and thus uses less memory. (For example, a 32-bit integer in binary always takes up 4 bytes, but a 32-bit integer in ASCII may take up to 10 bytes.) More complex MUDs may even offload the disk storage capabilities into a dedicated database server, such as a SQL server of some sort .

For SimpleMUD, you don't need anything fancy. It's going to stay nice and simple. Since I don't have room to introduce editor tools to you, I'm going to use the simplest and most editable format availableplain ASCII text files.

There's a lot of hype going around about XML data storage, but XML is far too complex for what I need; instead, I'm just going to use a simple line-by-line approach to store data. Each item will be defined as a seven-line string of ASCII text, in which each line contains a single attribute. Each line is composed of two things: the name of the attribute, contained within square brackets, and the value of the attribute.

Here's an example of an item string:

 [ID]            1 [NAME]          Knife [TYPE]          WEAPON [MIN]           2 [MAX]           4 [SPEED]         2 [PRICE]         10 [STRENGTH]      0 [HEALTH]        0 [AGILITY]       0 [MAXHITPOINTS]  0 [ACCURACY]      10 [DODGING]       0 [STRIKEDAMAGE]  0 [DAMAGEABSORB]  0 [HPREGEN]       0 

Since most of the writing capabilities of C++ files depend on using iostream s, I've decided to keep the tradition, and let items use iostream s as well. Because of this, items have the standard stream extraction operator, but not stream insertion, since there is no need to write items back to disk:

 friend istream& operator>>( istream& p_stream, Item& i ); 

There is no stream insertion operator, simply because there is no need for one. Items are never changed in the game; therefore, they never need to be written to disk.

Here's the function:

 inline istream& operator>>( istream& p_stream, Item& i ) {     std::string temp;     p_stream >> temp >> std::ws;   std::getline( p_stream, i.m_name );     p_stream >> temp >> temp;      i.m_type = GetItemType( temp );     p_stream >> temp >> i.m_min;     p_stream >> temp >> i.m_max;     p_stream >> temp >> i.m_speed;     p_stream >> temp >> i.m_price;     p_stream >> i.m_attributes;     return p_stream; } 

As you can see, the code is mostly straightforward. For each attribute, the line label is read into temp and ignored; then the real attribute is read into the appropriate variable.

NOTE

There is one limitation you should know about when extracting items: the attribute labels in the stream mean absolutely nothing to the computer; they are there only for you, so that when you open the file in a text editor, you know which attribute means what. The com puter, when loading in items, ignores the labels within the brackets, and assumes that the items within the stream are in the appropriate order.

There are two things to pay attention to, however: When reading in an item name, instead of streaming the name using operator>> , I use the std::getline function. This is important, because item names may have spaces in them, such as "Chainmail Armor". If you used the standard operator>> , it would just read the first word, which isn't what I want. Luckily, the std::getline function exists and reads in everything in the stream up until a newline character. Also note that the previous line pipes the stream into the std::ws object, which simply "eats" all the whitespace leading up to the next word. This is essential because the std::getline function doesn't do that automatically for you.

You should notice the two lines that read in the type of the item. Within the text file, as you saw when I listed a sample item printout, the item type is an actual word, like "weapon" or "armor". Because of this, I need to make use of the GetItemType function to convert a string into an ItemType enumeration.

Finally, you may have noticed that the function doesn't load in the ID of the item; instead, the item database class is relied on to do that, which you will see a little later on.

Item Function Listing

Table 8.5 lits all the functions in the Item class, omitting those inherited from the Entity class.

Table 8.5. Item Functions

Function

Purpose

Item()

Constructs an item with "invalid" values

ItemType& Type()

Returns the type of the item

int& Min()

Returns the min attribute

int& Max()

Returns the max attribute

int& Speed()

Returns the speed attribute

money& Price()

Returns the price of the item

int& GetAttr( int attr )

Returns a reference to player attribute 'attr'


As you can see, all the functions are simply accessor functions; items have little need for anything else. The GetAttr function works with the standard Attribute labels, like so:

 Item i; i.GetAttr( HEALTH ) = 10; int s = i.GetAttr( STRENGTH ); 

And so it continues. This saves you the trouble of writing different accessors for each attribute.

Item Database

The item database is an incredibly simple class, because most of the work has already been accomplished with the EntityDatabase class. There are only a few things I need to do to make a fully functional item database class. You can find the class within the ItemDatabase.h and .cpp files on the CD.

File Storage

The first issue you need to tackle is the question of file storage: How will you store items to disk? Previously, I deemed it optimal for a simple MUD like this to use ASCII text to store data, since you can open text files in any text editor, without expending significant time making a custom editor for items.

So now you need to figure out how items will actually be stored. When analyzing the game, you may notice that items are just static objects and really should not be modified. Therefore, you can assume that the MUD won't be modifying items, and there's really no need to be able to write them back out to disk while the MUD is running.

Because of this, I've decided to store all the items in one large text file: /items/items.itm. When the database starts up, it loads all the items from this file.

Class Definition

Here's the definition for the ItemDatabase class, which is located within the ItemDatabase.h file:

 class ItemDatabase : public EntityDatabase<Item> { public:     static bool Load(); }; 

As you can see, an ItemDatabase is simply an EntityDatabase that stores Item s. Additionally, it has one new function: the Load function, which loads the database from the /items/ items.itm file.

Since all the database classes are meant to be static, the Load function is static as well. This means you can call it like this within the game:

 ItemDatabase::Load(); 

Also, there's one more note I should make: Since the m_map member inside the EntityDatabase class is static, it must be defined in a .cpp file somewhere, or else you'll end up with linker errors when you compile. So here's the definition:

 std::map<entityid, Item> ItemDatabase::m_map; 

You must do this for every class you create that inherits from EntityDatabase .

Loading the Database

Finally, here's the function to load the database from the /items/items.itm file:

 bool ItemDatabase::Load() {     std::ifstream file( "items/items.itm" );     entityid id;     std::string temp;     while( file.good() )  {         file >> temp >> id;         m_map[id].ID() = id;         file >> m_map[id] >> std::ws;         USERLOG.Log( "Loaded Item: " + m_map[id].Name() );     }     return true; } 

The function basically opens up the item file, and tries to read in item after item until there is nothing more to read. The first thing the function does within the loop is read in the ID of the item.

Once I have the ID, I use the std::map::operator[] function to look up the item with that ID. At this point, one of two things can happen: If an item with the ID already exists, its ID is simply overwritten with the same value that I just read in from disk ( essentially accomplishing nothing). If the item doesn't exist, operator[] has a little side effect that I rely on: A new item with the given ID is created and inserted into the map automatically, and its ID is set to what was just loaded in. Then, the rest of the item is loaded in from the file, and all the whitespace after the item entry in the file is eaten up using the std::ws stream modifier.

The final act is to notify the userlog that an item was loaded.

Item Database Pointers

Finally, when accessing items within the item database, it's usually a good idea to use a database pointer, modeled around the database pointer macros I showed you earlier.

This simply requires a macro definition (which is located in the DatabasePointer.h file):

 DATABASEPOINTER( item, Item ) 

Now you can use item throughout the game (notice that "i" is lowercase) just like a pointer into the ItemDatabase , as I described earlier. You can use it like this:

 item i = 10; i->Dodge() = 20; 

And so on.

[ LiB ]


MUD Game Programming
MUD Game Programming (Premier Press Game Development)
ISBN: 1592000908
EAN: 2147483647
Year: 2003
Pages: 147
Authors: Ron Penton

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net