WTFaq - Adding races...
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 definesEditors Note: Races are very complex, and this tutorial will only detail the simple aspects of race defines and newbie race selection. First, let's open up the header (.h) files, as they are very important to the functioning of the program (.c) files.
OPEN EDIT FILE: structs.h /* 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 5Right 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 4Now 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 (1 << 15) /* Not usable by warriors */ #define ITEM_NOSELL (1 << 16) /* Shopkeepers won't touch it */Right below it, put the following #define ITEM_ANTI_HUMAN (1 << 17) /* Not usable by Humans */ #define ITEM_ANTI_ELF (1 << 18) /* Not usable by Elves */ #define ITEM_ANTI_GNOME (1 << 19) /* Not usable by Gnomes */ #define ITEM_ANTI_FAIRY (1 << 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 *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 class; /* PC / NPC's class */Right below it, between byte class and the next variable, put the following byte race; /* PC / NPC's race */Lastly search for struct char_file_u { /* char_player_data */ char name[MAX_NAME_LENGTH+1]; char description[EXDSCR_LENGTH]; char title[MAX_TITLE_LENGTH+1]; byte sex; byte class;Right below it, between byte class and the next variable, put the following byte race;
CLOSE EDIT FILE: structs.h 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))CLOSE EDIT FILE: utils.h Now, lets go on to the .C files, the core of it all. OPEN EDIT FILE: class.c 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; }CLOSE EDIT FILE: class.c OPEN EDIT FILE: constants.c 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",
CLOSE EDIT FILE: constant.c 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);CLOSE EDIT FILE: db.c OPEN EDIT FILE: handler.c 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)) {CLOSE EDIT FILE: handler.c OPEN EDIT FILE: interpreter.c 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;CLOSE EDIT FILE: interpreter.c - Nick C.
|