Chapter 3
Adding Player Abilities and System Effects

Having additional classes in your MUD is terrific, but if that class is just a composite of elements from other classes, it is not really all that much to brag about. For a new class to be interesting, it has to have something about it which is unique; special skills or abilities that no other class in the game has. Also, if you take a look at the abilities list toward the end of "class.c", there is a significant imbalance in how special powers were distributed, with the mages and clerics having the lion's share.

To this end, we should be able to add abilities. The places where modifications need to be made are, again, distributed over a few files, but this is not as complicated as adding a class was.

There are a couple of different types of spells and of skills we could create. Some spells are aggressive, and will automatically start a fight, or automatically target someone that the person or non-player character using the spell is already fighting. Some create or destroy objects. Some affect attributes of a character, either improving or diminishing the character's ability to move or to fight, and some of these have a lasting affect that eventually wears off (if it doesn't kill the target character first). Some only work on characters who are under the influence of these other affects. Some affect a character's ability to see other characters, some affect a character's ability to be seen.

The spells and skills being defined here are intended for the ranger class which was created in the previous chapter. Let's start with a couple of easy ones, and then maybe move onto some more intricate ones. First we'll create a generic attack skill. After that, we'll create a non-combat spell which adds to the player's inventory. Then we'll create a spell which applies a system effect to a character, and finish up with a social.

The first step in adding any command, with the exception of spells which must be cast rather than simply done, is to add the command to the list in "interpreter.c". At about line 197 you should find the cmd_info array, which contains a list of every command that can be typed by any player in "normal" play mode (CON_PLAYING). After the "reserved" entry and direction commands, the other entries are listed for the most part in alphabetical order. Each entry is in the array is a structure of type command_info which contains the components needed to make the command accessible through the game's normal command interpreter sequences. The first of these is a string containing the actual command as the player would type it. The second is the minimum position (dead, mortally wounded, incapacitated, stunned, sleeping, resting, sitting, fighting or standing) that a player must be in to use this command. The third is the name of the function, as it exists in the source code, which executes the command. What is actually stored here is a pointer to the function's location in memory, and the ability of C to do this allows for the routines which actually interpret the commands to be much simpler than they would otherwise need to be. The fourth element is the minimum level at which a player can type this command. Setting this LVL_IMMORT, for example, would restrict this ability to players at the Immortal level and above. The fifth and last element is the subcommand feature, which is set to a subcommand value, if more than one command is handled by the same function. For example, there are several different commands for communicating on one of the public channels, such as the "shout" and "gossip", all of which are handled by the do_gen_com function, but are done with different commands, which send do_gen_com different subcommand values. Subcommand values are defined in "interpreter.h" starting at about line 67, although we will not be using any of them.

The order the commands appear in actually serves a purpose. The command interpreter searches through the list from top to bottom until it finds a command which is either the same as or starts with the command that a player has typed. Consequently, if "cat" and "catch" are both on the list, and "cat" comes first, a player typing "catch" will in fact execute "cat". As a result, commands which are part of other commands should be placed earlier in the list. Also, if you want "bea" to be an abbreviation for "beat", and "bearhug" is also on the list, "beat" must be ahead of "bearhug".

Our new attack skill is going to be called "Bearhug", so we'll go down to the commands starting with "b", at about line 218:

  { "bounce"   , POS_STANDING, do_action   , 0, 0 },
  { "backstab" , POS_STANDING, do_backstab , 1, 0 },
  { "ban"      , POS_DEAD    , do_ban      , LVL_GRGOD, 0 },
  { "balance"  , POS_STANDING, do_not_here , 1, 0 },
  { "bash"     , POS_FIGHTING, do_bash     , 1, 0 },
  { "beg"      , POS_RESTING , do_action   , 0, 0 },
  { "bleed"    , POS_RESTING , do_action   , 0, 0 },
  { "blush"    , POS_RESTING , do_action   , 0, 0 },
  { "bow"      , POS_STANDING, do_action   , 0, 0 },
  { "brb"      , POS_RESTING , do_action   , 0, 0 },
  { "brief"    , POS_DEAD    , do_gen_tog  , 0, SCMD_BRIEF },
  { "burp"     , POS_RESTING , do_action   , 0, 0 },
  { "buy"      , POS_STANDING, do_not_here , 0, 0 },
  { "bug"      , POS_DEAD    , do_gen_write, 0, SCMD_BUG },


