WTFAQ - Adding a New Class

Files needed:

 constants.c -- to add bitvectors (!CLASSNAME, etc)
 magic.c -- to add magic saving throws
 shop.h -- to add NO_TRADE with your new class
 shop.c -- to add NO_TRADE with your new class
 utils.h -- to define class and bitvectors
 structs.h -- to define class and bitvectors
 spec_assign.c -- to assign spec procs for guildmaster/guard
 class.c -- adding class, spells, THAC0, etc.
 act.informative.c -- fixing up do_kick for Class_Ranger (not necessary)
 limits.c -- increase Ranger mana_regen
 30.wld -- adding the guild
 30.mob -- adding guildmaster, guildguard, waitress
 30.zon -- loading guild mobs
 30.shp -- guild's bar


Since CircleMUD only comes with the four most basic character classes, the first thing we'll do is to add a fifth. One of the more popular classes of characters for adventure games in general is the Ranger. Since this class is reasonably unique, it will not have too much overlap in terms of spells or skill with the other four classes, so I'll be using this class as a running example.

Before you begin, I strongly suggest that if you already have a MUD up and running that you do not make any of these changes directly to your existing MUD, but rather make a copy and work on the copy. You don't want players to inadvertently try to use any of what you are adding, until you are satisfied that it is ready for them.

In moving to version 3.0, Jeremy Elson spun off most of the classes-related code into a file called "class.c". However, small changes still need to be made to several other files for a new class to be usable by the rest of the game, so we will start with these. Most of these are of the simple little copy-paste-modify kind of addition, and they are all rather generic, but still there are a lot of them and you will need to make them all.

In the file "constants.c", look at about line 335 in the original code for a block of code that looks like:

/* ITEM_x (extra bits) */
const char *extra_bits[] = {
  "GLOW",
  "HUM",
  "!RENT",
  "!DONATE",
  "!INVIS",
  "INVISIBLE",
  "MAGIC",
  "!DROP",
  "BLESS",
  "!GOOD",
  "!EVIL",
  "!NEUTRAL",
  "!MAGE",
  "!CLERIC",
  "!THIEF",
  "!WARRIOR",
  "!SELL",
  "\n"
};
You need to add the new class into end of this list. Insert a line between "!SELL" and "\n", and add in "!RANGER" for the new class after the "!SELL" line, as follows:
  "!THIEF",
  "!WARRIOR",
  "!SELL",
  "!RANGER",
  "\n"
};
Next, let's move on to "magic.c", where we need to define the "magic saving throws", which, as far as I can tell, are the values for how likely a character is succumb to certain effects, specifically paralysis, "rods", petrification, breath effects, and spells, with higher numbers meaning a greater likelihood of being affected. Go to about line 62 and find a block that begins with
const byte saving_throws[NUM_CLASSES][5][41] = {
and runs to about line 171:
                38, 36, 35, 34, 33, 31, 30, 29, 28, 27,         /* 21 - 30 */
                25, 23, 21, 19, 17, 15, 13, 11, 9, 7}           /* 31 - 40 */
  }
};
As per the recommendation of Jesper Donnis (class.doc), just copy one of the existing blocks to the back of the section, between the two lines which contain the last two closing braces of the block, roughly lines 170 and 171. For a class like the Ranger, I'd say to copy the block for the Thief, lines 118 through 144, and maybe change the values later if you feel it's appropriate. You are free to choose any of these blocks to work from; the reason I chose the thief as a template for the ranger for these values is that, like the thief, the ranger's strengths typically come from his or her skills, rather than from spells or from brute strength and big weapons. Also, make sure you put a comma after the closing brace on line 170 and remove the comma, if any, from after the closing brace in the section you just pasted, which should be at around line 197, give or take a line or two. If you choose to revise the numbers, try to follow some decent kind of curve. A pattern based on a fragment of the bell curve is recommended, with a sharp drop in the number between the lower levels, and the smallest changes approaching the end of the third row. Remember to leave the fourth row as all zeros, unless you want your administrators to be affected by these.

Okay, now let's move on to the shop code files. Let's start with the header file, "shop.h". Go to about line 71, and find the following section:

/* Whom will we not trade with (bitvector for SHOP_TRADE_WITH()) */
#define TRADE_NOGOOD            1
#define TRADE_NOEVIL            2
#define TRADE_NONEUTRAL         4
#define TRADE_NOMAGIC_USER      8
#define TRADE_NOCLERIC          16
#define TRADE_NOTHIEF           32
#define TRADE_NOWARRIOR         64
We need to add an entry for Rangers, just in case someone writing scenery ("building") for your MUD, in their infinite wisdom (or lack thereof), decides that some shop or another shouldn't like rangers. For reasons computer, the values are powers of two, and the next available value is 128. So, after the last entry, insert a line and add
#define TRADE_NORANGER          128
to make this possible. Just as a reminder, be certain you tell anyone else who is building for your MUD about any changes you are making to any of the bitvectors, such as this one. While they may not need to know about all of them, there are a number that they should know about because they may want to use them.

Okay, now move down to about line 128, and look for the following block of #define statements:

