Enemies and Enemy Templates

[ LiB ]

Enemies are set up a little differently from everything else in the game. There are actually two different enemy classes: enemies themselves , and enemy templates. This is a fairly standard way of representing things in a game that has a few volatile attributes (such as hitpoints) and many attributes that won't change at all (everything else).


Please note that when I refer to enemy templates , I am referring to an actual class within the SimpleMUD, and not the C++ language feature known as templates .

The idea is to conserve memory. As Figure 10.1 shows, the templates store data that won't change about a particular type of enemy, and the enemy classes store volatile data about each individual enemy in the game. Whenever the game needs to access the nonvola-tile data of an enemy, it looks at the template. But if the game needs to access an individual enemy's hitpoints, it looks into the regular enemy class.

Figure 10.1. Relationship between enemies and enemy templates.


So the Enemy class represents individual instances of enemies within the game, and the EnemyTemplate class represents data about individual types of enemies within the game. You'll have a template representing Thugs, a template representing Thieves, a template representing Evil Monkeys, or whatever else you decide to put into the game.

EnemyTemplate Class

Since enemy templates store all the base information about an enemy, I'll go over them first.


Because an enemy's only purpose is to add combat into the game, all an enemy really needs are combat-based variables . You'll need hitpoints, obviously, as well as accuracy, dodging, strike damage, and damage absorption attributes. When a player dispatches an enemy within the game, he is rewarded with experience points, so templates must also know how much experience they are worth.

Enemies in the SimpleMUD get their minimum and maximum damage variables (as well as their swing time) from an item, which will serve as their weapon.