Somewhere in there, add the following line:

  { "bearhug"  , POS_FIGHTING, do_bearhug  , 1, 0 },


This having been done, we need to move up to about line 55 or so, and find the "do_x" function prototypes. Right below the line for do_bash, at about line 64, insert the following line:

 ACMD(do_bearhug); 


This will put a prototype in for the do_bearhug function, using the ACMD macro defined in "interpreter.h", which is used for all commands to insure that they interface properly with whatever functions call them.


Close "interpreter.c" and open "spells.h". Here, we need to define certain values so that the skill can be learned. Go down to about line 93 and find the following section:

/* PLAYER SKILLS - Numbered from MAX_SPELLS+1 to MAX_SKILLS */
#define SKILL_BACKSTAB              131 /* Reserved Skill[] DO NOT CHANGE */
#define SKILL_BASH                  132 /* Reserved Skill[] DO NOT CHANGE */
#define SKILL_HIDE                  133 /* Reserved Skill[] DO NOT CHANGE */
#define SKILL_KICK                  134 /* Reserved Skill[] DO NOT CHANGE */
#define SKILL_PICK_LOCK             135 /* Reserved Skill[] DO NOT CHANGE */
#define SKILL_PUNCH                 136 /* Reserved Skill[] DO NOT CHANGE */
#define SKILL_RESCUE                137 /* Reserved Skill[] DO NOT CHANGE */
#define SKILL_SNEAK                 138 /* Reserved Skill[] DO NOT CHANGE */
#define SKILL_STEAL                 139 /* Reserved Skill[] DO NOT CHANGE */
#define SKILL_TRACK                 140 /* Reserved Skill[] DO NOT CHANGE */


At the end of these, insert the following line:

#define SKILL_BEARHUG               141


Next, close "spells.h" and open "structs.h". Go down to about line 134 and find the bitvector flags for mobiles:

/* Mobile flags: used by char_data.char_specials.act */
#define MOB_SPEC         (1 << 0)  /* Mob has a callable spec-proc      */
#define MOB_SENTINEL     (1 << 1)  /* Mob should not move               */
#define MOB_SCAVENGER    (1 << 2)  /* Mob picks up stuff on the ground  */
#define MOB_ISNPC        (1 << 3)  /* (R) Automatically set on all Mobs */
#define MOB_AWARE        (1 << 4)  /* Mob can't be backstabbed          */
#define MOB_AGGRESSIVE   (1 << 5)  /* Mob hits players in the room      */
#define MOB_STAY_ZONE    (1 << 6)  /* Mob shouldn't wander out of zone  */
#define MOB_WIMPY        (1 << 7)  /* Mob flees if severely injured     */
#define MOB_AGGR_EVIL    (1 << 8)  /* auto attack evil PC's             */
#define MOB_AGGR_GOOD    (1 << 9)  /* auto attack good PC's             */
#define MOB_AGGR_NEUTRAL (1 << 10) /* auto attack neutral PC's          */
#define MOB_MEMORY       (1 << 11) /* remember attackers if attacked    */
#define MOB_HELPER       (1 << 12) /* attack PCs fighting other NPCs    */
#define MOB_NOCHARM      (1 << 13) /* Mob can't be charmed              */
#define MOB_NOSUMMON     (1 << 14) /* Mob can't be summoned             */
#define MOB_NOSLEEP      (1 << 15) /* Mob can't be slept                */
#define MOB_NOBASH       (1 << 16) /* Mob can't be bashed (e.g. trees)  */
#define MOB_NOBLIND      (1 << 17) /* Mob can't be blinded              */


Add the following line to the end of the list, enabling a bitvector entry for mobiles so that some can be made non-bearhug-able:

#define MOB_NOBEARHUG    (1 << 18) /* Mob can't be bearhugged           */


Now, since this is a combat function, close "structs.h" and open "act.offensive.c" so that we can add do_bearhug in with the other functions for combat abilities. Since we are adding a whole function here, this can actually go anywhere in the file after the #include statements and file-wide declarations, which end at about line 31, but for the sake of being able to find it later, we will place it at the end of the file.