#define NOTRADE_GOOD(i)         (IS_SET(SHOP_TRADE_WITH((i)), TRADE_NOGOOD))
#define NOTRADE_EVIL(i)         (IS_SET(SHOP_TRADE_WITH((i)), TRADE_NOEVIL))
#define NOTRADE_NEUTRAL(i)      (IS_SET(SHOP_TRADE_WITH((i)), TRADE_NONEUTRAL))
#define NOTRADE_MAGIC_USER(i)   (IS_SET(SHOP_TRADE_WITH((i)), TRADE_NOMAGIC_USER))
#define NOTRADE_CLERIC(i)       (IS_SET(SHOP_TRADE_WITH((i)), TRADE_NOCLERIC))
#define NOTRADE_THIEF(i)        (IS_SET(SHOP_TRADE_WITH((i)), TRADE_NOTHIEF))
#define NOTRADE_WARRIOR(i)      (IS_SET(SHOP_TRADE_WITH((i)), TRADE_NOWARRIOR))
At the end of the list, add a line for NOTRADE_RANGER:
#define NOTRADE_RANGER(i)       (IS_SET(SHOP_TRADE_WITH((i)), TRADE_NORANGER))
Now move down to about line 138 and look for the following block of code:
/* Constant list for printing out who we sell to */
const char *trade_letters[] = {
        "Good",                 /* First, the alignment based ones */
        "Evil",
        "Neutral",
        "Magic User",           /* Then the class based ones */
        "Cleric",
        "Thief",
        "Warrior",
        "\n"
} ;

We once again need to add the entry for rangers. Insert the following line

      "Ranger",
between the line containing "Warrior" and the line containing the newline character.

Now that we've finished with the header file, we can move on to the "shop.c" file, to update the is_ok_char function, which starts at about line 52. Go down to about line 73 and find the following "if" statement:

  if ((IS_MAGIC_USER(ch) && NOTRADE_MAGIC_USER(shop_nr)) ||
      (IS_CLERIC(ch) && NOTRADE_CLERIC(shop_nr)) ||
      (IS_THIEF(ch) && NOTRADE_THIEF(shop_nr)) ||
      (IS_WARRIOR(ch) && NOTRADE_WARRIOR(shop_nr))) {
    sprintf(buf, "%s %s", GET_NAME(ch), MSG_NO_SELL_CLASS);
    do_tell(keeper, buf, cmd_tell, 0);
    return (FALSE);
  }
In the parentheses between the "if" and the opening brace, we need to add a line to take advantage of the changes we've made in "shop.h". So, change the line containing IS_WARRIOR to the following two lines:
      (IS_WARRIOR(ch) && NOTRADE_WARRIOR(shop_nr)) ||
      (IS_RANGER(ch) && NOTRADE_RANGER(shop_nr))) {
This done, we can now move on to "utils.h", and look for the following block of code at around line 409:

#define CLASS_ABBR(ch) (IS_NPC(ch) ? "--" : class_abbrevs[(int)GET_CLASS(ch)])

#define IS_MAGIC_USER(ch)       (!IS_NPC(ch) && \
                                (GET_CLASS(ch) == CLASS_MAGIC_USER))
#define IS_CLERIC(ch)           (!IS_NPC(ch) && \
                                (GET_CLASS(ch) == CLASS_CLERIC))
#define IS_THIEF(ch)            (!IS_NPC(ch) && \
                                (GET_CLASS(ch) == CLASS_THIEF))
#define IS_WARRIOR(ch)          (!IS_NPC(ch) && \
                                (GET_CLASS(ch) == CLASS_WARRIOR))
At the end of this block, add the following lines:
#define IS_RANGER(ch)           (!IS_NPC(ch) && \
                                (GET_CLASS(ch) == CLASS_RANGER))
Now move on to "structs.h", and at about line 77 look for the following block of code:

/* PC classes */
#define CLASS_UNDEFINED   -1
#define CLASS_MAGIC_USER  0
#define CLASS_CLERIC      1
#define CLASS_THIEF       2
#define CLASS_WARRIOR     3

#define NUM_CLASSES       4  /* This must be the number of classes!! */
We will assign CLASS_RANGER the next available value. Insert the following line after the CLASS_WARRIOR line:
#define CLASS_RANGER      4
and increment the 4 in the NUM_CLASSES line to a 5 to reflect the addition of the class. Now move down to about line 297 and look for the following block of bitvectors:

/* Extra object flags: used by obj_data.obj_flags.extra_flags */
#define ITEM_GLOW          (1 << 0)     /* Item is glowing              */
#define ITEM_HUM           (1 << 1)     /* Item is humming              */
#define ITEM_NORENT        (1 << 2)     /* Item cannot be rented        */
#define ITEM_NODONATE      (1 << 3)     /* Item cannot be donated       */
#define ITEM_NOINVIS       (1 << 4)     /* Item cannot be made invis    */
#define ITEM_INVISIBLE     (1 << 5)     /* Item is invisible            */
#define ITEM_MAGIC         (1 << 6)     /* Item is magical              */
#define ITEM_NODROP        (1 << 7)     /* Item is cursed: can't drop   */
#define ITEM_BLESS         (1 << 8)     /* Item is blessed              */
#define ITEM_ANTI_GOOD     (1 << 9)     /* Not usable by good people    */
#define ITEM_ANTI_EVIL     (1 << 10)    /* Not usable by evil people    */
#define ITEM_ANTI_NEUTRAL  (1 << 11)    /* Not usable by neutral people */
#define ITEM_ANTI_MAGIC_USER (1 << 12)  /* Not usable by mages          */
#define ITEM_ANTI_CLERIC   (1 << 13)    /* Not usable by clerics        */
#define ITEM_ANTI_THIEF    (1 << 14)    /* Not usable by thieves        */
#define ITEM_ANTI_WARRIOR  (1 << 15)    /* Not usable by warriors       */
#define ITEM_NOSELL        (1 << 16)    /* Shopkeepers won't touch it   */
We will assign ITEM_ANTIRANGER the next unused bitvector. Insert the following line after the ITEM_NOSELL entry:
#define ITEM_ANTI_RANGER   (1 << 17)    /* Not usable by rangers        */
At this point, it's time to play builder. You need to add a guild for the new class, along with the guildmaster to teach rangers their skills, the guildguard to bar the way to the guildmaster against members of other classes, and whatever additional characters are appropriate to the scenery. Typically, this would include a waiter or waitress for the guild bar, and a number of "Peacekeepers" and "Cityguards." To do this, you need to examine and modify one each of the .wld, .mob and .zon files. Since the default starting point is Temple of Midgaard, which is in area 30, you will need to edit "30.wld", "30.mob", "30.zon" and "30.shp", and add the rooms and the new characters. The reason to do this now rather than after the changes are made to "class.c" is that you need the identification numbers for the rooms in order to make one of the additions.

