This article was originally written for Darren Hebden's RLNews
Representing objects in a roguelike. A difficult thing to do, in fact. When
I started War of the Runes, I wasn't sure exactly how to go about doing it.
Would everything be hard coded into the game? Would objects be malleable?
What kinds of different objects need to be represented?
Well, for starts, I knew I didn't want anything to be hard coded. One thing
about War of the Runes is that EVERYTHING would need to be able to be changed.
So objects need to be stored in external files, with all descriptions of the
object need to be stored; from description, to behavior, to properties. And
this also means all objects needed to be malleable. the state of an object can
change at any time through any number of ways. And what kinds of objects were
there? There's weapons and armor, food and drink, doors, chests, bags, rings,
books, anything that can exist in a real world.
So what is the best way to represent objects in a roguelike? Well, first we
need to start with an object class:
class Object {
public:
What kind of data do we need for objects? For starts, we need to know what
the object is. We can do this with a name and a description;
char *Name, *Desc;
That's fairly self-explanatory. Information about where the object is would
be useful, too:
int X, Y;
There are lots of different objects in the roguelike universe. Armor,
weapons, books, rings, potions, lawn-chairs, etc. Each object can only be
of one type.
int ObjType;
This field will be set to a value we define with #define's.
#OBJ_WEAPON 1
#OBJ_ARMOR 2
#OBJ_POTION 3
You get the idea. Every different type of object in the game would need a
separate number. This will help in a LOT of areas. For example, characters
can only equip items of type 2 (armor).
Since I'm sure some of you may be a bit confused (I know I'm not the best of
teachers) why don't we start defining an example item before we continue? A
longsword.
We'd set the Name field to point to "long sword". Simple enough. The Desc
field would be "a long, sharp, metal stick". Well, you may want to use
something other than that.
The X,Y coordinates can be set to whatever... let's say 2,10.
What about the object type? 1 (OBJ_WEAPON) of course!
Now what? What does a longsword do? The game knows it's a weapon, but the
game needs more information than that.
Let's make a class to define what an object does.
class ObjArg {
public:
Each ObjArg will define one aspect of an object. We'll need a way to store
what each ObjArg defines.
int ArgType;
We'll give this meaning through the use of more defines.
#define OARG_ATTACK 1
#define OARG_DEFEND 2
#define OARG_HEAL 3
Now we know what an ObjArg defines. Now we need to store the actual
definition. We'll do this by storing an array of 5 integers.
int Args[5];
And that's all there is to the ObjArg class. The meaning of the Args
array's field will depend on the value of ArgType.
Now, we want every object to be pretty versatile, so we'll make it so
every object has 5 of the ObjArg classes.
} Defines[5];
And that's all we need for a basic Object class
};
So how does the ObjArg class work? Well, for our longsword, we'll need
only one. It's ArgType will be set OARG_WEAPON. Now what about the
Args array? Let's say for ArgType 1 (weapon), the first field of Args
is the number of damage dice, the second field is sides per die, the
third field is damage modifier, and the fourth field is the modifier
to the character's skill. The fifth field will be unused.
So, if we wanted long sword's to cause 1 to 8 damage, with no
additional modifiers to damage or attack skill. So...
Args[0] = 1
Args[1] = 8
Args[2] = 0
Args[3] = 0
Now if the character equips the long sword and attacks, we'll first
check to see what kind of object is in the character's hand. We see
the ObjType field is set to 1 (weapon). OK, so he/she can attack with
that object. Now we'll look through the Defines array until we find an
ObjArg entry whose ArgType is set to 1 (attack). The game see that
Defines[0].ArgType = 2, so we'll use that ObjArg to look up the weapon's
statistics. The game checks Defines[0].Args[3] = 0, so there's no skill
modifier. The game does whatever combat system and determines the
character hit. It check the damage (Args fields 0, 1, 2) and sees that
the long sword does 1d8+0 damage. The game rolls the damage, hurts the
target, etc.
Well, that's all you need for simple objects. Although, you could do a
LOT more, using the sample code I've given here as a base. For example,
some ObjArgs are in effect whenever an item is equipped (the long sword's
attack field, or armor's defend field). Some objects, like potions,
don't effect unless a character uses the object (or in the case of objects
with ObjType = 3, the character quaffs the potion). Only then will their
ObjArg fields (like healing, in the case of a potion of healing) take
effect. So you might want to store how many times an object can be used,
and how many times it has been used.
The complete code for the Object class is:
#define OBJ_WEAPON 1
#define OBJ_ARMOR 2
#define OBJ_POTION 3
#define OARG_ATTACK 1
#define OARG_DEFEND 2
#define OARG_HEAL 3
class Object {
public:
char *Name, *Desc;
int X, Y;
int ObjType;
class ObjArg {
public:
int ArgType;
int Args[5];
} Defines[5];
};
If you have any questions, feel free to e-mail me at
sean.middleditch@iname.com
The End
|