Enemy templates have three more elements: the min and max money variables (which determine how much money an enemy drops when he dies) and a list of loot , which I will get to in a moment. First, here's the class listing:

 class EnemyTemplate : public Entity { public:     EnemyTemplate();     int m_hitpoints;      // number of HP     int m_accuracy;       // accuracy of enemy     int m_dodging;        // dodging of enemy     int m_strikedamage;   // strike damage     int m_damageabsorb;   // damage absorption     int m_experience;     // experience gained when killed     item m_weapon;        // weapon enemy uses     money m_moneymin;     // min $ enemy drops when it dies     money m_moneymax;     // max $ enemy drops when it dies     list<loot> m_loot;    // list of items that drop when it dies }; 

The major thing to note about this class is that all its data is public; you'll see that this is because this class is never really directly manipulated by anything except the Enemy class, so you don't really have to worry about hiding its data.

The loot structure is simple:

 typedef std::pair< item, int > loot; 

I used the std::pair class ( std::map s use pair objects to store pairs of keys and data) to define a piece of loot as being an item ID and an integer. The item ID is obviously the ID of the item you want the enemy to drop. The integer represents the percentage chance that the item will be dropped. So a loot entry containing a 10 and a 1 has a 1% chance that item 10 will be dropped when the enemy dies.


There are only two functions involved with the enemy templates; the constructor (which clears every variable to 0, so I'm not going to waste space with it here), and the stream extraction ( operator>> ) function.

Before I go into the latter function, let me show you the format of an enemy template on disk:

 [ID]            1 [NAME]          Rabid Monkey [HITPOINTS]     6 [ACCURACY]      40 [DODGING]       -30 [STRIKEDAMAGE]  0 [DAMAGEABSORB]  0 [EXPERIENCE]    4 [WEAPON]        40 [MONEYMIN]      0 [MONEYMAX]      2 [LOOT]          40  3 [LOOT]          35  2 [ENDLOOT] 

The only really major thing you need to pay attention to is the loot listing; everything else is fairly straightforward.

Every loot entry contains two values: the item ID to drop, and the percent chance that it drops. After every loot entry (even if there are no entries), you absolutely must have an [ENDLOOT] tag.

Because of the way this is set up, enemies can drop any number of items that you want them to.

I'm going to forgo showing you most of the operator>> function, and just show you the little snippet that loads in the loot entries:

 t.m_loot.clear(); while( extract( p_stream, temp ) != "[ENDLOOT]" ) {     entityid id;        int chance;     p_stream >> id >> chance;     t.m_loot.push_back( loot( id, chance ) ); } 

In this code, t is an EnemyTemplate object, and p_stream is the stream the enemy template is being extracted from. At the top of the code, the loot list is cleared (in case there is anything already in it; see my note about reloading room databases in Chapter 9, "Maps, Stores, and Training Rooms").

Then, the code loops through the stream until the token "[ENDLOOT]" is found. For each entry, the ID and the chance that the item is dropped are loaded into temporary local variables, and then pushed onto the back of the m_loot list.

Enemy Class

The Enemy class is a proxy class. Basically, it looks like a full enemy, but it really holds only a small amount of information.

Class Data

Here's the class definition with all function definitions removed:

 class Enemy : public Entity { protected:     enemytemplate m_template;     int m_hitpoints;     room m_room;     BasicLib::sint64 m_nextattacktime; }; 

There are four attributes:

  • The corresponding template in the form of an enemytemplate class, which you haven't seen yet. You can see, however, from the naming scheme that it's a database pointer to an EnemyTemplate /.

  • The enemy's hitpoints.

  • The room the enemy is in.

  • The next time the enemy may attack.

This last variable is explained more when I get around to the game loop later on in this chapter.

Class Functions

Since this class acts like a proxy into an enemy template, a number of functions enable you to completely hide the fact that this class accesses a template:

 // regular functions: Enemy();     void LoadTemplate( enemytemplate p_template ); // plain accessors: int& HitPoints()            { return m_hitpoints; } room& CurrentRoom()         { return m_room; } sint64& NextAttackTime()    { return m_nextattacktime; } // proxy accesors: string& Name(); int Accuracy(); int Dodging(); int StrikeDamage(); int DamageAbsorb(); int Experience(); item Weapon(); money MoneyMin(); money MoneyMax(); list<loot>& LootList(); 

I've grouped the functions into three different categories: the regular functions, the plain accessors, and the proxy accessors.

The constructor puts 0s into every attribute, so there's no need to show it to you. The LoadTemplate function loads in an enemy's hitpoints from an enemy template:

 void Enemy::LoadTemplate( enemytemplate p_template ) {     m_template = p_template;     m_hitpoints = p_template->m_hitpoints; } 

Even though Enemy s are Entity s, their m_name variable is not used. Instead, the variable is left empty, to conserve memory (since enemy names are already stored in the corresponding enemy template).

You can see that the three plain accessors simply return the values of three of the data members (not including the template).

The rest of the functions, however, are proxy functions, which means that they actually look up the corresponding EnemyTemplate , and return the requested value. Here is the code for three of them:

 std::string& Enemy::Name()  { return m_template->Name(); } int Enemy::Accuracy()       { return m_template->m_accuracy; } int Enemy::Dodging()        { return m_template->m_dodging; } 

Since m_template is an enemytemplate database pointer, you can use it to look up the corresponding EnemyTemplate class, and return its values. Figure 10.2 shows a small example of how this works. Enemy instances hold a small amount of data, but when you request things like accuracy and names, they look up those values from an enemy template.

Figure 10.2. The system of enemies and enemy templates.


The rest of the functions are similar.

Stream Functions

Enemies can be streamed both into and out of C++ streams, utilizing the standard operator>> and operator<< . There is absolutely nothing special about the code, so let me show you the format of an Enemy class when written to disk:

 [ID]             1 [TEMPLATEID]     1 [HITPOINTS]      6 [ROOM]           5 [NEXTATTACKTIME] 0 

This shows that Enemy 1, which is an instance of enemy type 1 (as you saw earlier, "Rabid Monkey" is template 1), currently has 6 hitpoints, is in room 5, and the next time the enemy can attack is 0. You'll see later that the last value means that this enemy can attack the moment he sees a player.

[ 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