While normally you could leave building up to a separate builder (unless you are your own builder anyway), adding a guild involves a couple of changes in the code as well as in the world files, and those changes, at last check, are not documented elsewhere. Most of the world file information that will be added at this point is copied from existing objects, or is a composite of characteristics of similar world objects. I will not go into detail about how to build; there is already a very good technical document on the subject by Jeremy Elson, which is included in the CircleMUD 3.0 distribution as "building.doc", and there are a number of documents out on the Internet which give advice about designing areas. Feel free to make changes here and there, but do so responsibly and with "building.doc" available.

The world files are located in a group of directories contained in the "world" section of the "lib" directory, which contains the games data library. The "lib" directory can be found in the same directory as the "src" directory containing the source code files we've been working with. Each type of world file has its own directory: one for room data ("wld"), one for objects ("obj"), one for shop rules ("shp"), one for mobile definitions ("mob"), and one for the initial placement information for mobiles and objects in the various rooms ("zon").

The first thing to do is to add the rooms, so go into the "wld" directory and open "30.wld" and look for a good gap in the room numbers, and a good place to attach the guild. This example attaches the guild to the north of a room called "Inside the West Gate of Midgaard." In an unaltered copy of the file, the room numbers in area 30 stop at 3066, so this example will start with room 3070. Go the end of the file, and insert the following between the last two lines, one of which contains only the letter "S" and the other contains only a dollar sign:

#3070
The Entrance to the Rangers' Guild~
  The entrance to the Rangers' Guild looks like the inside of a log cabin
with no furniture windows and no furniture.  The lighting is coming from
sconces on the walls, and a fire burns in a stone hearth.  A doorway that
seems to have been just cut out of the west wall leads to the bar.  An
archway to the south leads out to the West Gate of Midgaard.
~
30 d 0
D2
You can see the West Gate of Midgaard out there.
~
~
0 -1 3040
D3
The bar looks like it might be only slightly more comfortable than the room
you are in.
~
~
0 -1 3071
E
fireplace hearth fire~
You see nothing special about the hearth.
~
sconce sconces~
You see nothing special about the sconces.
~
S
#3071
The Bar of the Feathered Cap~
  The bar looked a lot less interesting from the outside.  Now that
you're here, you can see a couple of couches, a wooden Indian, and the
trophy heads from the Sackman family's latest trip to Africa.  The room is
lit by sconces, and a fire burns in a stone hearth.  The stone archway to
the north leads to your Guildmaster's Inner Sanctum.  The guild entrance
is through the doorway cut into the east wall.  There's a sign hanging on
the west wall.
~
30 d 0
D0
To the north lies the Inner Sanctum of your Guildmaster.
~
~
0 -1 3072
D1
You get a glimpse of the guild entrance hall.
~
~
0 -1 3070
E
fireplace hearth fire~
You see nothing special about the hearth.
~
E
sconce sconces~
You see nothing special about the sconces.
~
E
head trophy heads trophies~
You see a zebra, an elephant, and MY GOD that must have been one HUGE
rhino.
~
E
couch couches~
The couches seem comfortable enough.
~
E
wooden indian~
He probably should be holding cigars or something, but he isn't.  He looks
almost alive, but a quick knock confirms that he's made of solid wood.
~
E
sign~
The sign reads:
  Here's the drill:
  Buy  - Buy something drinkable from the waitress.
  List - The waitress will read off the price list.
      The waitress, by the way, is not for sale.
~
S
#3072
The Rangers' Inner Sanctum~
  There is a decidedly...foresty feel to the place.  The walls are made
of tree trunks interspersed with bookshelves.  Above you is a ceiling of
branches and leaves.  In the middle of one huge tree trunk in the south
wall is an archway that leads out to the bar.  The floor is carpeted with
flowers.  A hollowed-out tree trunk is in the center of the room.  The
vapors coming up from it would probably smell horrid, except for the
overwhelmingly sweet scent of forest flowers.
~
30 d 0
D2
Through the archway in the really big tree you see...the bar.
~
~
0 -1 3071
D5
There's a well down that tree trunk.  It goes straight down about ten feet
and then takes a sharp turn that probably leads into another well.  The
sides are sheer, and if you went down there's no way you could climb back up.
Also, it's dark down there.
~
~
0 -1 7026
E
tree trunk hollow well~
There's a well down that tree trunk.  It goes straight down about ten feet
and then takes a sharp turn that probably leads into another well.  The
sides are sheer, and if you went down there's no way you could climb back up.
Also, it's dark down there.
~
S
That should complete the addition of the rooms themselves, along with a few interesting pieces of scenery. To make them accessible, go to about line 797 and find the section for room #3040, which starts with:
#3040
Inside The West Gate Of Midgaard~
  You are by two small towers that have been built into the city wall and
connected with a footbridge across the heavy wooden gate.  Main Street
leads east and Wall Road leads south from here.
~
30 0 1
D1
You see Main Street.
~
~
0 -1 3012
Between the line that says "D1" and the line above it, insert the following lines:
D0
You see the entrance to the Rangers' Guild.
~
~
0 -1 3070
and amend the description of the room as follows:
  You are by two small towers that have been built into the city wall and