ACMD(do_bearhug)
{
  struct char_data *vict;
  int percent, prob;

  one_argument(argument, arg);

  if (!(vict = get_char_room_vis(ch, arg))) {
    if (FIGHTING(ch)) {
      vict = FIGHTING(ch);
    } else {
      send_to_char("Bearhug who?\r\n", ch);
      return;
    }
  }
  if (vict == ch) {
    send_to_char("Aren't we funny today...\r\n", ch);
    return;
  }
  percent = ((10 - (GET_AC(vict) / 10)) << 1) + number(1, 101);
  prob = GET_SKILL(ch, SKILL_BEARHUG);

  if (MOB_FLAGGED(vict, MOB_NOBEARHUG)) {
    percent = 101;
  }

  if (percent > prob) {
    damage(ch, vict, 0, SKILL_BEARHUG);
  } else
    damage(ch, vict, GET_LEVEL(ch) << 1, SKILL_BEARHUG);
  WAIT_STATE(ch, PULSE_VIOLENCE * 3);
}


This function was actually copied from do_kick, with all references to kicks changed so that they refer to bearhugs. The third-to-last line was modified so that it inflicts four times the damage that the kick would, and a clause was inserted so that mobs who shouldn't be bearhugged cannot be bearhugged.


Close "act.offensive.c" and open "spell_parser.c". Go down to about line 37 and find the definition for something called spells, which contains the spell and skill names. Go down to about line 130, where the unused skills start, find the following line:


"!UNUSED!", "!UNUSED!", "!UNUSED!", "!UNUSED!", "!UNUSED!", /* 145 */


Change the first element in this line to "bearhug", as follows:
"bearhug", "!UNUSED!", "!UNUSED!", "!UNUSED!", "!UNUSED!", /* 145 */


Now, go down to the declaration of skills at the end of the function mag_assign_spells, at about line 1015:

 
  skillo(SKILL_BACKSTAB);
  skillo(SKILL_BASH);
  skillo(SKILL_HIDE);
  skillo(SKILL_KICK);
  skillo(SKILL_PICK_LOCK);
  skillo(SKILL_PUNCH);
  skillo(SKILL_RESCUE);
  skillo(SKILL_SNEAK);
  skillo(SKILL_STEAL);
  skillo(SKILL_TRACK);


At the end of this list, insert the following line: skillo(SKILL_BEARHUG);

Now, open "class.c", and go down to the end of the function init_spell_levels, and find the section we added for the Rangers' spells and skills, about lines 562 through 574. Insert the following line to make the Bearhug skill available to Rangers level 12 or above: spell_level(SKILL_BEARHUG, CLASS_RANGER, 12);

Finally, we need to add the messages sent to players when this skill is attempted. Close "class.c", and go into the "lib" directory containing the game data library, and from the "misc" directory contained there open the file "messages". Note that the entries here apply not just to skill and spell attacks, but to regular attacks and non-player-character attacks as well. Also note that a given attack can have more than one block of messages associated with it.

Each block of messages is arranged in a specific order. It starts with the letter "M" alone on a line. The next line has a space followed by the skill number, in this case 141, and then twelve message lines. These twelve lines are divided into four groups of three messages. The first group is used if the attack kills the victim. The second group is used when the attack misses. The third group is used when the attack connects but is not a fatal strike. The fourth group is used when the attacker is foolish enough to try using these feeble skills against a character at the God levels. Within each group, the first message is seen by the victim, the second by the attacker, and the third by anyone else in the room. Each block is followed by a blank line, and the file is ended by a line with a dollar sign on it. Lines which start with an asterisk are comments.

For our bearhug, we will only use one block of messages. At about line 302, you will find comments marking the start of the regular attacks. The last group of messages above this point is the second block of messages for the kick. Between the end of the kick messages and the comment lines, insert the following block:

*Bearhug
M
 141
Your bearhug crushes $N to death!
You feel your ribs break as $n bearhugs you to death!
You hear the sound of bones snapping as $n crushes $N to death with a bearhug!
You try to bearhug $N, but topple over as $E slips out of the way.
You sidestep a bearhug from $n, who falls on $s face.
$N dodges a bearhug from $n, who ends up flat on the floor.
You squeeze the breath out of $N with your bearhug!
$n's bearhug squeezes your lungs empty!
$n squeezes the wind out of $N with a powerful bearhug!
You try to bearhug $N but $s body is like solid rock!
You grin as $n tries to bearhug you, but can't seem to apply any pressure.
$n tries to bearhug $N, but $N doesn't seem to feel it.


Now close "message", delete any compiler object files you might have in your "src" directory from previous compiles, and make or re-make the program. If everything is in order, your Rangers should now have full use of the Bearhug at and above level 12.


That was painless enough. Now let's see about adding a spell.