Xiu just made a race patch for tbaMUD 3.54, check it out:
http://www.tbamud.com/forum?c=showthread&ThreadID=68

If you have any additions or corrections please stop by the 
Builder Academy at telnet://tbamud.com:9091 or email:
rumble@tbamud.com -- Rumble

Files needed: 
  structs.h -- defining races, adding pfile stuff, etc
  utils.h -- get_race defines
  class.c -- race abrevations, race select, abilities
  constants.c -- race select, apply_race
  db.c -- get_race defines
  handler.c -- eq defines
  interpreter.c -- select race defines

Editors Note: Races are very complex, and this tutorial will only detail the simple aspects of race defines and newbie race selection.

Open structs.h and search for:

/* NPC classes (currently unused - feel free to implement!) */
#define CLASS_OTHER       0
#define CLASS_UNDEAD      1
#define CLASS_HUMANOID    2
#define CLASS_ANIMAL      3
#define CLASS_DRAGON      4
#define CLASS_GIANT       5

Right below it, put the following 
/* PC races */              
#define RACE_UNDEFINED   -1 
#define RACE_HUMAN        0 
#define RACE_ELF          1 
#define RACE_GNOME        2 
#define RACE_FAIRY        3 

#define NUM_RACES         4 

Now search for 
#define CON_DELCNF1      15             /* Delete confirmation 1        */
#define CON_DELCNF2      16             /* Delete confirmation 2        */

Right below it, put the following 
#define CON_QRACE        17             /* Race?                        */ 

Now search for 
#define ITEM_ANTI_WARRIOR     15   /* Not usable by warriors */
#define ITEM_NOSELL           16   /* Shopkeepers won't touch it */

Right below it, put the following 
#define ITEM_ANTI_HUMAN       17   /* Not usable by Humans   */ 
#define ITEM_ANTI_ELF         18   /* Not usable by Elves    */ 
#define ITEM_ANTI_GNOME       19   /* Not usable by Gnomes   */ 
#define ITEM_ANTI_FAIRY       20   /* Not usable by Fairies  */ 

Now search for 
#define APPLY_SAVING_BREATH    23       /* Apply to save throw: breath  */
#define APPLY_SAVING_SPELL     24       /* Apply to save throw: spells  */

Right below it, put the following 
#define APPLY_RACE             25       /* Reserved                     */ 

Now search for 
/* general player-related info, usually PC's and NPC's */
struct char_player_data {
   char	passwd[MAX_PWD_LENGTH+1]; /* character's password      */
   char	*name;	       /* PC / NPC s name (kill ...  )         */
   char	*short_descr;  /* for NPC 'actions'                    */
   char	*long_descr;   /* for 'look'			       */
   char	*description;  /* Extra descriptions                   */
   char	*title;        /* PC / NPC's title                     */
   byte sex;           /* PC / NPC's sex                       */
   byte chclass;       /* PC / NPC's class		       */
   byte level;         /* PC / NPC's level                     */
   struct time_data time;  /* PC's AGE in days                 */
   ubyte weight;       /* PC / NPC's weight                    */
   ubyte height;       /* PC / NPC's height                    */

Right below it, between byte chclass and the next variable, put the following 
   byte race;          /* PC / NPC's race                      */ 

Open utils.h and search for:
#define GET_REAL_LEVEL(ch) \
   (ch->desc && ch->desc->original ? GET_LEVEL(ch->desc->original) : \
    GET_LEVEL(ch))

#define GET_CLASS(ch)   ((ch)->player.class)

Right below it, put the following 
#define GET_RACE(ch)    ((ch)->player.race)  

Lastly search for 
#define IS_THIEF(ch)            (!IS_NPC(ch) && \
                                (GET_CLASS(ch) == CLASS_THIEF))
#define IS_WARRIOR(ch)          (!IS_NPC(ch) && \
                                (GET_CLASS(ch) == CLASS_WARRIOR))

Right below it, put the following 
#define IS_HUMAN(ch)            (!IS_NPC(ch) && \             
                                (GET_RACE(ch) == RACE_HUMAN)) 
                                                              
#define IS_ELF(ch)              (!IS_NPC(ch) && \             
                                (GET_RACE(ch) == RACE_ELF))   
                                                              
#define IS_GNOME(ch)            (!IS_NPC(ch) && \             
                                (GET_RACE(ch) == RACE_GNOME)) 
                                                              
#define IS_FAIRY(ch)            (!IS_NPC(ch) && \             
                                (GET_RACE(ch) == RACE_FAIRY)) 

Now, lets go on to the .C files, the core of it all. Open file class.c and search for:
const char *pc_class_types[] = {
  "Magic User",
  "Cleric",
  "Thief",
  "Warrior",
  "\n"
  };

Then put the following below it 
const char *race_abbrevs[] = {   
  "Hum",                         
  "Elf",                         
  "Gno",                         
  "Fai",                         
  "\n"                           
};                               
                                 