connected with a footbridge across the heavy wooden gate.  Main Street
leads east and Wall Road leads south from here.  To the north is the
entrance to the Rangers' Guild.
The rooms are now connected to the rest of the map. The next step is to create the characters who inhabit them. Close "30.wld", and open up "30.mob". Go down to about line 240, where the section for mob #3027, the knight, ends and the section for mob #3040, the bartender, begins. Insert the following between the last line of #3027 and the first line of #3040:
#3028
guildmaster master frog kermit~
the rangers' guildmaster~
Your guildmaster is a tall, lanky frog, perched on a lily pad in the corner.
~
In a strange way it almost makes sense, since a frog is a creature of
nature, that he'd be your guildmaster.  Except that he's singing and
playing a banjo.  Still, he's got a nice voice, and the heartfelt strains
of "The Rainbow Connection" give you a warm, fuzzy feeling.  He's dressed
all in green, a slightly darker shade than his skin.
~
abelnop dh 1000 S
34 0 -10 6d10+640 1d8+24
18794 100000
8 8 1
#3029
guard huntress~
the huntress~
A huntress plays with her sword as she guards the entrance.
~
The huntress is tall, attractive and deadly.  Don't mess with her.  She
doesn't like troublemakers.  Rumor has it she got this job after she
single-handedly cleaned all of the high-level monsters out of what is now
a newbie zone.  The only reason she's not a guildmaster is that she can't
sing.
~
ablnop dfh 1000 S
33 0 -5 6d10+990 1d8+22
2000 16000
8 8 2
#3030
waitress girl~
the waitress~
A rather young waitress in green is standing here.
~
This 14-year-old girl might fool a member of another guild, but you know
that she's one tough cookie.  She very friendly, a little flirtatious
even, but woe to anyone who angers her.  She specializes in bruised egos
and severed limbs.
~
bno 0 600 S
23 1 2 6d1+390 1d8+12
2000 80000
8 8 2
This creates the template for the guildmaster, guildguard and waitress for the guild bar. Close the mob file and open "30.zon". This is where the characters, rooms and objects all get tied together. Go to about line 61 and find the following entries:

M 0 3020 1 3019         Magic Users' Guildmaster
M 0 3021 1 3002         Clerics' Guildmaster
M 0 3022 1 3029         Thieves' Guildmaster
M 0 3023 1 3023         Warriors' Guildmaster
M 0 3024 1 3017         Mage Guard
E 1 3022 100 16         Long Sword
M 0 3025 1 3004         Priest Templar Guard
E 1 3022 100 16         Long Sword
M 0 3026 1 3027         Thief Guard
E 1 3022 100 16         Long Sword
M 0 3027 1 3021         Warrior Guard
E 1 3022 100 16         Long Sword
Between the lines for the Warriors' Guildmaster and the Mage Guard, add the following line to place the Rangers' guildmaster in the Inner Sanctum:
M 0 3028 1 3072         Rangers' Guildmaster
At the end of the block, after the Warrior Guard and his Long Sword, add the following lines to place the Huntress we've created as the guildguard at the entrance to the guild:
M 0 3029 1 3070         Ranger Guard
E 1 3022 100 16         Long Sword
Now, skip down to about line 83 and look for the following section:
M 0 3042 1 3018         Magic Users' Waiter
G 1 3002 100            Dark Ale Bottle
G 1 3003 100            Firebreather
E 1 3020 100 16         Dagger
M 0 3043 1 3003         Clerics' Waiter
G 1 3002 100            Dark Ale Bottle
G 1 3004 100            Local Bottle
E 1 3024 100 16         Warhammer
M 0 3044 1 3028         Thieves' Waiter
G 1 3003 100            Firebreather
G 1 3004 100            Local Bottle
E 1 3021 100 16         Small Sword
M 0 3045 1 3022         Fighters' Waiter
G 1 3002 100            Dark Ale Bottle
G 1 3003 100            Firebreather
G 1 3004 100            Local Bottle
E 1 3022 100 16         Long Sword
After these entries, insert the following lines to add the waitress:
M 0 3030 1 3071         Rangers' Waitress
G 1 3003 100            Firebreather
G 1 3004 100            Local Bottle
E 1 3021 100 16         Small Sword
Now find the block Peacekeepers and Cityguards, starting at about line 108. Change the five in the Peacekeeper lines to a six, and add the following line after the existing Peacekeepers:
M 0 3059 6 3059         Peacekeeper
At the end of the batch of Cityguards, insert the following lines:
M 0 3060 10 3070        Cityguard
E 1 3022 100 16         Long Sword
The number to be changed in the Peacekeeper entries is the limit on how many the system can have active at any one time. If left as it was, the added Peacekeeper would not be used, since the limit would be five and the additional one would be a sixth. It is not necessary to change the number for the Cityguards, since there are few enough already.

The last things we need to add in this file are a bulletin board and an ATM, and then our new class's guild will be outfitted the same as the other four. Go down to about line 157, and find the block paired lines that remove and re-add the ATM's. After the last one, add the following two lines:

R 0 3070 3034
O 1 3034 10 3070        ATM
And finally, at about line 175, find the batch of lines that do the same thing for a group of Social Bulletin Boards. After them, insert the following lines:
R 0 3071 3096
O 1 3096 5 3071         Social Bulletin Board
Now close "30.zon" and open "30.shp" so we can define the shopkeeper information for the waitress, and exclude Rangers from the bars in the other guilds. The shop handling routines have been completely rewritten for version 3.0 of CircleMUD by Jeff Fink, (building.doc, shop.c), and this file is in the new format.

