/***************************************************************************** * DikuMUD (C) 1990, 1991 by: * * Sebastian Hammer, Michael Seifert, Hans Henrik Staefeldt, Tom Madsen, * * and Katja Nyboe. * *---------------------------------------------------------------------------* * MERC 2.1 (C) 1992, 1993 by: * * Michael Chastain, Michael Quan, and Mitchell Tse. * *---------------------------------------------------------------------------* * SMAUG 1.4 (C) 1994, 1995, 1996, 1998 by: Derek Snider. * * Team: Thoric, Altrag, Blodkai, Narn, Haus, Scryn, Rennard, Swordbearer, * * gorog, Grishnakh, Nivek, Tricops, and Fireblade. * *---------------------------------------------------------------------------* * SMAUG 1.7 FUSS by: Samson and others of the SMAUG community. * * Their contributions are greatly appreciated. * *---------------------------------------------------------------------------* * LoP (C) 2006, 2007, 2008 by: the LoP team. * *****************************************************************************/ #include <stdio.h> #include "h/mud.h" RACE_TYPE *race_table[MAX_RACE]; void write_race_list( void ) { FILE *fp; int i; if( !( fp = fopen( RACE_LIST, "w" ) ) ) { bug( "%s: Error opening %s.", __FUNCTION__, RACE_LIST ); perror( RACE_LIST ); return; } for( i = 0; i < MAX_PC_RACE && race_table[i]; i++ ) if( race_table[i] && race_table[i]->name ) fprintf( fp, "%s.race\n", race_table[i]->name ); fprintf( fp, "%s", "$\n" ); fclose( fp ); fp = NULL; } bool create_new_race( int rcindex, char *argument ) { int stat, i; if( !argument || argument[0] == '\0' ) return false; if( rcindex >= MAX_RACE || race_table[rcindex] ) return false; CREATE( race_table[rcindex], struct race_type, 1 ); if( !race_table[rcindex] ) return false; argument[0] = UPPER( argument[0] ); race_table[rcindex]->used = 0; race_table[rcindex]->rlistplace = rcindex; race_table[rcindex]->name = STRALLOC( argument ); race_table[rcindex]->hit = 100; race_table[rcindex]->mana = 100; race_table[rcindex]->move = 100; race_table[rcindex]->uses = 1; /* Set it to use Mana */ race_table[rcindex]->alignment = 0; race_table[rcindex]->minalign = -1000; race_table[rcindex]->maxalign = 1000; race_table[rcindex]->ac_plus = 0; race_table[rcindex]->minheight = number_range( 50, 75 ); race_table[rcindex]->maxheight = number_range( 75, 100 ); race_table[rcindex]->minweight = number_range( 150, 200 ); race_table[rcindex]->maxweight = number_range( 200, 250 ); race_table[rcindex]->hunger_mod = 0; race_table[rcindex]->thirst_mod = 0; race_table[rcindex]->race_recall = 0; for( stat = 0; stat < STAT_MAX; stat++ ) race_table[rcindex]->base_stats[stat] = 13; for( stat = 0; stat < STAT_MAX; stat++ ) race_table[rcindex]->max_stats[stat] = MAX_LEVEL; for( i = 0; i < MAX_WHERE_NAME; ++i ) { race_table[rcindex]->where_name[i] = NULL; race_table[rcindex]->lodge_name[i] = NULL; } xCLEAR_BITS( race_table[rcindex]->class_restriction ); xCLEAR_BITS( race_table[rcindex]->language ); xCLEAR_BITS( race_table[rcindex]->affected ); xCLEAR_BITS( race_table[rcindex]->resist ); xCLEAR_BITS( race_table[rcindex]->suscept ); return true; } void write_race_file( int ra ) { FILE *fp; char filename[MIL]; RACE_TYPE *race = race_table[ra]; int i, x, y, stat; if( !race ) { bug( "%s: NULL race in race_table[%d].", __FUNCTION__, ra ); return; } if( !race->name ) { bug( "%s: Race has NULL name in race_table[%d].", __FUNCTION__, ra ); return; } snprintf( filename, sizeof( filename ), "%s%s.race", RACE_DIR, race->name ); if( !( fp = fopen( filename, "w" ) ) ) { bug( "%s: Can't open: %s for writing", __FUNCTION__, filename ); perror( filename ); return; } fprintf( fp, "Name %s~\n", race->name ); fprintf( fp, "Used %d\n", race->used ); if( !xIS_EMPTY( race->class_restriction ) ) fprintf( fp, "Classes %s~\n", ext_class_string( &race->class_restriction ) ); fprintf( fp, "%s", "Stats " ); for( stat = 0; stat < STAT_MAX; stat++ ) fprintf( fp, " %d", race->base_stats[stat] ); fprintf( fp, "%s", "\n" ); fprintf( fp, "%s", "MaxStats " ); for( stat = 0; stat < STAT_MAX; stat++ ) fprintf( fp, " %d", race->max_stats[stat] ); fprintf( fp, "%s", "\n" ); fprintf( fp, "Hit %d\n", race->hit ); fprintf( fp, "Mana %d\n", race->mana ); fprintf( fp, "Move %d\n", race->move ); fprintf( fp, "Uses %d\n", race->uses ); if( !xIS_EMPTY( race->affected ) ) fprintf( fp, "Affected %s~\n", ext_flag_string( &race->affected, a_flags ) ); if( !xIS_EMPTY( race->resist ) ) fprintf( fp, "Resist %s~\n", ext_flag_string( &race->resist, ris_flags ) ); if( !xIS_EMPTY( race->suscept ) ) fprintf( fp, "Suscept %s~\n", ext_flag_string( &race->suscept, ris_flags ) ); if( !xIS_EMPTY( race->language ) ) fprintf( fp, "SLanguage %s~\n", ext_flag_string( &race->language, lang_names ) ); fprintf( fp, "Align %d\n", race->alignment ); fprintf( fp, "Min_Align %d\n", race->minalign ); fprintf( fp, "Max_Align %d\n", race->maxalign ); fprintf( fp, "AC_Plus %d\n", race->ac_plus ); fprintf( fp, "MinHeight %d\n", race->minheight ); fprintf( fp, "MaxHeight %d\n", race->maxheight ); fprintf( fp, "MinWeight %d\n", race->minweight ); fprintf( fp, "MaxWeight %d\n", race->maxweight ); fprintf( fp, "Hunger_Mod %d\n", race->hunger_mod ); fprintf( fp, "Thirst_mod %d\n", race->thirst_mod ); fprintf( fp, "Race_Recall %d\n", race->race_recall ); for( i = 0; i < MAX_WHERE_NAME; i++ ) { if( race->where_name[i] && str_cmp( race->where_name[i], where_name[i] ) ) fprintf( fp, "WhereName %d %s~\n", i, race->where_name[i] ); if( race->lodge_name[i] && str_cmp( race->lodge_name[i], lodge_name[i] ) ) fprintf( fp, "LodgeName %d %s~\n", i, race->lodge_name[i] ); } for( x = 0; x < top_sn; x++ ) { if( !skill_table[x] || !skill_table[x]->name ) continue; if( ( y = skill_table[x]->race_level[race->rlistplace] ) > 0 ) fprintf( fp, "Skill '%s' %d %d\n", skill_table[x]->name, y, skill_table[x]->race_adept[race->rlistplace] ); } fprintf( fp, "End\n" ); fclose( fp ); fp = NULL; } RACE_TYPE *valid_race( char *name ) { RACE_TYPE *race = NULL; int rcheck; if( name && name[0] != '\0' ) { if( is_number( name ) ) { if( ( rcheck = atoi( name ) ) >= 0 && rcheck < MAX_PC_RACE ) race = race_table[rcheck]; } else { for( rcheck = 0; rcheck < MAX_PC_RACE; rcheck++ ) { if( !race_table[rcheck] || !race_table[rcheck]->name ) continue; if( !str_cmp( race_table[rcheck]->name, name ) ) { race = race_table[rcheck]; break; } } } } return race; } void show_race( CHAR_DATA *ch, struct race_type *race ) { int i, ct, stat, ra, cnt; bool first = true; set_pager_color( AT_PLAIN, ch ); if( !race ) { send_to_char( "No such race.\r\n", ch ); return; } ra = race->rlistplace; ch_printf( ch, "&wRACE: &W%s\r\n", race->name ? race->name : "Not Set" ); ch_printf( ch, "&wUsed: &W%d\r\n", race->used ); ch_printf( ch, "&wDisallowed classes:\r\n&W%s\r\n", ext_class_string( &race->class_restriction ) ); ct = 0; send_to_char( "&wAllowed classes:&W\r\n", ch ); for( i = 0; i < MAX_PC_CLASS; i++ ) { if( !xIS_SET( race->class_restriction, i ) ) { ch_printf( ch, "%s ", class_table[i]->name ); if( ++ct == 6 ) { send_to_char( "\r\n", ch ); ct = 0; } } } if( ct != 0 ) send_to_char( "\r\n", ch ); ch_printf( ch, "&wUses: &W%s\r\n", race->uses == 0 ? "Nothing" : race->uses == 1 ? "Mana" : race->uses == 2 ? "Blood" : "Unknown" ); send_to_char( "&wBase Stats:\r\n", ch ); ct = 0; ch_printf( ch, "&c%14s: &w%4d &c%14s: &w%4d &c%14s: &w%4d\r\n&c%14s: &w%4d &c%14s: &w%4d ", "Hit Points", race->hit, race->uses == 2 ? "Blood" : "Mana", race->mana, "Move", race->move, "Alignment", race->alignment, "AC", race->ac_plus ); ct = 2; for( stat = 0; stat < STAT_MAX; stat++ ) { ch_printf( ch, "&c%14s: &w%4d", capitalize( stattypes[stat] ), race->base_stats[stat] ); if( ++ct == 3 ) { send_to_char( "\r\n", ch ); ct = 0; } else send_to_char( " ", ch ); } if( ct != 0 ) send_to_char( "\r\n", ch ); send_to_char( "&wMax Stats: (To set use 'max <stat>')\r\n", ch ); ct = 0; for( stat = 0; stat < STAT_MAX; stat++ ) { ch_printf( ch, "&c%14s: &w%4d", capitalize( stattypes[stat] ), race->max_stats[stat] ); if( ++ct == 3 ) { send_to_char( "\r\n", ch ); ct = 0; } else send_to_char( " ", ch ); } if( ct != 0 ) send_to_char( "\r\n", ch ); ch_printf( ch, "&wMin Align: &W%4d &wMax Align: &W%4d\r\n", race->minalign, race->maxalign ); ch_printf( ch, "&wHeight: &W%4d-%4d &win. Weight: &W%4d-%4d &wlbs. HungerMod: &W%4d &wThirstMod: &W%d\r\n", race->minheight, race->maxheight, race->minweight, race->maxweight, race->hunger_mod, race->thirst_mod ); ch_printf( ch, "&wSpoken Languages: &W%s\r\n", ext_flag_string( &race->language, lang_names ) ); ch_printf( ch, "&wAffected by: &W%s\r\n", ext_flag_string( &race->affected, a_flags ) ); ch_printf( ch, "&wResistant to: &W%s\r\n", ext_flag_string( &race->resist, ris_flags ) ); ch_printf( ch, "&wSusceptible to: &W%s\r\n", ext_flag_string( &race->suscept, ris_flags ) ); { int x, y, low, hi, last = -1; low = 0; hi = MAX_LEVEL; for( x = low; x <= hi; x++ ) { cnt = 0; set_pager_color( AT_BLUE, ch ); for( y = gsn_first_spell; y < gsn_top_sn; y++ ) { if( skill_table[y]->race_level[ra] == x ) { if( first ) send_to_pager( "Spells/Skills/Weapons\r\n", ch ); first = false; if( x != last ) { if( cnt != 0 ) { send_to_pager( "\r\n", ch ); cnt = 0; } pager_printf( ch, "[Level %d]\r\n", x ); } last = x; pager_printf( ch, " (%3d)%-22s", skill_table[y]->race_adept[ra], skill_table[y]->name ); if( ++cnt == 3 ) { send_to_pager( "\r\n", ch ); cnt = 0; } } } if( cnt != 0 ) send_to_pager( "\r\n", ch ); } } cnt = 0; first = true; for( i = 0; i < MAX_WHERE_NAME; ++i ) { if( !race->where_name[i] ) continue; if( first ) send_to_pager( "Where names:\r\n", ch ); first = false; pager_printf( ch, "%10s> %s\r\n", wear_locs[i], race->where_name[i] ); } first = true; for( i = 0; i < MAX_WHERE_NAME; ++i ) { if( !race->lodge_name[i] ) continue; if( first ) send_to_pager( "Lodge names:\r\n", ch ); first = false; pager_printf( ch, "%10s> %s\r\n", wear_locs[i], race->lodge_name[i] ); } } /* Modified by Samson to allow setting language by name - 8-6-98 */ CMDF( do_setrace ) { RACE_TYPE *race; char arg1[MIL], arg2[MIL], arg3[MIL]; int value, v2, i, stat, ra; set_char_color( AT_PLAIN, ch ); argument = one_argument( argument, arg1 ); argument = one_argument( argument, arg2 ); if( !arg1 || arg1[0] == '\0' ) { send_to_char( "Usage: setrace <race> [create/save]\r\n", ch ); send_to_char( "Usage: setrace <race> <field> <value>\r\n", ch ); send_to_char( "Usage: setrace <race> wherename <location> <string>\r\n", ch ); send_to_char( "Usage: setrace <race> skill <skill> <level> <adept>\r\n", ch ); send_to_char( "\r\nField being one of:\r\n", ch ); send_to_char( " ac uses wisdom maxalign maxweight\r\n", ch ); send_to_char( " hit hunger classes minalign minheight\r\n", ch ); send_to_char( " luck recall suscept strength minweight\r\n", ch ); send_to_char( " mana resist affected alignment constitution\r\n", ch ); send_to_char( " move thirst charisma dexterity intelligence\r\n", ch ); send_to_char( " name language maxheight\r\n", ch ); return; } race = valid_race( arg1 ); if( !str_cmp( arg2, "create" ) && race ) { send_to_char( "That race already exists!\r\n", ch ); return; } else if( !race && str_cmp( arg2, "create" ) ) { send_to_char( "No such race.\r\n", ch ); return; } if( !arg2 || arg2[0] == '\0' ) { show_race( ch, race ); return; } if( !str_cmp( arg2, "create" ) ) { char filename[1024]; if( MAX_PC_RACE >= MAX_RACE ) { send_to_char( "You need to up MAX_RACE in mud.h and make clean.\r\n", ch ); return; } snprintf( filename, sizeof( filename ), "%s.race", arg1 ); if( !can_use_path( ch, RACE_DIR, filename ) ) return; if( !create_new_race( MAX_PC_RACE, arg1 ) ) { send_to_char( "Couldn't create a new race.\r\n", ch ); return; } write_race_file( MAX_PC_RACE ); MAX_PC_RACE++; write_race_list( ); send_to_char( "Done.\r\n", ch ); return; } ra = race->rlistplace; if( !str_cmp( arg2, "save" ) ) { write_race_file( ra ); send_to_char( "Race saved.\r\n", ch ); return; } if( !argument || argument[0] == '\0' ) { send_to_char( "You must specify an argument.\r\n", ch ); return; } if( !str_cmp( arg2, "skill" ) ) { SKILLTYPE *skill; int sn; argument = one_argument( argument, arg2 ); if( ( sn = skill_lookup( arg2 ) ) >= 0 ) { if( !( skill = get_skilltype( sn ) ) ) { send_to_char( "Invalid skilltype???\r\n", ch ); return; } argument = one_argument( argument, arg2 ); skill->race_level[ra] = URANGE( -1, atoi( arg2 ), MAX_LEVEL ); argument = one_argument( argument, arg2 ); skill->race_adept[ra] = URANGE( 0, atoi( arg2 ), 100 ); write_race_file( ra ); ch_printf( ch, "Skill \"%s\" added at level %d and %d%%.\r\n", skill->name, skill->race_level[ra], skill->race_adept[ra] ); } else ch_printf( ch, "No skill: %s.\r\n", arg2 ); return; } if( !str_cmp( arg2, "name" ) ) { char buf[256]; RACE_TYPE *rcheck; one_argument( argument, arg1 ); if( !arg1 || arg1[0] == '\0' ) { send_to_char( "You can't set a race name to nothing.\r\n", ch ); return; } snprintf( buf, sizeof( buf ), "%s.race", arg1 ); if( !can_use_path( ch, RACE_DIR, buf ) ) return; if( ( rcheck = valid_race( arg1 ) ) ) { ch_printf( ch, "Already a race called %s.\r\n", arg1 ); return; } snprintf( buf, sizeof( buf ), "%s%s.race", RACE_DIR, race->name ); remove_file( buf ); STRSET( race->name, capitalize( arg1 ) ); write_race_file( ra ); write_race_list( ); send_to_char( "Race name set.\r\n", ch ); return; } if( !str_cmp( arg2, "wherename" ) ) { if( !argument || argument[0] == '\0' ) { send_to_char( "Usage: setrace <race> wherename <location> <string>\r\n", ch ); return; } argument = one_argument( argument, arg3 ); value = get_flag( arg3, wear_locs, WEAR_MAX ); if( value < 0 || value >= WEAR_MAX ) { ch_printf( ch, "Unknown wearlocation (%s), see Help WEARLOCS.\r\n", arg3 ); return; } STRSET( race->where_name[value], argument ); write_race_file( ra ); send_to_char( "Race wherename set.\r\n", ch ); return; } if( !str_cmp( arg2, "lodgename" ) ) { if( !argument || argument[0] == '\0' ) { send_to_char( "Usage: setrace <race> lodgename <location> <string>\r\n", ch ); return; } argument = one_argument( argument, arg3 ); value = get_flag( arg3, wear_locs, WEAR_MAX ); if( value < 0 || value >= WEAR_MAX ) { ch_printf( ch, "Unknown wearlocation (%s), see Help WEARLOCS.\r\n", arg3 ); return; } STRSET( race->lodge_name[value], argument ); write_race_file( ra ); send_to_char( "Race lodgename set.\r\n", ch ); return; } if( !str_cmp( arg2, "affected" ) ) { bool fset = false; if( !argument || argument[0] == '\0' ) { send_to_char( "Usage: setrace <race> affected <flag> [flag]...\r\n", ch ); return; } while( argument && argument[0] != '\0' ) { argument = one_argument( argument, arg3 ); value = get_flag( arg3, a_flags, AFF_MAX ); if( value < 0 || value >= AFF_MAX ) ch_printf( ch, "Unknown flag: %s\r\n", arg3 ); else { xTOGGLE_BIT( race->affected, value ); fset = true; } } if( !fset ) return; write_race_file( ra ); send_to_char( "Racial affects set.\r\n", ch ); return; } if( !str_cmp( arg2, "resist" ) || !str_cmp( arg2, "suscept" ) ) { bool resist = false, fset = false; if( !argument || argument[0] == '\0' ) { ch_printf( ch, "Usage: setrace <race> %s <flag> [flag]...\r\n", arg2 ); return; } while( argument[0] != '\0' ) { argument = one_argument( argument, arg3 ); value = get_flag( arg3, ris_flags, RIS_MAX ); if( value < 0 || value >= RIS_MAX ) ch_printf( ch, "Unknown %s: %s\r\n", arg2, arg3 ); else { fset = true; if( !str_cmp( arg2, "resist" ) ) { xTOGGLE_BIT( race->resist, value ); resist = true; } else xTOGGLE_BIT( race->suscept, value ); } } if( !fset ) return; write_race_file( ra ); ch_printf( ch, "Racial %s set.\r\n", resist ? "resistances" : "susceptabilities" ); return; } if( !str_cmp( arg2, "language" ) ) { while( argument && argument[0] != '\0' ) { argument = one_argument( argument, arg3 ); value = get_langflag( arg3 ); v2 = get_langnum( arg3 ); if( v2 == -1 || value == LANG_UNKNOWN ) { ch_printf( ch, "Unknown language: %s\r\n", arg3 ); continue; } else { if( !( value &= VALID_LANGS ) ) { ch_printf( ch, "Player races may not speak %s.\r\n", arg3 ); continue; } } xTOGGLE_BIT( race->language, v2 ); } write_race_file( ra ); send_to_char( "Racial language set.\r\n", ch ); return; } if( !str_cmp( arg2, "classes" ) ) { bool modified = false; while( argument && argument[0] != '\0' ) { argument = one_argument( argument, arg1 ); for( i = 0; i < MAX_PC_CLASS && class_table[i]; i++ ) { if( !class_table[i] || !class_table[i]->name ) continue; if( !str_cmp( arg1, class_table[i]->name ) ) { xTOGGLE_BIT( race->class_restriction, i ); modified = true; } } } if( !modified ) send_to_char( "No such class.\r\n", ch ); else { write_race_file( ra ); send_to_char( "Race Classes set.\r\n", ch ); } return; } /* Check stats for matches */ for( stat = 0; stat < STAT_MAX; stat++ ) { if( !str_cmp( arg2, stattypes[stat] ) ) { race->base_stats[stat] = ( short )atoi( argument ); write_race_file( ra ); ch_printf( ch, "%s set to %d\r\n", capitalize( stattypes[stat] ), atoi( argument ) ); return; } } if( !str_cmp( arg2, "max" ) ) { argument = one_argument( argument, arg2 ); /* Check stats for matches */ for( stat = 0; stat < STAT_MAX; stat++ ) { if( !str_cmp( arg2, stattypes[stat] ) ) { race->max_stats[stat] = ( short )URANGE( 0, atoi( argument ), MAX_LEVEL ); write_race_file( ra ); ch_printf( ch, "Max %s set to %d\r\n", capitalize( stattypes[stat] ), atoi( argument ) ); return; } } send_to_char( "No such max stat to set.\r\n", ch ); return; } if( !str_cmp( arg2, "hit" ) ) race->hit = ( short )atoi( argument ); else if( !str_cmp( arg2, "mana" ) ) race->mana = ( short )atoi( argument ); else if( !str_cmp( arg2, "move" ) ) race->move = ( short )atoi( argument ); else if( !str_cmp( arg2, "uses" ) ) { if( is_number( argument ) ) { int tval = atoi( argument ); if( tval < 0 || tval > 2 ) { send_to_char( "You may only set uses to 0 = Nothing, 1 = Mana, 2 = Blood.\r\n", ch ); return; } race->uses = tval; } else if( !str_cmp( argument, "Mana" ) ) race->uses = 1; else if( !str_cmp( argument, "Blood" ) ) race->uses = 2; else if( !str_cmp( argument, "Nothing" ) ) race->uses = 0; else { send_to_char( "You may only set uses to 0 = Nothing, 1 = Mana, 2 = Blood.\r\n", ch ); return; } write_race_file( ra ); ch_printf( ch, "Uses set to %s\r\n", race->uses == 0 ? "Nothing" : race->uses == 1 ? "Mana" : race->uses == 2 ? "Blood" : "Unknown" ); return; } else if( !str_cmp( arg2, "ac" ) ) race->ac_plus = atoi( argument ); else if( !str_cmp( arg2, "alignment" ) ) race->alignment = atoi( argument ); else if( !str_cmp( arg2, "minalign" ) ) race->minalign = atoi( argument ); else if( !str_cmp( arg2, "maxalign" ) ) race->maxalign = atoi( argument ); else if( !str_cmp( arg2, "minheight" ) ) race->minheight = atoi( argument ); else if( !str_cmp( arg2, "maxheight" ) ) race->maxheight = atoi( argument ); else if( !str_cmp( arg2, "minweight" ) ) race->minweight = atoi( argument ); else if( !str_cmp( arg2, "maxweight" ) ) race->maxweight = atoi( argument ); else if( !str_cmp( arg2, "thirst" ) ) race->thirst_mod = atoi( argument ); else if( !str_cmp( arg2, "hunger" ) ) race->hunger_mod = atoi( argument ); else if( !str_cmp( arg2, "recall" ) ) race->race_recall = atoi( argument ); else { do_setrace( ch, (char *)"" ); return; } write_race_file( ra ); ch_printf( ch, "%s changed and race saved.\r\n", arg2 ); } CMDF( do_races ) { RACE_TYPE *Race; int ra, count; for( ra = 0, count = ( is_immortal( ch ) ? 0 : 1 ); ra < MAX_PC_RACE; ra++, count++ ) { if( !( Race = race_table[ra] ) || !Race->name ) continue; pager_printf( ch, "&w[&W%2d&w] &w(&W%3d&w) &W%s&D\r\n", count, Race->used, Race->name ); } } bool load_race_file( const char *fname ) { char buf[MSL], *infoflags, flag[MIL]; const char *word; bool fMatch; struct race_type *race; FILE *fp; int i, wear = 0, value, ra = -1, stat; snprintf( buf, sizeof( buf ), "%s%s", RACE_DIR, fname ); if( !( fp = fopen( buf, "r" ) ) ) { perror( buf ); return false; } CREATE( race, struct race_type, 1 ); race->uses = 0; for( i = 0; i < MAX_WHERE_NAME; ++i ) { race->where_name[i] = NULL; race->lodge_name[i] = NULL; } for( stat = 0; stat < STAT_MAX; stat++ ) { race->base_stats[stat] = 13; race->max_stats[stat] = MAX_LEVEL; } for( ra = 0; ra < MAX_RACE; ra++ ) { if( !race_table[ra] ) break; } for( ;; ) { word = feof( fp ) ? "End" : fread_word( fp ); fMatch = false; switch( UPPER( word[0] ) ) { case '*': fMatch = true; fread_to_eol( fp ); break; case 'A': KEY( "Align", race->alignment, fread_number( fp ) ); KEY( "AC_Plus", race->ac_plus, fread_number( fp ) ); WEXTKEY( "Affected", race->affected, fp, a_flags, AFF_MAX ); break; case 'C': if( !str_cmp( word, "Classes" ) ) { int iclass; infoflags = fread_flagstring( fp ); while( infoflags && infoflags[0] != '\0' ) { infoflags = one_argument( infoflags, flag ); for( iclass = 0; iclass < MAX_PC_CLASS; iclass++ ) { if( !class_table[iclass] || !class_table[iclass]->name ) continue; if( !str_cmp( class_table[iclass]->name, flag ) ) { xSET_BIT( race->class_restriction, iclass ); break; } } } fMatch = true; break; } break; case 'E': if( !str_cmp( word, "End" ) ) { fclose( fp ); fp = NULL; if( ra < 0 || ra >= MAX_RACE ) { bug( "%s: Race (%s) bad/not found (%d)", __FUNCTION__, race->name ? race->name : "name not found", ra ); STRFREE( race->name ); for( i = 0; i < MAX_WHERE_NAME; ++i ) { STRFREE( race->where_name[i] ); STRFREE( race->lodge_name[i] ); } DISPOSE( race ); return false; } race_table[ra] = race; race_table[ra]->rlistplace = ra; return true; } break; case 'F': if( !str_cmp( word, "FMana" ) ) { int iclass = fread_number( fp ); if( iclass ) race->uses = 1; fMatch = true; break; } if( !str_cmp( word, "FBlood" ) ) { int iclass = fread_number( fp ); if( iclass ) race->uses = 2; fMatch = true; break; } break; case 'H': KEY( "Hit", race->hit, fread_number( fp ) ); KEY( "Hunger_Mod", race->hunger_mod, fread_number( fp ) ); break; case 'L': if( !str_cmp( word, "Language" ) ) /* Handle loading old ones and just don't bother with them */ { fread_number( fp ); fMatch = true; break; } if( !str_cmp( word, "LodgeName" ) ) { wear = fread_number( fp ); if( wear < MAX_WHERE_NAME ) { STRFREE( race->lodge_name[wear] ); race->lodge_name[wear] = fread_string( fp ); } else { bug( "%s: Too many lodge_names", __FUNCTION__ ); fread_flagstring( fp ); } fMatch = true; break; } break; case 'M': KEY( "MinHeight", race->minheight, fread_number( fp ) ); KEY( "MaxHeight", race->maxheight, fread_number( fp ) ); KEY( "MinWeight", race->minweight, fread_number( fp ) ); KEY( "MaxWeight", race->maxweight, fread_number( fp ) ); KEY( "Mana", race->mana, fread_number( fp ) ); KEY( "Move", race->move, fread_number( fp ) ); KEY( "Min_Align", race->minalign, fread_number( fp ) ); KEY( "Max_Align", race->maxalign, fread_number( fp ) ); if( !str_cmp( word, "MaxStats" ) ) { for( stat = 0; stat < STAT_MAX; stat++ ) race->max_stats[stat] = fread_number( fp ); fMatch = true; break; } break; case 'N': KEY( "Name", race->name, fread_string( fp ) ); break; case 'R': KEY( "Race_Recall", race->race_recall, fread_number( fp ) ); WEXTKEY( "Resist", race->resist, fp, ris_flags, RIS_MAX ); break; case 'S': WEXTKEY( "SLanguage", race->language, fp, lang_names, LANG_UNKNOWN ); if( !str_cmp( word, "Stats" ) ) { for( stat = 0; stat < STAT_MAX; stat++ ) race->base_stats[stat] = fread_number( fp ); fMatch = true; break; } WEXTKEY( "Suscept", race->suscept, fp, ris_flags, RIS_MAX ); if( !str_cmp( word, "Skill" ) ) { int sn, lev, adp; word = fread_word( fp ); lev = fread_number( fp ); adp = fread_number( fp ); sn = skill_lookup( word ); if( !is_valid_sn( sn ) ) bug( "%s: Skill %s unknown", __FUNCTION__, word ); else { if( ra >= 0 && ra < MAX_RACE ) { skill_table[sn]->race_level[ra] = lev; skill_table[sn]->race_adept[ra] = adp; } } fMatch = true; break; } break; case 'T': KEY( "Thirst_Mod", race->thirst_mod, fread_number( fp ) ); break; case 'U': KEY( "Uses", race->uses, fread_number( fp ) ); KEY( "Used", race->used, fread_number( fp ) ); break; case 'W': if( !str_cmp( word, "WhereName" ) ) { wear = fread_number( fp ); if( wear < MAX_WHERE_NAME ) { STRFREE( race->where_name[wear] ); race->where_name[wear] = fread_string( fp ); } else { bug( "%s: Too many where_names", __FUNCTION__ ); fread_flagstring( fp ); } fMatch = true; break; } break; } if( !fMatch ) { bug( "%s: no match: %s", __FUNCTION__, word ); fread_to_eol( fp ); } } } /* Load in all the race files. */ void load_races( void ) { FILE *fp; const char *filename; int i; MAX_PC_RACE = 0; for( i = 0; i < MAX_RACE; i++ ) race_table[i] = NULL; if( !( fp = fopen( RACE_LIST, "r" ) ) ) { perror( RACE_LIST ); exit( 1 ); } for( ;; ) { filename = feof( fp ) ? "$" : fread_word( fp ); if( filename[0] == '$' || filename[0] == EOF ) break; if( !load_race_file( filename ) ) bug( "%s: Can't load race file: %s", __FUNCTION__, filename ); else MAX_PC_RACE++; } fclose( fp ); fp = NULL; } void save_races( void ) { int x; for( x = 0; x < MAX_PC_RACE; x++ ) if( race_table[x] && race_table[x]->name ) write_race_file( x ); }