/*************************************************************************** * Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer, * * Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe. * * * * Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael * * Chastain, Michael Quan, and Mitchell Tse. * * * * In order to use any part of this Merc Diku Mud, you must comply with * * both the original Diku license in 'license.doc' as well the Merc * * license in 'license.txt'. In particular, you may not remove either of * * these copyright notices. * * * * Much time and thought has gone into this software and you are * * benefitting. We hope that you share your changes too. What goes * * around, comes around. * ***************************************************************************/ #include <sys/types.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <ctype.h> #include <time.h> #include "merc.h" extern int _filbuf args( (FILE *) ); /* * Max sizes. * Increase if you add more areas. * Use 'memory' command in game to see current sizes. * Note: MAX_HASH takes no permanent memory. */ #define MAX_AREA 45 #define MAX_EXIT 6000 #define MAX_HELP 160 #define MAX_MOBILE 760 #define MAX_OBJECT 900 #define MAX_RESET 4200 #define MAX_ROOM 2600 #define MAX_SHOP 36 #define MAX_SHARE 840000 #define MAX_HASH 12000 /* * Reset commands: * '*': comment * 'M': read a mobile * 'O': read an object * 'P': put object in object * 'G': give object to mobile * 'E': equip object to mobile * 'D': set state of door * 'R': randomize room exits * 'S': stop (end of list) */ struct reset_com { char command; sh_int arg1; sh_int arg2; sh_int arg3; }; /* * Area definition. */ struct area_data { char * name; sh_int age; sh_int cmd; }; /* * String hashing stuff. * Saves memory by re-using duplicate strings. */ typedef struct hash_data HASH_DATA; struct hash_data { HASH_DATA * next; char * string; }; #define MAX_KEY_HASH 512 /* * Globals. */ struct exit_type exit_index [MAX_EXIT]; struct help_index_type help_index [MAX_HELP]; struct mob_index_type mob_index [MAX_MOBILE]; struct obj_index_type obj_index [MAX_OBJECT]; struct room_index_type room_index [MAX_ROOM]; struct shop_type shop_index [MAX_SHOP]; CHAR_DATA * char_free; NOTE_DATA * note_free; OBJ_DATA * obj_free; char bug_buf [2*MAX_INPUT_LENGTH]; CHAR_DATA * char_list; long current_time; char log_buf [2*MAX_INPUT_LENGTH]; KILL_DATA kill_table [MAX_LEVEL]; NOTE_DATA * note_list; OBJ_DATA * object_list; TIME_INFO_DATA time_info; WEATHER_DATA weather_info; sh_int gsn_backstab; sh_int gsn_dodge; sh_int gsn_hide; sh_int gsn_peek; sh_int gsn_pick_lock; sh_int gsn_sneak; sh_int gsn_steal; sh_int gsn_disarm; sh_int gsn_enhanced_damage; sh_int gsn_kick; sh_int gsn_parry; sh_int gsn_rescue; sh_int gsn_second_attack; sh_int gsn_third_attack; sh_int gsn_blindness; sh_int gsn_charm_person; sh_int gsn_curse; sh_int gsn_invis; sh_int gsn_mass_invis; sh_int gsn_poison; sh_int gsn_sleep; /* * Locals. */ struct reset_com reset_table [MAX_RESET]; struct area_data area_table [MAX_AREA]; char share_space [MAX_SHARE]; char * top_share; HASH_DATA ** hash_table; HASH_DATA * hash_block; int top_hash; int top_area; int top_exit; int top_help; int top_mob; int top_obj; int top_reset; int top_room; int top_shop; int nAlloc; int sAlloc; /* * Semi-locals */ bool abort_bad_vnum; FILE * fpArea; char strArea[MAX_INPUT_LENGTH]; /* * Local booting procedures. */ void * alloc_share args( ( int size ) ); void load_area args( ( FILE *fp ) ); void load_helps args( ( FILE *fp ) ); void load_mobiles args( ( FILE *fp ) ); void load_objects args( ( FILE *fp ) ); void load_resets args( ( FILE *fp ) ); void load_rooms args( ( FILE *fp ) ); void load_shops args( ( FILE *fp ) ); void load_specials args( ( FILE *fp ) ); void load_notes args( ( void ) ); char fread_letter args( ( FILE *fp ) ); int fread_number args( ( FILE *fp ) ); char * fread_string args( ( FILE *fp ) ); void fread_to_eol args( ( FILE *fp ) ); char * fread_word args( ( FILE *fp ) ); void fix_exits args( ( void ) ); void reset_area args( ( int area, bool fplayer ) ); /* * Big mama top level function. */ #define SECS_PER_MUD_HOUR (PULSE_TICK/PULSE_PER_SECOND) #define SECS_PER_MUD_DAY (24*SECS_PER_MUD_HOUR) #define SECS_PER_MUD_MONTH (35*SECS_PER_MUD_DAY) #define SECS_PER_MUD_YEAR (17*SECS_PER_MUD_MONTH) void boot_db( void ) { /* * Init some data space stuff. */ { int iHash; abort_bad_vnum = TRUE; top_exit = 1; share_space[0] = '\0'; top_share = &share_space[1]; hash_table = alloc_mem( MAX_KEY_HASH * sizeof(*hash_table) ); hash_block = alloc_mem( MAX_HASH * sizeof(*hash_block) ); for ( iHash = 0; iHash < MAX_KEY_HASH; iHash++ ) hash_table[iHash] = NULL; } /* * Set time and weather. */ { long secs; secs = current_time - 650336715; time_info.year = secs / SECS_PER_MUD_YEAR; secs -= time_info.year * SECS_PER_MUD_YEAR; time_info.month = secs / SECS_PER_MUD_MONTH; secs -= time_info.month * SECS_PER_MUD_MONTH; time_info.day = secs / SECS_PER_MUD_DAY; secs -= time_info.day * SECS_PER_MUD_DAY; time_info.hours = secs / SECS_PER_MUD_HOUR; if ( time_info.hours < 5 ) weather_info.sunlight = SUN_DARK; else if ( time_info.hours < 6 ) weather_info.sunlight = SUN_RISE; else if ( time_info.hours < 19 ) weather_info.sunlight = SUN_LIGHT; else if ( time_info.hours < 20 ) weather_info.sunlight = SUN_SET; else weather_info.sunlight = SUN_DARK; weather_info.change = 0; weather_info.mmhg = 960; if ( time_info.month >= 7 && time_info.month <=12 ) weather_info.mmhg += number_range( 1, 50 ); else weather_info.mmhg += number_range( 1, 80 ); if ( weather_info.mmhg <= 980 ) weather_info.sky = SKY_LIGHTNING; else if ( weather_info.mmhg <= 1000 ) weather_info.sky = SKY_RAINING; else if ( weather_info.mmhg <= 1020 ) weather_info.sky = SKY_CLOUDY; else weather_info.sky = SKY_CLOUDLESS; } /* * Assign gsn's for skills which have them. */ { int sn; for ( sn = 0; sn < MAX_SKILL; sn++ ) { if ( skill_table[sn].pgsn != NULL ) *skill_table[sn].pgsn = sn; } } /* * Read in all the area files. */ { FILE *fpList; if ( ( fpList = fopen( AREA_LIST, "r" ) ) == NULL ) { perror( AREA_LIST ); exit( 1 ); } for ( ; ; ) { strcpy( strArea, fread_word( fpList ) ); if ( strArea[0] == '$' ) break; if ( strArea[0] == '-' ) { fpArea = stdin; } else { if ( ( fpArea = fopen( strArea, "r" ) ) == NULL ) { perror( strArea ); exit( 1 ); } } for ( ; ; ) { char *word; if ( fread_letter( fpArea ) != '#' ) { bug( "Boot_db: # not found.", 0 ); exit( 1 ); } word = fread_word( fpArea ); if ( word[0] == '$' ) break; else if ( !str_cmp( word, "AREA" ) ) load_area (fpArea); else if ( !str_cmp( word, "HELPS" ) ) load_helps (fpArea); else if ( !str_cmp( word, "MOBILES" ) ) load_mobiles (fpArea); else if ( !str_cmp( word, "OBJECTS" ) ) load_objects (fpArea); else if ( !str_cmp( word, "RESETS" ) ) load_resets (fpArea); else if ( !str_cmp( word, "ROOMS" ) ) load_rooms (fpArea); else if ( !str_cmp( word, "SHOPS" ) ) load_shops (fpArea); else if ( !str_cmp( word, "SPECIALS" ) ) load_specials(fpArea); else { bug( "Boot_db: bad section name.", 0 ); exit( 1 ); } } if ( fpArea != stdin ) fclose( fpArea ); fpArea = NULL; } fclose( fpList ); } /* * Fix up exits. * Reset all areas once. * Load up the notes file. * Declare db booting over. */ { fix_exits( ); area_update( ); load_notes( ); free_mem( hash_table ); free_mem( hash_block ); abort_bad_vnum = FALSE; } return; } /* * Snarf an 'area' header line. */ void load_area( FILE *fp ) { int area; if ( ( area = top_area ) >= MAX_AREA ) { bug( "Load_area: MAX_AREA: more than %d areas.", MAX_AREA ); exit( 1 ); } area_table[area].name = fread_string( fp ); area_table[area].age = 15; area_table[area].cmd = top_reset; top_area = area + 1; return; } /* * Snarf a help section. */ void load_helps( FILE *fp ) { int iHelp; for ( iHelp = top_help; iHelp < MAX_HELP; iHelp++ ) { help_index[iHelp].level = fread_number( fp ); help_index[iHelp].keyword = fread_string( fp ); if ( help_index[iHelp].keyword[0] == '$' ) { top_help = iHelp; return; } help_index[iHelp].text = fread_string( fp ); /* * Leading period (to allow initial space). */ if ( help_index[iHelp].text[0] == '.' ) help_index[iHelp].text++; } bug( "Load_helps: MAX_HELP: more than %d helps.", MAX_HELP ); exit( 1 ); return; } /* * Snarf a mob section. */ void load_mobiles( FILE *fp ) { int iMob; for ( iMob = top_mob; iMob < MAX_MOBILE; iMob++ ) { char letter; letter = fread_letter( fp ); if ( letter != '#' ) { bug( "Load_mobiles: # not found after vnum %d.", iMob == 0 ? 0 : mob_index[iMob-1].vnum ); exit( 1 ); } mob_index[iMob].vnum = fread_number( fp ); if ( mob_index[iMob].vnum == 0 ) { top_mob = iMob; return; } if ( iMob > 0 && mob_index[iMob].vnum <= mob_index[iMob-1].vnum ) { bug( "Load_mobiles: vnum %d not in increasing order.", mob_index[iMob].vnum ); exit( 1 ); } mob_index[iMob].player_name = fread_string( fp ); mob_index[iMob].short_descr = fread_string( fp ); mob_index[iMob].long_descr = fread_string( fp ); mob_index[iMob].description = fread_string( fp ); mob_index[iMob].long_descr[0] = UPPER(mob_index[iMob].long_descr[0]); mob_index[iMob].description[0] = UPPER(mob_index[iMob].description[0]); mob_index[iMob].act = fread_number( fp ) | ACT_IS_NPC; mob_index[iMob].affected_by = fread_number( fp ); mob_index[iMob].shop = SHOP_NONE; mob_index[iMob].alignment = fread_number( fp ); letter = fread_letter( fp ); mob_index[iMob].level = number_fuzzy( fread_number( fp ) ); /* * The unused stuff is for imps who want to use the old-style * stats-in-files method. */ mob_index[iMob].hitroll = fread_number( fp ); /* Unused */ mob_index[iMob].ac = fread_number( fp ); /* Unused */ mob_index[iMob].hitnodice = fread_number( fp ); /* Unused */ /* 'd' */ fread_letter( fp ); /* Unused */ mob_index[iMob].hitsizedice = fread_number( fp ); /* Unused */ /* '+' */ fread_letter( fp ); /* Unused */ mob_index[iMob].hitplus = fread_number( fp ); /* Unused */ mob_index[iMob].damnodice = fread_number( fp ); /* Unused */ /* 'd' */ fread_letter( fp ); /* Unused */ mob_index[iMob].damsizedice = fread_number( fp ); /* Unused */ /* '+' */ fread_letter( fp ); /* Unused */ mob_index[iMob].damplus = fread_number( fp ); /* Unused */ mob_index[iMob].gold = fread_number( fp ); /* Unused */ /* xp, can't be used! */ fread_number( fp ); /* Unused */ /* position */ fread_number( fp ); /* Unused */ /* start pos */ fread_number( fp ); /* Unused */ /* * Back to meaningful values. */ mob_index[iMob].sex = fread_number( fp ); if ( letter != 'S' ) { bug( "Load_mobiles: vnum %d non-S.", mob_index[iMob].vnum ); exit( 1 ); } kill_table[MAX(0, MIN(MAX_LEVEL-1, mob_index[iMob].level))].number++; } bug( "Load_mobiles: MAX_MOBILE: more than %d mobs.", MAX_MOBILE ); exit( 1 ); return; } /* * Snarf an obj section. */ void load_objects( FILE *fp ) { int iObj; for ( iObj = top_obj; iObj < MAX_OBJECT; iObj++ ) { char letter; letter = fread_letter( fp ); if ( letter != '#' ) { bug( "Load_objects: # not found after vnum %d.", iObj == 0 ? 0 : obj_index[iObj-1].vnum ); exit( 1 ); } obj_index[iObj].vnum = fread_number( fp ); if ( obj_index[iObj].vnum == 0 ) { top_obj = iObj; return; } if ( iObj > 0 && obj_index[iObj].vnum <= obj_index[iObj-1].vnum ) { bug( "Load_objects: vnum %d not in increasing order.", obj_index[iObj].vnum ); exit( 1 ); } obj_index[iObj].name = fread_string( fp ); obj_index[iObj].short_descr = fread_string( fp ); obj_index[iObj].description = fread_string( fp ); /* Action description */ fread_string( fp ); obj_index[iObj].short_descr[0] = LOWER(obj_index[iObj].short_descr[0]); obj_index[iObj].description[0] = UPPER(obj_index[iObj].description[0]); obj_index[iObj].item_type = fread_number( fp ); obj_index[iObj].extra_flags = fread_number( fp ); obj_index[iObj].wear_flags = fread_number( fp ); obj_index[iObj].value[0] = fread_number( fp ); obj_index[iObj].value[1] = fread_number( fp ); obj_index[iObj].value[2] = fread_number( fp ); obj_index[iObj].value[3] = fread_number( fp ); obj_index[iObj].weight = fread_number( fp ); obj_index[iObj].cost = fread_number( fp ); /* Unused */ /* Cost per day */ fread_number( fp ); if ( obj_index[iObj].item_type == ITEM_POTION ) SET_BIT(obj_index[iObj].extra_flags, ITEM_NODROP); for ( ; ; ) { char letter; letter = fread_letter( fp ); if ( letter == 'A' ) { AFFECT_DATA *paf; paf = alloc_share( sizeof(*paf) ); paf->type = -1; paf->duration = -1; paf->location = fread_number( fp ); paf->modifier = fread_number( fp ); paf->bitvector = 0; paf->next = obj_index[iObj].affected; obj_index[iObj].affected = paf; } else if ( letter == 'E' ) { EXTRA_DESCR_DATA *ed; ed = alloc_share( sizeof(*ed) ); ed->keyword = fread_string( fp ); ed->description = fread_string( fp ); ed->next = obj_index[iObj].ex_description; obj_index[iObj].ex_description = ed; } else { ungetc( letter, fp ); break; } } /* * Translate spell "slot numbers" to internal "skill numbers." */ switch ( obj_index[iObj].item_type ) { case ITEM_PILL: case ITEM_POTION: case ITEM_SCROLL: obj_index[iObj].value[1] = slot_lookup( obj_index[iObj].value[1] ); obj_index[iObj].value[2] = slot_lookup( obj_index[iObj].value[2] ); obj_index[iObj].value[3] = slot_lookup( obj_index[iObj].value[3] ); break; case ITEM_STAFF: case ITEM_WAND: obj_index[iObj].value[3] = slot_lookup( obj_index[iObj].value[3] ); break; } } bug( "Load_objects: MAX_OBJECT: more than %d objects.", MAX_OBJECT ); exit( 1 ); return; } /* * Snarf a reset section. */ void load_resets( FILE *fp ) { int iReset; int iexit; for ( iReset = top_reset; iReset < MAX_RESET; iReset++ ) { switch ( reset_table[iReset].command = fread_letter( fp ) ) { default: bug( "Load_resets: bad command '%c'.", reset_table[iReset].command ); exit( 1 ); break; case 'S': fread_to_eol( fp ); top_reset = iReset + 1; return; case '*': break; case 'M': /* if_flag */ fread_number( fp ) ; reset_table[iReset].arg1 = real_mobile ( fread_number( fp ) ) ; reset_table[iReset].arg2 = fread_number( fp ) ; reset_table[iReset].arg3 = real_room ( fread_number( fp ) ) ; break; case 'O': /* if_flag */ fread_number( fp ) ; reset_table[iReset].arg1 = real_object ( fread_number( fp ) ) ; reset_table[iReset].arg2 = fread_number( fp ) ; reset_table[iReset].arg3 = real_room ( fread_number( fp ) ) ; break; case 'P': /* if_flag */ fread_number( fp ) ; reset_table[iReset].arg1 = real_object ( fread_number( fp ) ) ; reset_table[iReset].arg2 = fread_number( fp ) ; reset_table[iReset].arg3 = real_object ( fread_number( fp ) ) ; break; case 'G': /* if_flag */ fread_number( fp ) ; reset_table[iReset].arg1 = real_object ( fread_number( fp ) ) ; reset_table[iReset].arg2 = fread_number( fp ) ; reset_table[iReset].arg3 = 0; break; case 'E': /* if_flag */ fread_number( fp ) ; reset_table[iReset].arg1 = real_object ( fread_number( fp ) ) ; reset_table[iReset].arg2 = fread_number( fp ) ; reset_table[iReset].arg3 = fread_number( fp ) ; break; case 'D': /* if_flag */ fread_number( fp ) ; reset_table[iReset].arg1 = real_room ( fread_number( fp ) ) ; reset_table[iReset].arg2 = fread_number( fp ) ; reset_table[iReset].arg3 = fread_number( fp ) ; if ( reset_table[iReset].arg2 < 0 || reset_table[iReset].arg2 > 5 || ( iexit = room_index[reset_table[iReset].arg1]. exit[reset_table[iReset].arg2] ) == 0 || !IS_SET( exit_index[iexit].exit_info, EX_ISDOOR ) ) { bug( "Load_resets: 'D': exit %d not door.", reset_table[iReset].arg2 ); exit( 1 ); } if ( reset_table[iReset].arg3 < 0 || reset_table[iReset].arg3 > 2 ) { bug( "Load_resets: 'D': bad 'locks': %d.", reset_table[iReset].arg3 ); exit( 1 ); } break; case 'R': /* if_flag */ fread_number( fp ) ; reset_table[iReset].arg1 = real_room ( fread_number( fp ) ) ; reset_table[iReset].arg2 = fread_number( fp ) ; if ( reset_table[iReset].arg2 < 0 || reset_table[iReset].arg2 > 6 ) { bug( "Load_resets: 'R': bad exit %d.", reset_table[iReset].arg2 ); exit( 1 ); } break; } fread_to_eol( fp ); } bug( "Load_resets: MAX_RESET: more than %d resets.", MAX_RESET ); exit( 1 ); return; } /* * Snarf a room section. */ void load_rooms( FILE *fp ) { int iRoom; for ( iRoom = top_room; iRoom < MAX_ROOM; iRoom++ ) { char letter; letter = fread_letter( fp ); if ( letter != '#' ) { bug( "Load_rooms: error at vnum %d.", iRoom == 0 ? 0 : room_index[iRoom-1].vnum ); exit( 1 ); } room_index[iRoom].area = top_area - 1; room_index[iRoom].vnum = fread_number( fp ); if ( room_index[iRoom].vnum == 0 ) { top_room = iRoom; return; } if ( iRoom > 0 && room_index[iRoom].vnum <= room_index[iRoom-1].vnum ) { bug( "Load_rooms: vnum %d not in increasing order.", room_index[iRoom].vnum ); exit( 1 ); } room_index[iRoom].name = fread_string( fp ); room_index[iRoom].description = fread_string( fp ); /* Area number */ fread_number( fp ); room_index[iRoom].room_flags = fread_number( fp ); room_index[iRoom].sector_type = fread_number( fp ); for ( ; ; ) { letter = fread_letter( fp ); if ( letter == 'S' ) break; if ( letter == 'D' ) { int door; int locks; door = fread_number( fp ); if ( door < 0 || door > 5 ) { bug( "Fread_rooms: vnum %d has bad door number.", room_index[iRoom].vnum ); exit( 1 ); } if ( top_exit >= MAX_EXIT ) { bug( "Load_rooms: MAX_EXIT: more than %d exits.", MAX_EXIT ); exit( 1 ); } room_index[iRoom].exit[door] = top_exit; exit_index[top_exit].description = fread_string( fp ); exit_index[top_exit].keyword = fread_string( fp ); exit_index[top_exit].exit_info = 0; locks = fread_number( fp ); exit_index[top_exit].key = fread_number( fp ); exit_index[top_exit].to_room = fread_number( fp ); if ( locks == 1 ) exit_index[top_exit].exit_info = EX_ISDOOR; if ( locks == 2 ) exit_index[top_exit].exit_info = EX_ISDOOR | EX_PICKPROOF; top_exit++; } else if ( letter == 'E' ) { EXTRA_DESCR_DATA *ed; ed = alloc_share( sizeof(*ed) ); ed->keyword = fread_string( fp ); ed->description = fread_string( fp ); ed->next = room_index[iRoom].ex_description; room_index[iRoom].ex_description = ed; } else { bug( "Load_rooms: vnum %d has flag not 'DES'.", room_index[iRoom].vnum ); exit( 1 ); } } } bug( "Load_rooms: MAX_ROOM: more than %d rooms.", MAX_ROOM ); exit( 1 ); return; } /* * Snarf a shop section. */ void load_shops( FILE *fp ) { int iShop; for ( iShop = top_shop; iShop < MAX_SHOP; iShop++ ) { int iTrade; shop_index[iShop].keeper = fread_number( fp ); if ( shop_index[iShop].keeper == 0 ) { top_shop = iShop; return; } for ( iTrade = 0; iTrade < MAX_TRADE; iTrade++ ) shop_index[iShop].buy_type[iTrade] = fread_number( fp ); shop_index[iShop].profit_buy = fread_number( fp ); shop_index[iShop].profit_sell = fread_number( fp ); shop_index[iShop].open_hour = fread_number( fp ); shop_index[iShop].close_hour = fread_number( fp ); fread_to_eol( fp ); mob_index[real_mobile(shop_index[iShop].keeper)].shop = iShop; } bug( "Load_shops: MAX_SHOP: more than %d shops.", MAX_SHOP ); exit( 1 ); return; } /* * Snarf spec proc declarations. */ void load_specials( FILE *fp ) { for ( ; ; ) { char *name; char letter; int vnum; switch ( letter = fread_letter( fp ) ) { default: bug( "Load_specials: letter '%c' not *MS.", letter ); return; case 'S': return; case '*': break; case 'M': vnum = fread_number( fp ); name = fread_word ( fp ); mob_index[real_mobile(vnum)].spec_fun = spec_lookup( vnum, name ); break; } fread_to_eol( fp ); } } /* * Snarf notes file. */ void load_notes( void ) { FILE *fp; NOTE_DATA *pnotelast; if ( ( fp = fopen( NOTE_FILE, "r" ) ) == NULL ) return; pnotelast = NULL; for ( ; ; ) { NOTE_DATA *pnote; char letter; do { letter = getc( fp ); if ( feof(fp) ) { fclose( fp ); return; } } while ( isspace(letter) ); ungetc( letter, fp ); pnote = alloc_share( sizeof(*pnote) ); if ( str_cmp( fread_word( fp ), "sender" ) ) break; pnote->sender = fread_string( fp ); if ( str_cmp( fread_word( fp ), "date" ) ) break; pnote->date = fread_string( fp ); if ( str_cmp( fread_word( fp ), "to" ) ) break; pnote->to_list = fread_string( fp ); if ( str_cmp( fread_word( fp ), "subject" ) ) break; pnote->subject = fread_string( fp ); if ( str_cmp( fread_word( fp ), "text" ) ) break; pnote->text = fread_string( fp ); if ( note_list == NULL ) note_list = pnote; else pnotelast->next = pnote; pnotelast = pnote; } strcpy( strArea, NOTE_FILE ); fpArea = fp; bug( "Load_notes: bad key word.", 0 ); exit( 1 ); return; } /* * Translate all room exits from virtual to real. * Has to be done after all rooms are read in. * Check for bad reverse exits. */ void fix_exits( void ) { extern const sh_int rev_dir []; char buf[MAX_STRING_LENGTH]; int iexit; int iexit_rev; int room; int to_room; int door; for ( room = 0; room < top_room; room++ ) { for ( door = 0; door <= 5; door++ ) { if ( ( iexit = room_index[room].exit[door] ) == 0 || exit_index[iexit].to_room == NOWHERE ) continue; exit_index[iexit].to_room = real_room( exit_index[iexit].to_room ); } } for ( room = 0; room < top_room; room++ ) { for ( door = 0; door <= 5; door++ ) { if ( ( iexit = room_index[room].exit[door] ) == 0 || ( to_room = exit_index[iexit].to_room ) == NOWHERE ) continue; iexit_rev = room_index[to_room].exit[rev_dir[door]]; if ( iexit_rev == 0 || exit_index[iexit_rev].to_room == room ) continue; sprintf( buf, "Fix_exits: %d:%d -> %d:%d -> %d.", room_index[room].vnum, door, room_index[to_room].vnum, rev_dir[door], exit_index[iexit_rev].to_room != NOWHERE ? room_index[exit_index[iexit_rev].to_room].vnum : NOWHERE ); bug( buf, 0 ); } } } /* * Repopulate areas periodically. */ void area_update( void ) { int area; for ( area = 0; area < top_area; area++ ) { CHAR_DATA *pch; bool fplayer; area_table[area].age++; if ( area_table[area].age < 3 ) continue; /* * Check for PC's. */ fplayer = FALSE; for ( pch = char_list; pch != NULL; pch = pch->next ) { if ( !IS_NPC(pch) && IS_AWAKE(pch) && pch->in_room != NOWHERE && room_index[pch->in_room].area == area ) { fplayer = TRUE; if ( area_table[area].age == 15 - 1 ) { send_to_char( "You hear the patter of little feet.\n\r", pch ); } break; } } /* * Check age. */ if ( fplayer && area_table[area].age < 15 ) continue; /* * Ok let's reset! */ reset_area( area, fplayer ); area_table[area].age = 0; if ( area == room_index[real_room(ROOM_VNUM_SCHOOL)].area ) area_table[area].age = 15-3; } return; } /* * Reset one area. */ void reset_area( int area, bool fplayer ) { CHAR_DATA *mob; bool last; int cmd; int level; if ( area < 0 || area >= top_area ) { bug( "Reset_area: bad area %d.", area ); return; } mob = NULL; last = TRUE; level = 0; for ( cmd = area_table[area].cmd; reset_table[cmd].command != 'S'; cmd++ ) { OBJ_DATA *obj; OBJ_DATA *obj_to; int arg1; int arg2; int arg3; int iexit; arg1 = reset_table[cmd].arg1; arg2 = reset_table[cmd].arg2; arg3 = reset_table[cmd].arg3; switch ( reset_table[cmd].command ) { case 'M': level = MAX( 0, MIN( MAX_LEVEL-5, mob_index[arg1].level-2 ) ); if ( mob_index[arg1].count >= arg2 ) { last = FALSE; break; } mob = create_mobile( arg1 ); if ( arg3 > 0 && IS_SET(room_index[arg3-1].room_flags, ROOM_PET_SHOP) ) SET_BIT(mob->act, ACT_PET); if ( room_is_dark( arg3 ) ) SET_BIT(mob->affected_by, AFF_INFRARED); char_to_room( mob, arg3 ); level = MAX( 0, MIN( MAX_LEVEL-5, mob->level-2 ) ); last = TRUE; break; case 'O': if ( fplayer || count_obj_list( arg1, room_index[arg3].contents ) > 0 ) { last = FALSE; break; } obj = create_object( arg1, level ); obj_to_room( obj, arg3 ); last = TRUE; break; case 'P': if ( fplayer || ( obj_to = get_obj_num( arg3 ) ) == NULL || obj_to->in_room == NOWHERE || count_obj_list( arg1, obj_to->contains ) > 0 ) { last = FALSE; break; } obj = create_object( arg1, obj_to->level ); obj_to_obj( obj, obj_to ); last = TRUE; break; case 'G': case 'E': if ( !last ) break; if ( mob == NULL ) { bug( "Reset_area: 'E' or 'G': null mob.", 0 ); last = FALSE; break; } if ( mob_index[mob->rnum].shop != SHOP_NONE ) { obj = create_object( arg1, 6 ); SET_BIT( obj->extra_flags, ITEM_INVENTORY ); } else { obj = create_object( arg1, level ); } obj_to_char( obj, mob ); if ( reset_table[cmd].command == 'E' ) equip_char( mob, obj, arg3 ); last = TRUE; break; case 'D': if ( ( iexit = room_index[arg1].exit[arg2] ) == 0 ) break; switch ( arg3 ) { case 0: REMOVE_BIT( exit_index[iexit].exit_info, EX_CLOSED ); REMOVE_BIT( exit_index[iexit].exit_info, EX_LOCKED ); break; case 1: SET_BIT( exit_index[iexit].exit_info, EX_CLOSED ); REMOVE_BIT( exit_index[iexit].exit_info, EX_LOCKED ); break; case 2: SET_BIT( exit_index[iexit].exit_info, EX_CLOSED ); SET_BIT( exit_index[iexit].exit_info, EX_LOCKED ); break; } last = TRUE; break; case 'R': { int d0; int d1; for ( d0 = 0; d0 < arg2 - 1; d0++ ) { d1 = number_range( d0, arg2-1 ); iexit = room_index[arg1].exit[d0]; room_index[arg1].exit[d0] = room_index[arg1].exit[d1]; room_index[arg1].exit[d1] = iexit; } } break; } } return; } /* * Create an instance of a mobile. */ CHAR_DATA *create_mobile( int rnum ) { CHAR_DATA *mob; if ( rnum < 0 || rnum >= top_mob ) { bug( "Create_mobile: %d out of range.", rnum ); exit( 1 ); } if ( char_free == NULL ) { mob = alloc_mem( sizeof(*mob) ); } else { mob = char_free; char_free = char_free->next; } clear_char( mob ); mob->rnum = rnum; mob->name = mob_index[rnum].player_name; mob->short_descr = mob_index[rnum].short_descr; mob->long_descr = mob_index[rnum].long_descr; mob->description = mob_index[rnum].description; mob->level = number_fuzzy( mob_index[rnum].level ); mob->act = mob_index[rnum].act; mob->affected_by = mob_index[rnum].affected_by; mob->alignment = mob_index[rnum].alignment; mob->sex = mob_index[rnum].sex; mob->armor = interpolate( mob->level, 100, -100 ); mob->max_hit = mob->level * 8 + number_range( mob->level * mob->level / 4, mob->level * mob->level ); mob->hit = mob->max_hit; /* * Insert in list. */ mob->next = char_list; char_list = mob; mob_index[rnum].count++; return mob; } /* * Create an instance of an object. */ OBJ_DATA *create_object( int rnum, int level ) { static OBJ_DATA obj_zero; OBJ_DATA *obj; if ( rnum < 0 || rnum >= top_obj ) { bug( "Create_object: rnum %d out of range.", rnum ); exit( 1 ); } if ( obj_free == NULL ) { obj = alloc_mem( sizeof(*obj) ); } else { obj = obj_free; obj_free = obj_free->next; } *obj = obj_zero; obj->rnum = rnum; obj->in_room = NOWHERE; obj->level = level; obj->wear_loc = -1; obj->name = obj_index[rnum].name; obj->short_descr = obj_index[rnum].short_descr; obj->description = obj_index[rnum].description; obj->item_type = obj_index[rnum].item_type; obj->extra_flags = obj_index[rnum].extra_flags; obj->wear_flags = obj_index[rnum].wear_flags; obj->value[0] = obj_index[rnum].value[0]; obj->value[1] = obj_index[rnum].value[1]; obj->value[2] = obj_index[rnum].value[2]; obj->value[3] = obj_index[rnum].value[3]; obj->weight = obj_index[rnum].weight; obj->cost = number_fuzzy( 10 ) * number_fuzzy( level ) * number_fuzzy( level ); /* * The ED's and affects reside completely in shared space, * so we just copy by reference. */ obj->affected = obj_index[rnum].affected; obj->ex_description = obj_index[rnum].ex_description; /* * Mess with object properties. */ switch ( obj->item_type ) { default: bug( "Read_object: vnum %d bad type.", obj_index[rnum].vnum ); break; case ITEM_LIGHT: case ITEM_TREASURE: case ITEM_FURNITURE: case ITEM_TRASH: case ITEM_CONTAINER: case ITEM_DRINK_CON: case ITEM_KEY: case ITEM_FOOD: case ITEM_BOAT: case ITEM_CORPSE_NPC: case ITEM_CORPSE_PC: case ITEM_FOUNTAIN: break; case ITEM_SCROLL: obj->value[0] = number_fuzzy( obj->value[0] ); break; case ITEM_WAND: case ITEM_STAFF: obj->value[0] = number_fuzzy( obj->value[0] ); obj->value[1] = number_fuzzy( obj->value[1] ); obj->value[2] = obj->value[1]; break; case ITEM_WEAPON: obj->value[1] = number_fuzzy( number_fuzzy( 1 * level / 4 + 2 ) ); obj->value[2] = number_fuzzy( number_fuzzy( 3 * level / 4 + 6 ) ); break; case ITEM_ARMOR: obj->value[0] = number_fuzzy( level / 4 + 2 ); break; case ITEM_POTION: case ITEM_PILL: obj->value[0] = number_fuzzy( number_fuzzy( obj->value[0] ) ); break; case ITEM_MONEY: obj->value[0] = obj->cost; break; } obj->next = object_list; object_list = obj; obj_index[rnum].count++; return obj; } /* * Clear a new character. */ void clear_char( CHAR_DATA *ch ) { static CHAR_DATA ch_zero; *ch = ch_zero; ch->name = &share_space[0]; ch->pwd = &share_space[0]; ch->short_descr = &share_space[0]; ch->long_descr = &share_space[0]; ch->description = &share_space[0]; ch->title = &share_space[0]; ch->poofin = &share_space[0]; ch->poofout = &share_space[0]; ch->in_room = NOWHERE; ch->logon = current_time; ch->armor = 100; ch->was_in_room = NOWHERE; ch->position = POS_STANDING; ch->practice = 21; ch->hit = 20; ch->max_hit = 20; ch->mana = 100; ch->max_mana = 100; ch->move = 100; ch->max_move = 100; return; } /* * Free a character. */ void free_char( CHAR_DATA *ch ) { OBJ_DATA *obj; OBJ_DATA *obj_next; AFFECT_DATA *paf; AFFECT_DATA *paf_next; for ( obj = ch->carrying; obj != NULL; obj = obj_next ) { obj_next = obj->next_content; extract_obj( obj ); } for ( paf = ch->affected; paf != NULL; paf = paf_next ) { paf_next = paf->next; affect_remove( ch, paf ); } free_mem( ch->name ); free_mem( ch->pwd ); free_mem( ch->short_descr ); free_mem( ch->long_descr ); free_mem( ch->description ); free_mem( ch->title ); free_mem( ch->poofin ); free_mem( ch->poofout ); free_mem( ch->pcdata ); ch->next = char_free; char_free = ch; return; } /* * Translates virtual room number to real room number. * Binary search. */ int real_room( int vnum ) { int bot; int top; int mid; bot = 0; top = top_room - 1; for ( ; ; ) { mid = (bot + top) / 2; if ( room_index[mid].vnum == vnum ) return mid; if ( bot >= top ) break; if ( room_index[mid].vnum > vnum ) top = mid - 1; else bot = mid + 1; } if ( abort_bad_vnum ) { bug( "Real_room: bad vnum %d.", vnum ); exit( 1 ); } return -1; } /* * Translates virtual mob number to real mob number. * Binary search. */ int real_mobile( int vnum ) { int bot; int top; int mid; bot = 0; top = top_mob - 1; for ( ; ; ) { mid = (bot + top) / 2; if ( mob_index[mid].vnum == vnum ) return mid; if ( bot >= top ) break; if ( mob_index[mid].vnum > vnum ) top = mid - 1; else bot = mid + 1; } if ( abort_bad_vnum ) { bug( "Real_mobile: bad vnum %d.", vnum ); exit( 1 ); } return -1; } /* * Translates virtual obj number to real obj number. * Binary search. */ int real_object( int vnum ) { int bot; int top; int mid; bot = 0; top = top_obj - 1; for ( ; ; ) { mid = (bot + top) / 2; if ( obj_index[mid].vnum == vnum ) return mid; if ( bot >= top ) break; if ( obj_index[mid].vnum > vnum ) top = mid - 1; else bot = mid + 1; } if ( abort_bad_vnum ) { bug( "Read_object: bad vnum %d.", vnum ); exit( 1 ); } return -1; } /* * Read a letter from a file. */ char fread_letter( FILE *fp ) { char c; do { c = getc( fp ); } while ( isspace(c) ); return c; } /* * Read a number from a file. */ int fread_number( FILE *fp ) { int number; bool sign; char c; do { c = getc( fp ); } while ( isspace(c) ); number = 0; sign = FALSE; if ( c == '+' ) { c = getc( fp ); } else if ( c == '-' ) { sign = TRUE; c = getc( fp ); } while ( isdigit(c) ) { number = number * 10 + c - '0'; c = getc( fp ); } if ( sign ) number = 0 - number; if ( c == '|' ) number += fread_number( fp ); else if ( c != ' ' ) ungetc( c, fp ); return number; } /* * Read and allocate space for a string from a file. * These strings are read-only and shared. * Note that share_space[0] == '\0', all zero-length strings are there. * Char type lookup and funny code for even more speed, * this function takes 40% to 50% of boot-up time. */ char *fread_string( FILE *fp ) { static bool char_special[256-EOF]; HASH_DATA *phash; int hash_key; char *plast; char c; if ( char_special[EOF-EOF] != TRUE ) { char_special[EOF-EOF] = TRUE; char_special['\n'-EOF] = TRUE; char_special['\r'-EOF] = TRUE; char_special['~'-EOF] = TRUE; } if ( ( plast = top_share ) > &share_space[MAX_SHARE - MAX_STRING_LENGTH] ) { bug( "Fread_string: more than %d shared space (MAX_SHARE).", MAX_SHARE ); exit( 1 ); } /* * Skip blanks. * Read first char. */ do { c = getc( fp ); } while ( isspace(c) ); if ( ( *plast++ = c ) == '~' ) return &share_space[0]; for ( ;; ) { if ( !char_special[ ( *plast++ = getc( fp ) ) - EOF ] ) continue; switch ( plast[-1] ) { default: break; case EOF: bug( "Fread_string: EOF", 0 ); exit( 1 ); break; case '\n': *plast++ = '\r'; break; case '\r': plast--; break; case '~': plast[-1] = '\0'; hash_key = MIN( MAX_KEY_HASH - 1, plast - 1 - top_share ); for ( phash = hash_table[hash_key]; phash; phash = phash->next ) { if ( phash->string[0] == top_share[0] && !strcmp( phash->string+1, top_share+1 ) ) { tail_chain( ); /* For profiling */ return phash->string; } } if ( top_hash >= MAX_HASH ) { bug( "Fread_string: more than %d hashed strings (MAX_HASH).", MAX_HASH ); exit( 1 ); } phash = &hash_block[top_hash++]; phash->string = top_share; phash->next = hash_table[hash_key]; hash_table[hash_key] = phash; top_share = plast; return phash->string; } } } /* * Read to end of line (for comments). */ void fread_to_eol( FILE *fp ) { char c; do { c = getc( fp ); } while ( c != '\n' && c != '\r' ); do { c = getc( fp ); } while ( c == '\n' || c == '\r' ); ungetc( c, fp ); return; } /* * Read one word (into static buffer). */ char *fread_word( FILE *fp ) { static char word[MAX_INPUT_LENGTH]; char *pword; char cEnd; do { word[0] = getc( fp ); } while ( isspace( word[0] ) ); cEnd = ( word[0] == '\'' || word[0] == '"' ) ? word[0] : ' '; for ( pword = word + 1; pword < word + MAX_INPUT_LENGTH; pword++ ) { *pword = getc( fp ); if ( ( cEnd == ' ' ) ? isspace( *pword ) : *pword == cEnd ) { if ( cEnd == ' ' ) ungetc( *pword, fp ); *pword = '\0'; return word; } } bug( "Fread_word: word too long.", 0 ); exit( 1 ); return NULL; } /* * Duplicate a string into dynamic memory. * Fread_strings are read-only and shared. */ char *str_dup( const char *str ) { char *str_new; if ( str >= share_space && str < top_share ) return (char *) str; if ( str[0] == '\0' ) return &share_space[0]; str_new = alloc_mem( strlen(str) + 1 ); strcpy( str_new, str ); return str_new; } /* * Allocate some (permanent) shared memory. * Have to dance with memory alignment. */ #define MASK (sizeof(long) - 1) void *alloc_share( int size ) { void *pMem; top_share = (char *) ( ( ( (long) top_share + MASK ) ) & ~ MASK ); if ( top_share + size >= &share_space[MAX_SHARE] ) { bug( "Alloc_share: more than %d shared space (MAX_SHARE).", MAX_SHARE ); exit( 1 ); } pMem = top_share; top_share += size; return pMem; } /* * Allocate some memory. */ void *alloc_mem( int size ) { void *pMem; nAlloc += 1; sAlloc += size; if ( ( pMem = malloc( size ) ) == NULL ) { perror( "Alloc_mem" ); exit( 1 ); } return pMem; } /* * Free some memory. * Null is legal here to simplify callers. * Read-only shared strings are not touched. */ void free_mem( void *pMem ) { if ( pMem == NULL ) return; if ( (char *) pMem >= share_space && (char *) pMem < top_share ) return; free( pMem ); return; } void do_areas( CHAR_DATA *ch, char *argument ) { char buf[MAX_STRING_LENGTH]; int area; int half; half = (top_area + 1) / 2; for ( area = 0; area < half; area++ ) { sprintf( buf, "%-39s%-39s\n\r", area_table[area].name, area+half < top_area ? area_table[area+half].name : "" ); send_to_char( buf, ch ); } return; } void do_memory( CHAR_DATA *ch, char *argument ) { char buf[MAX_STRING_LENGTH]; sprintf( buf, "Areas: %7d of %7d.\n\rExits: %7d of %7d.\n\rHelps: %7d of %7d.\n\rMobs: %7d of %7d.\n\rObjs: %7d of %7d.\n\rResets: %7d of %7d.\n\rRooms: %7d of %7d.\n\rShops: %7d of %7d.\n\rShare: %7d of %7d.\n\rHash: %7d of %7d.\n\rMalloc: %7d of %7d.\n\r", top_area, MAX_AREA, top_exit, MAX_EXIT, top_help, MAX_HELP, top_mob, MAX_MOBILE, top_obj, MAX_OBJECT, top_reset, MAX_RESET, top_room, MAX_ROOM, top_shop, MAX_SHOP, top_share - share_space, MAX_SHARE, top_hash, MAX_HASH, nAlloc, sAlloc ); send_to_char( buf, ch ); return; } /* * Stick a little fuzz on a number. */ int number_fuzzy( int number ) { switch ( number_bits( 2 ) ) { case 0: number -= 1; break; case 3: number += 1; break; } return MAX( 1, number ); } /* * Generate a random number. */ int number_range( int from, int to ) { int bits; int power; int number; if ( ( to = to - from + 1 ) <= 1 ) return from; for ( bits = 1, power = 2; power < to; bits++, power <<= 1 ) ; while ( ( number = number_bits( bits ) ) >= to ) ; return from + number; } /* * Generate a percentile roll. */ int number_percent( void ) { int percent; while ( ( percent = number_bits( 7 ) ) >= 100 ) ; return 1 + percent; } /* * Generate a random door. */ int number_door( void ) { int door; while ( ( door = number_bits( 3 ) ) > 5 ) ; return door; } /* * Special high-performance random number generator. * Note the '32' is slightly machine dependent (32-bit random numbers). * We avoid division (expensive on RISC machines) and ration out bits. * Highest bit (sign bit) is unused to avoid sign-extension worries. * -- Furey */ int number_bits( int width ) { static long latch_value; static int latch_width; int value; if ( latch_width < width ) { latch_value = random( ); latch_width = 32 - 1; } value = (int) ( latch_value & ( ( 1 << width ) - 1 ) ); latch_value >>= width; latch_width -= width; return value; } /* * Roll some dice. */ int dice( int number, int size ) { int idice; int sum; switch ( size ) { case 0: return 0; case 1: return number; } for ( idice = 0, sum = 0; idice < number; idice++ ) sum += number_range( 1, size ); return sum; } /* * Simple linear interpolation. */ int interpolate( int level, int value_00, int value_32 ) { return value_00 + level * (value_32 - value_00) / 32; } /* * Removes the tildes from a string. * Used for player-entered strings that go into disk files. */ void smash_tilde( char *str ) { for ( ; *str != '\0'; str++ ) { if ( *str == '~' ) *str = '-'; } return; } /* * Compare strings, case insensitive. * Return TRUE if different (compatability with historical functions). */ bool str_cmp( const char *astr, const char *bstr ) { if ( astr == NULL ) { bug( "Str_cmp: null astr.", 0 ); return TRUE; } if ( bstr == NULL ) { bug( "Str_cmp: null bstr.", 0 ); return TRUE; } for ( ; *astr || *bstr; astr++, bstr++ ) { if ( LOWER(*astr) != LOWER(*bstr) ) return TRUE; } return FALSE; } /* * Returns an initial-capped string. */ char *capitalize( const char *str ) { static char *strcap; static int caplen; int length; int i; length = strlen( str ); if ( length + 1 > caplen ) { free_mem( strcap ); caplen = length + 1; strcap = alloc_mem( length + 1 ); } for ( i = 0; str[i] != '\0'; i++ ) strcap[i] = LOWER(str[i]); strcap[i] = '\0'; strcap[0] = UPPER(strcap[0]); return strcap; } /* * Append a string to a file. */ void append_file( CHAR_DATA *ch, char *file, char *str ) { FILE *fp; if ( IS_NPC(ch) || str[0] == '\0' ) return; fclose( fpReserve ); if ( ( fp = fopen( file, "a" ) ) == NULL ) { perror( file ); send_to_char( "Could not open the file!\n\r", ch ); } else { fprintf( fp, "[%5d] %s: %s\n", room_index[ch->in_room].vnum, ch->name, str ); fclose( fp ); } fpReserve = fopen( NULL_FILE, "r" ); return; } /* * Reports a bug. */ void bug( const char *str, int param ) { char buf[MAX_STRING_LENGTH]; FILE *fp; if ( fpArea != NULL ) { int iLine; int iChar; if ( fpArea == stdin ) { iLine = 0; } else { iChar = ftell( fpArea ); fseek( fpArea, 0, 0 ); for ( iLine = 0; ftell( fpArea ) < iChar; iLine++ ) { while ( getc( fpArea ) != '\n' ) ; } fseek( fpArea, iChar, 0 ); } sprintf( buf, "[*****] FILE: %s LINE: %d", strArea, iLine ); log_string( buf ); if ( ( fp = fopen( "shutdown.txt", "a" ) ) != NULL ) { fprintf( fp, "[*****] %s\n", buf ); fclose( fp ); } } strcpy( buf, "[*****] BUG: " ); sprintf( buf + strlen(buf), str, param ); log_string( buf ); fclose( fpReserve ); if ( ( fp = fopen( BUG_FILE, "a" ) ) != NULL ) { fprintf( fp, "%s\n", buf ); fclose( fp ); } fpReserve = fopen( NULL_FILE, "r" ); return; } /* * Writes a string to the log. */ void log_string( const char *str ) { char *strtime; strtime = ctime( ¤t_time ); strtime[strlen(strtime)-1] = '\0'; fprintf( stderr, "%s :: %s\n", strtime, str ); return; } /* * This function is here to aid in debugging. * If the last expression in a function is another function call, * gcc likes to generate a JMP instead of a CALL. * This is called "tail chaining." * It hoses the debugger call stack for that call. * So I make this the last call in certain critical functions, * where I really need the call stack to be right for debugging! * * If you don't understand this, then LEAVE IT ALONE. * Don't remove any calls to tail_chain anywhere. * * -- Furey */ void tail_chain( void ) { return; }