The first priority here is to make the waitress a shopkeeper. Go to about line 252 and find the following block:

#3040~
3000
3001
3002
3003
3004
-1
1.1
1.0
-1
That block is the start of shopkeeper number 3040. Since the waitress is shopkeeper number 3030, to follow the existing pattern, insert the following code before the start of shopkeeper 3040:
#3030~
3003
3004
-1
1.7
1.0
-1
%s Haven't got that on storage - try list!~
%s I don't buy!~
%s I don't buy!~
%s I don't buy!~
%s I'm sorry, I don't do tabs.~
%s That'll be %d coins.~
%s Oops - %d a minor bug - please report!~
0
2
3030
120
3071
-1
0
28
0
0
The waitress will now be able to sell beverages. All that remains before tackling "class.c" is to find the other guild waiters, and adjust their bitvectors to disallow rangers. So, go down to about line 279 and find the top of shopkeeper number 3042, who is the waiter in the mages' guild:
#3042~
3002
3003
-1
1.0
1.0
-1
%s Haven't got that on storage - try list!~
%s I don't buy!~
%s I don't buy!~
%s I don't buy!~
%s If you have no money, you'll have to go!~
%s That'll be %d coins.~
%s Oops - %d a minor bug - please report!~
0
2
3042
112
3018
-1
The fourth line after the last line starting with a %s is the bitvector. Since the bitvector value for TRADE_NORANGERS is 128, we need to add 128 to this value. Since the bitvector value for this shopkeeper is 112, replace the 112 with a 250 to prevent this waiter from selling to rangers. Also add 128 to the bitvectors for shopkeeper numbers 3043, 3044 and 3045, who are the clerics' thieves', and fighters' guild waiters, respectively. In case you haven't figured it out by now, every object defined in a .wld, .mob, .zon and .shp file starts with a # sign followed immediately by the object number. Then close "30.shp".

The last thing that must be done before moving on to "class.c" is to assign the guildmaster and guildguard their special powers in the source code. Open "spec_assign.c", and look around line 92 for the following block of code:

/* Midgaard */
  ASSIGNMOB(3005, receptionist);
  ASSIGNMOB(3010, postmaster);
  ASSIGNMOB(3020, guild);
  ASSIGNMOB(3021, guild);
  ASSIGNMOB(3022, guild);
  ASSIGNMOB(3023, guild);
  ASSIGNMOB(3024, guild_guard);
  ASSIGNMOB(3025, guild_guard);
  ASSIGNMOB(3026, guild_guard);
  ASSIGNMOB(3027, guild_guard);
  ASSIGNMOB(3059, cityguard);
  ASSIGNMOB(3060, cityguard);
  ASSIGNMOB(3061, janitor);
  ASSIGNMOB(3062, fido);
  ASSIGNMOB(3066, fido);
  ASSIGNMOB(3067, cityguard);
  ASSIGNMOB(3068, janitor);
  ASSIGNMOB(3095, cryogenicist);
  ASSIGNMOB(3105, mayor);
The ASSIGNMOB function, which is defined in this file, is used to assign special powers to groups of mobiles who are based on the same entry in a .mob file. In this case, we want to assign guild powers to mob #3028, and guild_guard powers to mob #3029, who are our guildmaster and guildguard, respectively. After the line that assigns guild_guard powers to mob #3027, insert the following two lines:
  ASSIGNMOB(3028, guild);
  ASSIGNMOB(3029, guild_guard);
We are now ready to move on to "class.c", where we are going to make the rest of the changes. Many of these are once again of the copy-paste-modify variety, but a few of them allow for a little creativity. Since everything here is classes- related, the blocks being changed will appear pretty much one right after the other.

We can start at about line 32, where we find the declaration for the class_abbrevs and pc_class_types array constants:

const char *class_abbrevs[] = {
  "Mu",
  "Cl",
  "Th",
  "Wa",
  "\n"
};


const char *pc_class_types[] = {
  "Magic User",
  "Cleric",
  "Thief",
  "Warrior",
  "\n"
};

Between the "Wa", and "\n" lines in class_abbrevs, insert the following line with the abbreviation for Ranger:

  "Ra",
and add the following line between the "Warrior", and "\n" lines in pc_class_types:
  "Ranger",
Next we have the array constant class_menu, which contains the menu entries for new players choosing a class:
const char *class_menu =
"\r\n"
"Select a class:\r\n"
"  [C]leric\r\n"
"  [T]hief\r\n"
"  [W]arrior\r\n"
"  [M]agic-user\r\n";
Take the semicolon off of the end of the " [M]agic-user\r\n" line, and insert the following line below it:
"  [R]anger\r\n";
Right below class_menu in the function parse_class, whose job it is to interpret the response to the menu:

int parse_class(char arg)
{
  arg = LOWER(arg);

  switch (arg) {
  case 'm':
    return CLASS_MAGIC_USER;
    break;
  case 'c':
    return CLASS_CLERIC;
    break;
  case 'w':
    return CLASS_WARRIOR;
    break;
  case 't':
    return CLASS_THIEF;
    break;
  default:
    return CLASS_UNDEFINED;
    break;
  }
}
Insert the following lines above the default: case:
  case 'r':
    return CLASS_RANGER;
    break;
After parse_class is the function find_class_bitvector, which takes the same argument as parse_class but returns the bitvector rather than the class value:

long find_class_bitvector(char arg)
{
  arg = LOWER(arg);

  switch (arg) {
    case 'm':
      return (1 << 0);
      break;
    case 'c':
      return (1 << 1);
      break;
    case 't':
      return (1 << 2);
      break;
    case 'w':
      return (1 << 3);
      break;
    default:
      return 0;
      break;
  }
}
Insert the following lines above the default: case:
  case 'r':
    return (1 << 4);
    break;