const char *pc_race_types[] = {  
  "Human",                       
  "Elf",                         
  "Gnome",                       
  "Fairy",                       
  "\n"
};

Now search for 
"Select a class:\r\n"
"  [C]leric\r\n"
"  [T]hief\r\n"
"  [W]arrior\r\n"
"  [M]agic-user\r\n";

And put the following right below it 
/* The menu for choosing a race in interpreter.c: */ 
const char *race_menu =                              
"\r\n"                                               
"Select a race:\r\n"                                 
"  [H]uman\r\n"                                      
"  [E]lf\r\n"
"  [G]nome\r\n"
"  [F]airy\r\n";

Now search for 
  case 'w':
    return CLASS_WARRIOR;
    break;
  case 't':
    return CLASS_THIEF;
    break;
  default:
    return CLASS_UNDEFINED;
    break;
  }
}

And block copy the following below it 
/*                                                                   
 * The code to interpret a race letter (used in interpreter.c when a 
 * new character is selecting a race).                               
 */                                                                  
int parse_race(char arg)                                             
{                                                                    
  arg = LOWER(arg);                                                  
                                                                     
  switch (arg) {                                                     
  case 'h':                                                          
    return RACE_HUMAN;                                               
    break;                                                           
  case 'e':                                                          
    return RACE_ELF;                                                 
    break;                                                           
  case 'g':                                                          
    return RACE_GNOME;                                               
    break;                                                           
  case 'f':                                                          
    return RACE_FAIRY;                                               
    break;                                                           
  default:                                                           
    return RACE_UNDEFINED;                                           
    break;                                                           
  }                                                                  
}                                                                    

Now search for 
    case 't':
      return 4;
      break;
    case 'w':
      return 8;
      break;
    default:
      return 0;
      break;
  }
}

Then place the following below it 
long find_race_bitvector(char arg)  {

  arg = LOWER(arg);                                                  
                                                                     
  switch (arg) {                                                     
    case 'h':                                                        
      return 1;                                                      
      break;                                                         
    case 'e':                                                        
      return 2;                                                      
      break;                                                         
    case 'g':                                                        
      return 4;                                                      
      break;                                                         
    case 'f':                                                        
      return 8;                                                      
      break;                                                         
    default:                                                         
      return 0;                                                      
      break;                                                         
  }                                                                  
}                                                                    

Now replace the description of void roll_real_abils(...) with this one 
/*
 * Roll the 6 stats for a character... each stat is made of the sum of
 * the best 3 out of 4 rolls of a 6-sided die.  Each class then decides
 * which priority will be given for the best to worst stats.  Race also 
 * affects stats.                                                       
 */

Now search for 
  case CLASS_WARRIOR:
    ch->real_abils.str = table[0];
    ch->real_abils.dex = table[1];
    ch->real_abils.con = table[2];
    ch->real_abils.wis = table[3];
    ch->real_abils.intel = table[4];
    ch->real_abils.cha = table[5];
    if (ch->real_abils.str == 18)
      ch->real_abils.str_add = number(0, 100);
    break;
  }

And directly below it, put this 
  switch (GET_RACE(ch)) {                       
  case RACE_HUMAN:                              
  ++ch->real_abils.con;                         
  break;                                        
  case RACE_ELF:                                
  ++ch->real_abils.dex;                         
  ++ch->real_abils.intel;                       
  --ch->real_abils.con;                         
  --ch->real_abils.str;                         
  break;                                        
  case RACE_GNOME:                              
  ch->real_abils.str+=3;                        
  --ch->real_abils.dex;                         
  --ch->real_abils.intel;                       
  --ch->real_abils.cha;                         
  break;                                        
  case RACE_FAIRY:                              
  ch->real_abils.dex+=2;                        
  ++ch->real_abils.wis;                         
  ++ch->real_abils.cha;                         
  ch->real_abils.str-=2;                        
  --ch->real_abils.con;                         
  break;                                        
 }                                              

You should have still left the end of the function the same though, like: 
  ch->aff_abils = ch->real_abils;               
}                                               

Okay, now, at the very END of the file, add this function 
int invalid_race(struct char_data *ch, struct obj_data *obj) {   
  if ((IS_OBJ_STAT(obj, ITEM_ANTI_HUMAN) && IS_HUMAN(ch)) ||     
      (IS_OBJ_STAT(obj, ITEM_ANTI_ELF)   && IS_ELF(ch)) ||       
      (IS_OBJ_STAT(obj, ITEM_ANTI_GNOME) && IS_GNOME(ch)) ||     
      (IS_OBJ_STAT(obj, ITEM_ANTI_FAIRY) && IS_FAIRY(ch)))       
        return 1;                                                
  else                                                           
        return 0;                                                
}                                                                