Now, skip down to about line 161 and find the following definition for prac_params:

int prac_params[4][NUM_CLASSES] = {
  /* MAG        CLE     THE     WAR */
  {95,          95,     85,     80},            /* learned level */
  {100,         100,    12,     12},            /* max per prac */
  {25,          25,     0,      0,},            /* min per pac */
  {SPELL,       SPELL,  SKILL,  SKILL}          /* prac name */
};
This one is a little different. Instead of adding a line, this definition needs a column added. Also, this is the point at which we need to decide if our class uses skills or spells. Selecting one or the other now does not preclude the ability to use the other, it is merely cosmetic. For the rangers, my personal preference would be to choose skills, but to make them easier to learn than the skills of a thief or warrior, and with a greater knowledge required to be considered "learned", which is the point beyond which the spell or skill cannot be practiced. Replace the above section with the following:

int prac_params[4][NUM_CLASSES] = {
  /* MAG     CLE     THE     WAR     RAN */
  {  95,     95,     85,     80,     90},        /* learned level */
  {  100,    100,    12,     12,     30},        /* max per prac */
  {  25,     25,     0,      0,      10},        /* min per pac */
  {  SPELL,  SPELL,  SKILL,  SKILL,  SKILL}      /* prac name */
};
Below this, at about line 176, is the definition for guild_info, which controls the behavior of the guildguards:

int guild_info[][3] = {

/* Midgaard */
  {CLASS_MAGIC_USER,    3017,   SCMD_SOUTH},
  {CLASS_CLERIC,        3004,   SCMD_NORTH},
  {CLASS_THIEF,         3027,   SCMD_EAST},
  {CLASS_WARRIOR,       3021,   SCMD_EAST},

/* Brass Dragon */
  {-999 /* all */ ,     5065,   SCMD_WEST},

/* New Sparta */
  {CLASS_MAGIC_USER,    21075,  SCMD_NORTH},
  {CLASS_CLERIC,        21019,  SCMD_WEST},
  {CLASS_THIEF,         21014,  SCMD_SOUTH},
  {CLASS_WARRIOR,       21023,  SCMD_SOUTH},

/* this must go last -- add new guards above! */
  {-1, -1, -1}};
The format of these entries are:
  {class,               room,   direction}
where class is the class constant, room is the room ID number of the entrance to the guild, and direction is the direction constant for the direction from that room to the bar. Since the only guild that we've created specifically for the rangers is in Midgaard, insert the following line below the CLASS_WARRIOR entry for Midgaard: {CLASS_RANGER, 3070, SCMD_WEST}, The stock game distribution does not come with files for area 210, which contain the guilds in New Sparta. The area is, however, available for download from the CircleMUD ftp site, ftp.circlemud.org, in the /pub/CircleMUD/submissions/areas directory. The file new_sparta.tar.gz contains the area. If you want to use New Sparta, download the file and extract the five files within. Place them in the appropriate directories, add their names to the index files as appropriate, and then build (or have someone build) the connections between this area and the other areas in the MUD. If you want to place a guild in New Sparta, you can follow the a similar procedure to the guild addition above. Also, while the stock game contains the references for the guilds for "class.c", the references needed in "spec_assign.c" will still need to be added. There are other pre-built areas available for download from the CircleMUD ftp site, and some of them do include towns with guilds. The guilds in any areas you install or all will need to have the appropriate references placed in "class.c" and "spec_assign.c".

To continue adding the ranger class, below the guilds you will find the thaco (to hit armor class zero) array constant:

/* [class], [level] (all) */
const int thaco[NUM_CLASSES][LVL_IMPL + 1] = {

/* MAGE */
  /* 0                   5                  10                  15          */
  {100, 20, 20, 20, 19, 19, 19, 18, 18, 18, 17, 17, 17, 16, 16, 16, 15, 15,
  15, 14, 14, 14, 13, 13, 13, 12, 12, 12, 11, 11, 11, 10, 10, 10, 9},
  /* 20                  25                  30             */

/* CLERIC */
  /* 0                   5                  10                  15          */
  {100, 20, 20, 20, 18, 18, 18, 16, 16, 16, 14, 14, 14, 12, 12, 12, 10, 10,
  10, 8, 8, 8, 6, 6, 6, 4, 4, 4, 2, 2, 2, 1, 1, 1, 1},
  /* 20             25             30                               */

/* THIEF */
  /* 0                   5                  10                  15          */
  {100, 20, 20, 19, 19, 18, 18, 17, 17, 16, 16, 15, 15, 14, 13, 13, 12, 12,
  11, 11, 10, 10, 9, 9, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3},
  /* 20              25             30                              */

/* WARRIOR */
/* 0                   5                  10              15      */
  {100, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3,
  2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
  /* 20             25             30                               */
};
The THAC0 controls how easy it is for a mob to hit a member of given class at a given level, with higher numbers being easier hits. A section for the rangers is also needed here. After the closing brace in the warrior section (not the closing brace with the semicolon after it), which should be on about line 226, add a comma, and insert the following section between the warrior section and the line containing the closing brace and the semicolon:

/* RANGER */
  /* 0                   5                  10              15      */
  {100, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 9, 8, 8, 7, 7, 6,
  6, 5, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 1, 1, 1},
  /* 20             25             30                               */
While the other classes have roughly constant rates of improvement in their THAC0 from level to level, the rangers will have steady improvements in the first eleven levels at the same rate as the Warrior, and then the rate at which their THAC0 improves will gradually reduce along the way.

The next change is in the roll_real_abils function. When a player character is created, a set of random numbers is selected and assigned to the character's statistics, which are intelligence (int or intel), wisdom (wis), strength (str), dexterity (dex), charisma (cha) and conation (con). Depending on the player's class, the random numbers are then assigned with the highest values being placed in those statistics given highest priority for that class. For example, the mage gives top priority to intelligence, followed by wisdom, dexterity, strength, etc., while the warrior gives top priority to strength, then dexterity, conation, wisdom, etc. Find the switch statement in this function that determines which class and jumps to the assignment of stats, at about line 269. This switch statement should end at about 304 with a closing brace after the section for warriors. Above that brace, insert the following lines to assign the statistics based on the rangers' priorities:

  case CLASS_RANGER:
    ch->real_abils.str = table[0];
    ch->real_abils.con = table[1];
    ch->real_abils.dex = table[2];
    ch->real_abils.intel = table[3];
    ch->real_abils.wis = table[4];
    ch->real_abils.cha = table[5];
    break;
After the roll_real_abils function is the do_start function, which initializes new characters. At about line 329 is a switch statement which is responsible for conducting class-specific start-up actions on the characters:

  switch (GET_CLASS(ch)) {

  case CLASS_MAGIC_USER:
    break;

  case CLASS_CLERIC:
    break;

  case CLASS_THIEF:
    SET_SKILL(ch, SKILL_SNEAK, 10);
    SET_SKILL(ch, SKILL_HIDE, 5);
    SET_SKILL(ch, SKILL_STEAL, 15);
    SET_SKILL(ch, SKILL_BACKSTAB, 10);
    SET_SKILL(ch, SKILL_PICK_LOCK, 10);
    SET_SKILL(ch, SKILL_TRACK, 10);
    break;

  case CLASS_WARRIOR:
    break;
  }
You will notice that the only class which actually has startup actions here is the thief. At this point, there is no reason for any startup activities for the ranger, so we will insert an empty entry. Between the two lines for the warrior and the closing brace, insert the following:
  case CLASS_RANGER:
    break;
The next function, advance_level, is triggered when a player acquires enough experience to advance to the next level. The switch statement for class-specific actions starts at about line 382:

  switch (GET_CLASS(ch)) {

  case CLASS_MAGIC_USER:
    add_hp += number(3, 8);
    add_mana = number(GET_LEVEL(ch), (int) (1.5 * GET_LEVEL(ch)));
    add_mana = MIN(add_mana, 10);
    add_move = number(0, 2);
    break;

  case CLASS_CLERIC:
    add_hp += number(5, 10);
    add_mana = number(GET_LEVEL(ch), (int) (1.5 * GET_LEVEL(ch)));
    add_mana = MIN(add_mana, 10);
    add_move = number(0, 2);
    break;

  case CLASS_THIEF:
    add_hp += number(7, 13);
    add_mana = 0;
    add_move = number(1, 3);
    break;

  case CLASS_WARRIOR:
    add_hp += number(10, 15);
    add_mana = 0;
    add_move = number(1, 3);
    break;
  }
Between the section for the warrior and the closing brace, insert the following code:

  case CLASS_RANGER:
    add_hp += number(7, 13);
    add_mana = number((int) (0.5 * GET_LEVEL(ch)), GET_LEVEL(ch));
    add_mana = MIN(add_mana, 5);
    add_move = number(2, 4);
    break;
Now, jump down to about line 472 and find the function invalid_class, which is used to determine if a member of a given class can use a piece of equipment:

int invalid_class(struct char_data *ch, struct obj_data *obj) {
  if ((IS_OBJ_STAT(obj, ITEM_ANTI_MAGIC_USER) && IS_MAGIC_USER(ch)) ||
      (IS_OBJ_STAT(obj, ITEM_ANTI_CLERIC) && IS_CLERIC(ch)) ||
      (IS_OBJ_STAT(obj, ITEM_ANTI_WARRIOR) && IS_WARRIOR(ch)) ||
      (IS_OBJ_STAT(obj, ITEM_ANTI_THIEF) && IS_THIEF(ch)))
        return 1;
  else
        return 0;
}
We need to add an entry for rangers, so replace the thief line with the following two lines:
      (IS_OBJ_STAT(obj, ITEM_ANTI_THIEF) && IS_THIEF(ch)) ||
      (IS_OBJ_STAT(obj, ITEM_ANTI_RANGER) && IS_RANGER(ch)))
The next function is init_spell_levels, which configures which classes can learn which spells or skills at which levels. Since we don't have any spells or skills specific to the Ranger at this time, we'll just borrow a couple from some of the other classes for now, and make them available for practice at different levels than they would be to their own class. We can come back and replace or supplement them with some more unique abilities later.

Above the closing brace for the function, at about line 561, insert the following:

  /* RANGERS */
  spell_level(SKILL_KICK, CLASS_RANGER, 1);
  spell_level(SPELL_CURE_LIGHT, CLASS_RANGER, 3);
  spell_level(SKILL_RESCUE, CLASS_RANGER, 5);
  spell_level(SKILL_TRACK, CLASS_RANGER, 5);
  spell_level(SKILL_HIDE, CLASS_RANGER, 9);
  spell_level(SPELL_INFRAVISION, CLASS_RANGER, 9);
  spell_level(SPELL_CREATE_FOOD, CLASS_RANGER, 12);
  spell_level(SPELL_DETECT_POISON, CLASS_RANGER, 12);
  spell_level(SPELL_CREATE_WATER, CLASS_RANGER, 12);
  spell_level(SPELL_REMOVE_POISON, CLASS_RANGER, 15);
  spell_level(SPELL_HEAL, CLASS_RANGER, 16);
  spell_level(SPELL_CONTROL_WEATHER, CLASS_RANGER, 25);
This should give the ranger a small selection of spells and skills borrowed from other classes. You can always come back and add to the list, although I'm not certain what the side-effects would be if you remove any entries from the list.

Below init_spell_levels is the definition of the array constant title_type, which contains the titles for male and female members of the various classes at each level, along with the amount of experience the player will need to reach that level. Go to about line 726, which contains a closing brace followed by semicolon. This should be the last actual line of the file. Put a comma after the closing brace two lines up which finishes the definitions for the warriors, and insert the following code between the two lines with closing braces:

  {{"the Man", "the Woman", 0},
  {"the Fledgling", "the Fledgling", 1},
  {"the Tenderfoot", "the Tenderfoot", 1500},
  {"the Cub", "the Kitten", 3000},
  {"the Fox Pup", "the Fox Kit", 6000},
  {"the Wolf Cub", "the Wolf Cub", 13000},
  {"the Bear Cub", "the Bear Cub", 27500},
  {"the Tiger Cub", "the Tiger Kitten", 55000},
  {"the Lion Cub", "the Lion Kitten", 110000},
  {"the Fledgling Hawk", "the Fledgling Hawk", 225000},
  {"the Fledgling Eagle", "the Fledgling Eagle", 450000},
  {"the Ferret", "the Ferret", 675000},
  {"the Fox", "the Vixen", 900000},
  {"the Bobcat", "the Bobcat", 1125000},
  {"the Wolf", "the Wolf", 1350000},
  {"the Bear", "the Bear", 1575000},
  {"the Tiger", "the Tigress", 1800000},
  {"the Lion", "the Lioness", 2100000},
  {"the Falcon", "the Peregrine", 2400000},
  {"the Hawk", "the Lady Hawk", 2700000},
  {"the Eagle", "the Lady Eagle", 3000000},
  {"the Scout", "the Lady Scout", 3250000},
  {"the Woodsman", "the Woodswoman", 3500000},
  {"the Pathfinder", "the Pathfinder", 3800000},
  {"the Trailblazer", "the Trailblazer", 4100000},
  {"the Mountaineer", "the Mountainere", 4400000},
  {"the Ranger", "the Lady Ranger", 4800000},
  {"the Hunter", "the Huntress", 5200000},
  {"the High Ranger", "the High Lady Ranger", 5600000},
  {"the Great Lord Ranger", "the Great Lady Ranger", 6000000},
  {"the Great Lord Hunter", "the Great Lady Huntress", 6400000},
  {"the Immortal Lord Hunter", "the Immortal Lady Huntress", 7000000},
  {"the Patron of the Hunt", "the Matron of the Hunt", 9000000},
  {"the God of the Forests", "the Goddess of the Forests", 9500000},
  {"the Implementor", "the Implementress", 10000000}
  }
Now, save and close "class.c".

There are only a few more changes we need to make before we can compile and build the program, changes which are side-effects of the abilities we've given the new class.

The "kick" and "rescue" abilities we've given the rangers are currently reserved for the warrior class, so we need to enable them for rangers as well. Otherwise any ranger player who tries to make use of these skills will just end up wasting their practice sessions. Open "act.offensive.c" and look around line 372 for the following block of code:

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

  if (GET_CLASS(ch) != CLASS_WARRIOR) {
    send_to_char("You'd better leave all the martial arts to fighters.\r\n", ch);
    return;
  }
The if statement in the sixth line of do_kick needs to be modified to allow rangers as well as warriors to use the spell. Modify this line as follows:
  if ((GET_CLASS(ch) != CLASS_WARRIOR) && (GET_CLASS(ch) != CLASS_RANGER)) {

Now go down to about line 341, and find the following lines in do_rescue:

  if (GET_CLASS(ch) != CLASS_WARRIOR)
    send_to_char("But only true warriors can do this!", ch);
  else {
    percent = number(1, 101);   /* 101% is a complete failure */
    prob = GET_SKILL(ch, SKILL_RESCUE);
Amend the if statement as follows:
  if ((GET_CLASS(ch) != CLASS_WARRIOR) && (GET_CLASS(ch) != CLASS_RANGER))
Close "act.offensive.c", and open up "limits.c". Because our rangers have use of spells, it would be nice if they gained back their mana points at an accelerated rate, as with mages and clerics. These two classes regain their mana at double the rate of the other two. We don't want rangers to gain back mana quite so fast, we just want them to recover about fifty percent faster than the warrior or thief. Look around line 95 for the following if statement in the mana_gain function:

    if ((GET_CLASS(ch) == CLASS_MAGIC_USER) || (GET_CLASS(ch) == CLASS_CLERIC))
      gain <<= 1;
Right below this if statement, add the following:
    if (GET_CLASS(ch) == CLASS_RANGER)
      gain = gain + (gain >> 1);
The mages and clerics have a price to pay for their accelerated mana regeneration. They gain back hit points more slowly than other classes. While we will not be charging the Rangers this price, make note of a very similar if statement to the one above in the hit_gain function, around line 143, for future reference. However, the nature of the ranger makes it desirable for them to regain movement points a little faster than members of the other classes, say about one-quarter faster. Go down to about line 186 in the move_gain function, and find a closing brace that ends a switch statement, followed on the next line by a closing brace that ends an else clause. Between these two lines, insert the following:

    if (GET_CLASS(ch) == CLASS_RANGER)
      gain = gain + (gain >> 2);
Finally, close "limits.c", and 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, you should have a new, fully usable player class: the Ranger. As a finishing touch, you or your builder should go through your shop and object files to make some of the equipment and stores unavailable to rangers.