Open constants.c and search for:
/* CON_x */
const char *connected_types[] = {
  "Playing",
  "Disconnecting",
  "Get name",
  "Confirm name",
  "Get password",
  "Get new PW",
  "Confirm new PW",
  "Select sex",
  "Select class",
  "Reading MOTD",
  "Main Menu",
  "Get descript.",
  "Changing PW 1",
  "Changing PW 2",
  "Changing PW 3",
  "Self-Delete 1",
  "Self-Delete 2",

And add the following below it 
  "Select race",

Lastly search for 
/* APPLY_x */
const char *apply_types[] = {
  "NONE",
  "STR",
  "DEX",
  "INT",
  "WIS",
  "CON",
  "CHA",
  "CLASS",
  "LEVEL",
  "AGE",
  "CHAR_WEIGHT",
  "CHAR_HEIGHT",
  "MAXMANA",
  "MAXHIT",
  "MAXMOVE",
  "GOLD",
  "EXP",
  "ARMOR",
  "HITROLL",
  "DAMROLL",
  "SAVING_PARA",
  "SAVING_ROD",
  "SAVING_PETRI",
  "SAVING_BREATH",
  "SAVING_SPELL",

And add the following below it 
  "RACE",

Open db.c and search for:
    mob_proto[i].player.sex = t[2];

    mob_proto[i].player.class = 0;

Then put this after it 
    mob_proto[i].player.race = 0;  

Now search for 
  GET_SEX(ch) = st->sex;
  GET_CLASS(ch) = st->class;

Then add the following below it 
  GET_RACE(ch) = st->race;              

Lastly search for 
  st->sex = GET_SEX(ch);
  st->class = GET_CLASS(ch);

Then put this below it 
  st->race = GET_RACE(ch);

Open handler.c and search for:
  case APPLY_SAVING_SPELL:
    GET_SAVE(ch, SAVING_SPELL) += mod;
    break;

Put the following below it 
  case APPLY_RACE:              
    /* ??? GET_RACE(ch) += mod; */
    break;

Now search for 
void equip_char(struct char_data * ch, struct obj_data * obj, int pos)
{
  int j;
  int invalid_class(struct char_data *ch, struct obj_data *obj);

And add this below it 
  int invalid_race(struct char_data *ch, struct obj_data *obj);  

Lastly search for the following 
  if ((IS_OBJ_STAT(obj, ITEM_ANTI_EVIL) && IS_EVIL(ch)) ||
      (IS_OBJ_STAT(obj, ITEM_ANTI_GOOD) && IS_GOOD(ch)) ||
      (IS_OBJ_STAT(obj, ITEM_ANTI_NEUTRAL) && IS_NEUTRAL(ch)) ||
      invalid_class(ch, obj)) {

and REPLACE the entire thing with the following: 
  if ((IS_OBJ_STAT(obj, ITEM_ANTI_EVIL) && IS_EVIL(ch)) ||
      (IS_OBJ_STAT(obj, ITEM_ANTI_GOOD) && IS_GOOD(ch)) ||
      (IS_OBJ_STAT(obj, ITEM_ANTI_NEUTRAL) && IS_NEUTRAL(ch)) ||
      invalid_class(ch, obj) || invalid_race(ch, obj)) {   

Open interpreter.c and search for:
  extern const char *class_menu;

Then add this below it 
  extern const char *race_menu;   

Now, search for (its right below it) 
  sh_int load_room;

  int load_char(char *name, struct char_file_u * char_element);
  int parse_class(char arg);

And add this... 
  int parse_race(char arg);     

Now replace the following two CASE statements for the ones that already exist (THIS IS VERY IMPORTANT!) completely erase old case CON_QCLASS, and add this one plus the additional new case CON_QRACE at the end of it... 
  case CON_QCLASS:
    if ((GET_CLASS(d->character) = parse_class(*arg)) == CLASS_UNDEFINED) {
      SEND_TO_Q("\r\nThat's not a class.\r\nClass: ", d);
      return;
    }
    SEND_TO_Q(race_menu, d);         
    SEND_TO_Q("\r\nRace: ", d);      
    STATE(d) = CON_QRACE;            
    break;                           

  case CON_QRACE:                                                        
    if ((GET_RACE(d->character) = parse_race(*arg)) == CLASS_UNDEFINED) {
      SEND_TO_Q("\r\nThat's not a race.\r\nRace: ", d);                  
      return;                                                            
    }                                                                    
                                                                         
    if (GETPFILEPOS(d->character) < 0)
      GETPFILEPOS(d->character) = create_entry(GET_NAME(d->character);
    init_char(d->character);
    save_char(d->character, NOWHERE);
    SEND_TO_Q(motd, d);                                                  
    SEND_TO_Q("\r\n\n*** PRESS RETURN: ", d);                            
    STATE(d) = CON_RMOTD;                                                
                                                                         
    sprintf(buf, "%s [%s] new player.", GET_NAME(d->character), d->host);
    mudlog(buf, NRM, LVL_IMMORT, TRUE);                                  
    break;                                                               

Originally created by Nick C.