db.c
1 /* ************************************************************************ 2 * File: db.c Part of CircleMUD * 3 * Usage: Loading/saving chars, booting/resetting world, internal funcs * 4 * * 5 * All rights reserved. See license.doc for complete information. * 6 * * 7 * Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University * 8 * CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991. * 9 ************************************************************************ */ 10 11 #define __DB_C__ 12 13 #include "conf.h" 14 #include "sysdep.h" 15 16 17 #include "structs.h" 18 #include "utils.h" 19 #include "db.h" 20 #include "comm.h" 21 #include "handler.h" 22 #include "spells.h" 23 #include "mail.h" 24 #include "interpreter.h" 25 #include "house.h" 26 #include "constants.h" 27 28 /************************************************************************** 29 * declarations of most of the 'global' variables * 30 **************************************************************************/ 31 32 struct room_data *world = NULL; /* array of rooms */ 33 room_rnum top_of_world = 0; /* ref to top element of world */ 34 35 struct char_data *character_list = NULL; /* global linked list of 36 * chars */ 37 struct index_data *mob_index; /* index table for mobile file */ 38 struct char_data *mob_proto; /* prototypes for mobs */ 39 mob_rnum top_of_mobt = 0; /* top of mobile index table */ 40 41 struct obj_data *object_list = NULL; /* global linked list of objs */ 42 struct index_data *obj_index; /* index table for object file */ 43 struct obj_data *obj_proto; /* prototypes for objs */ 44 obj_rnum top_of_objt = 0; /* top of object index table */ 45 46 struct zone_data *zone_table; /* zone table */ 47 zone_rnum top_of_zone_table = 0;/* top element of zone tab */ 48 struct message_list fight_messages[MAX_MESSAGES]; /* fighting messages */ 49 50 struct player_index_element *player_table = NULL; /* index to plr file */ 51 FILE *player_fl = NULL; /* file desc of player file */ 52 int top_of_p_table = 0; /* ref to top of table */ 53 long top_idnum = 0; /* highest idnum in use */ 54 55 int no_mail = 0; /* mail disabled? */ 56 int mini_mud = 0; /* mini-mud mode? */ 57 int no_rent_check = 0; /* skip rent check on boot? */ 58 time_t boot_time = 0; /* time of mud boot */ 59 int circle_restrict = 0; /* level of game restriction */ 60 room_rnum r_mortal_start_room; /* rnum of mortal start room */ 61 room_rnum r_immort_start_room; /* rnum of immort start room */ 62 room_rnum r_frozen_start_room; /* rnum of frozen start room */ 63 64 char *credits = NULL; /* game credits */ 65 char *news = NULL; /* mud news */ 66 char *motd = NULL; /* message of the day - mortals */ 67 char *imotd = NULL; /* message of the day - immorts */ 68 char *GREETINGS = NULL; /* opening credits screen */ 69 char *help = NULL; /* help screen */ 70 char *info = NULL; /* info page */ 71 char *wizlist = NULL; /* list of higher gods */ 72 char *immlist = NULL; /* list of peon gods */ 73 char *background = NULL; /* background story */ 74 char *handbook = NULL; /* handbook for new immortals */ 75 char *policies = NULL; /* policies page */ 76 77 struct help_index_element *help_table = 0; /* the help table */ 78 int top_of_helpt = 0; /* top of help index table */ 79 80 struct time_info_data time_info;/* the infomation about the time */ 81 struct weather_data weather_info; /* the infomation about the weather */ 82 struct player_special_data dummy_mob; /* dummy spec area for mobs */ 83 struct reset_q_type reset_q; /* queue of zones to be reset */ 84 85 /* local functions */ 86 int check_bitvector_names(bitvector_t bits, size_t namecount, const char *whatami, const char *whatbits); 87 int check_object_spell_number(struct obj_data *obj, int val); 88 int check_object_level(struct obj_data *obj, int val); 89 void setup_dir(FILE *fl, int room, int dir); 90 void index_boot(int mode); 91 void discrete_load(FILE *fl, int mode, char *filename); 92 int check_object(struct obj_data *); 93 void parse_room(FILE *fl, int virtual_nr); 94 void parse_mobile(FILE *mob_f, int nr); 95 char *parse_object(FILE *obj_f, int nr); 96 void load_zones(FILE *fl, char *zonename); 97 void load_help(FILE *fl); 98 void assign_mobiles(void); 99 void assign_objects(void); 100 void assign_rooms(void); 101 void assign_the_shopkeepers(void); 102 void build_player_index(void); 103 int is_empty(zone_rnum zone_nr); 104 void reset_zone(zone_rnum zone); 105 int file_to_string(const char *name, char *buf); 106 int file_to_string_alloc(const char *name, char **buf); 107 void reboot_wizlists(void); 108 ACMD(do_reboot); 109 void boot_world(void); 110 int count_alias_records(FILE *fl); 111 int count_hash_records(FILE *fl); 112 bitvector_t asciiflag_conv(char *flag); 113 void parse_simple_mob(FILE *mob_f, int i, int nr); 114 void interpret_espec(const char *keyword, const char *value, int i, int nr); 115 void parse_espec(char *buf, int i, int nr); 116 void parse_enhanced_mob(FILE *mob_f, int i, int nr); 117 void get_one_line(FILE *fl, char *buf); 118 void save_etext(struct char_data *ch); 119 void check_start_rooms(void); 120 void renum_world(void); 121 void renum_zone_table(void); 122 void log_zone_error(zone_rnum zone, int cmd_no, const char *message); 123 void reset_time(void); 124 long get_ptable_by_name(const char *name); 125 126 /* external functions */ 127 void paginate_string(char *str, struct descriptor_data *d); 128 struct time_info_data *mud_time_passed(time_t t2, time_t t1); 129 void free_alias(struct alias_data *a); 130 void load_messages(void); 131 void weather_and_time(int mode); 132 void mag_assign_spells(void); 133 void boot_social_messages(void); 134 void update_obj_file(void); /* In objsave.c */ 135 void sort_commands(void); 136 void sort_spells(void); 137 void load_banned(void); 138 void Read_Invalid_List(void); 139 void boot_the_shops(FILE *shop_f, char *filename, int rec_count); 140 int hsort(const void *a, const void *b); 141 void prune_crlf(char *txt); 142 void destroy_shops(void); 143 144 /* external vars */ 145 extern int no_specials; 146 extern int scheck; 147 extern room_vnum mortal_start_room; 148 extern room_vnum immort_start_room; 149 extern room_vnum frozen_start_room; 150 extern struct descriptor_data *descriptor_list; 151 extern const char *unused_spellname; /* spell_parser.c */ 152 153 /************************************************************************* 154 * routines for booting the system * 155 *************************************************************************/ 156 157 /* this is necessary for the autowiz system */ 158 void reboot_wizlists(void) 159 { 160 file_to_string_alloc(WIZLIST_FILE, &wizlist); 161 file_to_string_alloc(IMMLIST_FILE, &immlist); 162 } 163 164 165 /* Wipe out all the loaded text files, for shutting down. */ 166 void free_text_files(void) 167 { 168 char **textfiles[] = { 169 &wizlist, &immlist, &news, &credits, &motd, &imotd, &help, &info, 170 &policies, &handbook, &background, &GREETINGS, NULL 171 }; 172 int rf; 173 174 for (rf = 0; textfiles[rf]; rf++) 175 if (*textfiles[rf]) { 176 free(*textfiles[rf]); 177 *textfiles[rf] = NULL; 178 } 179 } 180 181 182 /* 183 * Too bad it doesn't check the return values to let the user 184 * know about -1 values. This will result in an 'Okay.' to a 185 * 'reload' command even when the string was not replaced. 186 * To fix later, if desired. -gg 6/24/99 187 */ 188 ACMD(do_reboot) 189 { 190 char arg[MAX_INPUT_LENGTH]; 191 192 one_argument(argument, arg); 193 194 if (!str_cmp(arg, "all") || *arg == '*') { 195 if (file_to_string_alloc(GREETINGS_FILE, &GREETINGS) == 0) 196 prune_crlf(GREETINGS); 197 file_to_string_alloc(WIZLIST_FILE, &wizlist); 198 file_to_string_alloc(IMMLIST_FILE, &immlist); 199 file_to_string_alloc(NEWS_FILE, &news); 200 file_to_string_alloc(CREDITS_FILE, &credits); 201 file_to_string_alloc(MOTD_FILE, &motd); 202 file_to_string_alloc(IMOTD_FILE, &imotd); 203 file_to_string_alloc(HELP_PAGE_FILE, &help); 204 file_to_string_alloc(INFO_FILE, &info); 205 file_to_string_alloc(POLICIES_FILE, &policies); 206 file_to_string_alloc(HANDBOOK_FILE, &handbook); 207 file_to_string_alloc(BACKGROUND_FILE, &background); 208 } else if (!str_cmp(arg, "wizlist")) 209 file_to_string_alloc(WIZLIST_FILE, &wizlist); 210 else if (!str_cmp(arg, "immlist")) 211 file_to_string_alloc(IMMLIST_FILE, &immlist); 212 else if (!str_cmp(arg, "news")) 213 file_to_string_alloc(NEWS_FILE, &news); 214 else if (!str_cmp(arg, "credits")) 215 file_to_string_alloc(CREDITS_FILE, &credits); 216 else if (!str_cmp(arg, "motd")) 217 file_to_string_alloc(MOTD_FILE, &motd); 218 else if (!str_cmp(arg, "imotd")) 219 file_to_string_alloc(IMOTD_FILE, &imotd); 220 else if (!str_cmp(arg, "help")) 221 file_to_string_alloc(HELP_PAGE_FILE, &help); 222 else if (!str_cmp(arg, "info")) 223 file_to_string_alloc(INFO_FILE, &info); 224 else if (!str_cmp(arg, "policy")) 225 file_to_string_alloc(POLICIES_FILE, &policies); 226 else if (!str_cmp(arg, "handbook")) 227 file_to_string_alloc(HANDBOOK_FILE, &handbook); 228 else if (!str_cmp(arg, "background")) 229 file_to_string_alloc(BACKGROUND_FILE, &background); 230 else if (!str_cmp(arg, "greetings")) { 231 if (file_to_string_alloc(GREETINGS_FILE, &GREETINGS) == 0) 232 prune_crlf(GREETINGS); 233 } else if (!str_cmp(arg, "xhelp")) { 234 if (help_table) 235 free_help(); 236 index_boot(DB_BOOT_HLP); 237 } else { 238 send_to_char(ch, "Unknown reload option.\r\n"); 239 return; 240 } 241 242 send_to_char(ch, "%s", OK); 243 } 244 245 246 void boot_world(void) 247 { 248 log("Loading zone table."); 249 index_boot(DB_BOOT_ZON); 250 251 log("Loading rooms."); 252 index_boot(DB_BOOT_WLD); 253 254 log("Renumbering rooms."); 255 renum_world(); 256 257 log("Checking start rooms."); 258 check_start_rooms(); 259 260 log("Loading mobs and generating index."); 261 index_boot(DB_BOOT_MOB); 262 263 log("Loading objs and generating index."); 264 index_boot(DB_BOOT_OBJ); 265 266 log("Renumbering zone table."); 267 renum_zone_table(); 268 269 if (!no_specials) { 270 log("Loading shops."); 271 index_boot(DB_BOOT_SHP); 272 } 273 } 274 275 276 void free_extra_descriptions(struct extra_descr_data *edesc) 277 { 278 struct extra_descr_data *enext; 279 280 for (; edesc; edesc = enext) { 281 enext = edesc->next; 282 283 free(edesc->keyword); 284 free(edesc->description); 285 free(edesc); 286 } 287 } 288 289 290 /* Free the world, in a memory allocation sense. */ 291 void destroy_db(void) 292 { 293 ssize_t cnt, itr; 294 struct char_data *chtmp; 295 struct obj_data *objtmp; 296 297 /* Active Mobiles & Players */ 298 while (character_list) { 299 chtmp = character_list; 300 character_list = character_list->next; 301 free_char(chtmp); 302 } 303 304 /* Active Objects */ 305 while (object_list) { 306 objtmp = object_list; 307 object_list = object_list->next; 308 free_obj(objtmp); 309 } 310 311 /* Rooms */ 312 for (cnt = 0; cnt <= top_of_world; cnt++) { 313 if (world[cnt].name) 314 free(world[cnt].name); 315 if (world[cnt].description) 316 free(world[cnt].description); 317 free_extra_descriptions(world[cnt].ex_description); 318 319 for (itr = 0; itr < NUM_OF_DIRS; itr++) { 320 if (!world[cnt].dir_option[itr]) 321 continue; 322 323 if (world[cnt].dir_option[itr]->general_description) 324 free(world[cnt].dir_option[itr]->general_description); 325 if (world[cnt].dir_option[itr]->keyword) 326 free(world[cnt].dir_option[itr]->keyword); 327 free(world[cnt].dir_option[itr]); 328 } 329 } 330 free(world); 331 332 /* Objects */ 333 for (cnt = 0; cnt <= top_of_objt; cnt++) { 334 if (obj_proto[cnt].name) 335 free(obj_proto[cnt].name); 336 if (obj_proto[cnt].description) 337 free(obj_proto[cnt].description); 338 if (obj_proto[cnt].short_description) 339 free(obj_proto[cnt].short_description); 340 if (obj_proto[cnt].action_description) 341 free(obj_proto[cnt].action_description); 342 free_extra_descriptions(obj_proto[cnt].ex_description); 343 } 344 free(obj_proto); 345 free(obj_index); 346 347 /* Mobiles */ 348 for (cnt = 0; cnt <= top_of_mobt; cnt++) { 349 if (mob_proto[cnt].player.name) 350 free(mob_proto[cnt].player.name); 351 if (mob_proto[cnt].player.title) 352 free(mob_proto[cnt].player.title); 353 if (mob_proto[cnt].player.short_descr) 354 free(mob_proto[cnt].player.short_descr); 355 if (mob_proto[cnt].player.long_descr) 356 free(mob_proto[cnt].player.long_descr); 357 if (mob_proto[cnt].player.description) 358 free(mob_proto[cnt].player.description); 359 360 while (mob_proto[cnt].affected) 361 affect_remove(&mob_proto[cnt], mob_proto[cnt].affected); 362 } 363 free(mob_proto); 364 free(mob_index); 365 366 /* Shops */ 367 destroy_shops(); 368 369 /* Zones */ 370 for (cnt = 0; cnt <= top_of_zone_table; cnt++) { 371 if (zone_table[cnt].name) 372 free(zone_table[cnt].name); 373 if (zone_table[cnt].cmd) 374 free(zone_table[cnt].cmd); 375 } 376 free(zone_table); 377 } 378 379 380 /* body of the booting system */ 381 void boot_db(void) 382 { 383 zone_rnum i; 384 385 log("Boot db -- BEGIN."); 386 387 log("Resetting the game time:"); 388 reset_time(); 389 390 log("Reading news, credits, help, bground, info & motds."); 391 file_to_string_alloc(NEWS_FILE, &news); 392 file_to_string_alloc(CREDITS_FILE, &credits); 393 file_to_string_alloc(MOTD_FILE, &motd); 394 file_to_string_alloc(IMOTD_FILE, &imotd); 395 file_to_string_alloc(HELP_PAGE_FILE, &help); 396 file_to_string_alloc(INFO_FILE, &info); 397 file_to_string_alloc(WIZLIST_FILE, &wizlist); 398 file_to_string_alloc(IMMLIST_FILE, &immlist); 399 file_to_string_alloc(POLICIES_FILE, &policies); 400 file_to_string_alloc(HANDBOOK_FILE, &handbook); 401 file_to_string_alloc(BACKGROUND_FILE, &background); 402 if (file_to_string_alloc(GREETINGS_FILE, &GREETINGS) == 0) 403 prune_crlf(GREETINGS); 404 405 log("Loading spell definitions."); 406 mag_assign_spells(); 407 408 boot_world(); 409 410 log("Loading help entries."); 411 index_boot(DB_BOOT_HLP); 412 413 log("Generating player index."); 414 build_player_index(); 415 416 log("Loading fight messages."); 417 load_messages(); 418 419 log("Loading social messages."); 420 boot_social_messages(); 421 422 log("Assigning function pointers:"); 423 424 if (!no_specials) { 425 log(" Mobiles."); 426 assign_mobiles(); 427 log(" Shopkeepers."); 428 assign_the_shopkeepers(); 429 log(" Objects."); 430 assign_objects(); 431 log(" Rooms."); 432 assign_rooms(); 433 } 434 435 log("Assigning spell and skill levels."); 436 init_spell_levels(); 437 438 log("Sorting command list and spells."); 439 sort_commands(); 440 sort_spells(); 441 442 log("Booting mail system."); 443 if (!scan_file()) { 444 log(" Mail boot failed -- Mail system disabled"); 445 no_mail = 1; 446 } 447 log("Reading banned site and invalid-name list."); 448 load_banned(); 449 Read_Invalid_List(); 450 451 if (!no_rent_check) { 452 log("Deleting timed-out crash and rent files:"); 453 update_obj_file(); 454 log(" Done."); 455 } 456 457 /* Moved here so the object limit code works. -gg 6/24/98 */ 458 if (!mini_mud) { 459 log("Booting houses."); 460 House_boot(); 461 } 462 463 for (i = 0; i <= top_of_zone_table; i++) { 464 log("Resetting #%d: %s (rooms %d-%d).", zone_table[i].number, 465 zone_table[i].name, zone_table[i].bot, zone_table[i].top); 466 reset_zone(i); 467 } 468 469 reset_q.head = reset_q.tail = NULL; 470 471 boot_time = time(0); 472 473 log("Boot db -- DONE."); 474 } 475 476 477 /* reset the time in the game from file */ 478 void reset_time(void) 479 { 480 time_t beginning_of_time = 0; 481 FILE *bgtime; 482 483 if ((bgtime = fopen(TIME_FILE, "r")) == NULL) 484 log("SYSERR: Can't read from '%s' time file.", TIME_FILE); 485 else { 486 fscanf(bgtime, "%ld\n", &beginning_of_time); 487 fclose(bgtime); 488 } 489 if (beginning_of_time == 0) 490 beginning_of_time = 650336715; 491 492 time_info = *mud_time_passed(time(0), beginning_of_time); 493 494 if (time_info.hours <= 4) 495 weather_info.sunlight = SUN_DARK; 496 else if (time_info.hours == 5) 497 weather_info.sunlight = SUN_RISE; 498 else if (time_info.hours <= 20) 499 weather_info.sunlight = SUN_LIGHT; 500 else if (time_info.hours == 21) 501 weather_info.sunlight = SUN_SET; 502 else 503 weather_info.sunlight = SUN_DARK; 504 505 log(" Current Gametime: %dH %dD %dM %dY.", time_info.hours, 506 time_info.day, time_info.month, time_info.year); 507 508 weather_info.pressure = 960; 509 if ((time_info.month >= 7) && (time_info.month <= 12)) 510 weather_info.pressure += dice(1, 50); 511 else 512 weather_info.pressure += dice(1, 80); 513 514 weather_info.change = 0; 515 516 if (weather_info.pressure <= 980) 517 weather_info.sky = SKY_LIGHTNING; 518 else if (weather_info.pressure <= 1000) 519 weather_info.sky = SKY_RAINING; 520 else if (weather_info.pressure <= 1020) 521 weather_info.sky = SKY_CLOUDY; 522 else 523 weather_info.sky = SKY_CLOUDLESS; 524 } 525 526 527 /* Write the time in 'when' to the MUD-time file. */ 528 void save_mud_time(struct time_info_data *when) 529 { 530 FILE *bgtime; 531 532 if ((bgtime = fopen(TIME_FILE, "w")) == NULL) 533 log("SYSERR: Can't write to '%s' time file.", TIME_FILE); 534 else { 535 fprintf(bgtime, "%ld\n", mud_time_to_secs(when)); 536 fclose(bgtime); 537 } 538 } 539 540 541 void free_player_index(void) 542 { 543 int tp; 544 545 if (!player_table) 546 return; 547 548 for (tp = 0; tp <= top_of_p_table; tp++) 549 if (player_table[tp].name) 550 free(player_table[tp].name); 551 552 free(player_table); 553 player_table = NULL; 554 top_of_p_table = 0; 555 } 556 557 558 /* generate index table for the player file */ 559 void build_player_index(void) 560 { 561 int nr = -1, i; 562 long size, recs; 563 struct char_file_u dummy; 564 565 if (!(player_fl = fopen(PLAYER_FILE, "r+b"))) { 566 if (errno != ENOENT) { 567 perror("SYSERR: fatal error opening playerfile"); 568 exit(1); 569 } else { 570 log("No playerfile. Creating a new one."); 571 touch(PLAYER_FILE); 572 if (!(player_fl = fopen(PLAYER_FILE, "r+b"))) { 573 perror("SYSERR: fatal error opening playerfile"); 574 exit(1); 575 } 576 } 577 } 578 579 fseek(player_fl, 0L, SEEK_END); 580 size = ftell(player_fl); 581 rewind(player_fl); 582 if (size % sizeof(struct char_file_u)) 583 log("\aWARNING: PLAYERFILE IS PROBABLY CORRUPT!"); 584 recs = size / sizeof(struct char_file_u); 585 if (recs) { 586 log(" %ld players in database.", recs); 587 CREATE(player_table, struct player_index_element, recs); 588 } else { 589 player_table = NULL; 590 top_of_p_table = -1; 591 return; 592 } 593 594 for (;;) { 595 fread(&dummy, sizeof(struct char_file_u), 1, player_fl); 596 if (feof(player_fl)) 597 break; 598 599 /* new record */ 600 nr++; 601 CREATE(player_table[nr].name, char, strlen(dummy.name) + 1); 602 for (i = 0; (*(player_table[nr].name + i) = LOWER(*(dummy.name + i))); i++) 603 ; 604 player_table[nr].id = dummy.char_specials_saved.idnum; 605 top_idnum = MAX(top_idnum, dummy.char_specials_saved.idnum); 606 } 607 608 top_of_p_table = nr; 609 } 610 611 /* 612 * Thanks to Andrey (andrey@alex-ua.com) for this bit of code, although I 613 * did add the 'goto' and changed some "while()" into "do { } while()". 614 * -gg 6/24/98 (technically 6/25/98, but I care not.) 615 */ 616 int count_alias_records(FILE *fl) 617 { 618 char key[READ_SIZE], next_key[READ_SIZE]; 619 char line[READ_SIZE], *scan; 620 int total_keywords = 0; 621 622 /* get the first keyword line */ 623 get_one_line(fl, key); 624 625 while (*key != '$') { 626 /* skip the text */ 627 do { 628 get_one_line(fl, line); 629 if (feof(fl)) 630 goto ackeof; 631 } while (*line != '#'); 632 633 /* now count keywords */ 634 scan = key; 635 do { 636 scan = one_word(scan, next_key); 637 if (*next_key) 638 ++total_keywords; 639 } while (*next_key); 640 641 /* get next keyword line (or $) */ 642 get_one_line(fl, key); 643 644 if (feof(fl)) 645 goto ackeof; 646 } 647 648 return (total_keywords); 649 650 /* No, they are not evil. -gg 6/24/98 */ 651 ackeof: 652 log("SYSERR: Unexpected end of help file."); 653 exit(1); /* Some day we hope to handle these things better... */ 654 } 655 656 /* function to count how many hash-mark delimited records exist in a file */ 657 int count_hash_records(FILE *fl) 658 { 659 char buf[128]; 660 int count = 0; 661 662 while (fgets(buf, 128, fl)) 663 if (*buf == '#') 664 count++; 665 666 return (count); 667 } 668 669 670 671 void index_boot(int mode) 672 { 673 const char *index_filename, *prefix = NULL; /* NULL or egcs 1.1 complains */ 674 FILE *db_index, *db_file; 675 int rec_count = 0, size[2]; 676 char buf2[PATH_MAX], buf1[MAX_STRING_LENGTH]; 677 678 switch (mode) { 679 case DB_BOOT_WLD: 680 prefix = WLD_PREFIX; 681 break; 682 case DB_BOOT_MOB: 683 prefix = MOB_PREFIX; 684 break; 685 case DB_BOOT_OBJ: 686 prefix = OBJ_PREFIX; 687 break; 688 case DB_BOOT_ZON: 689 prefix = ZON_PREFIX; 690 break; 691 case DB_BOOT_SHP: 692 prefix = SHP_PREFIX; 693 break; 694 case DB_BOOT_HLP: 695 prefix = HLP_PREFIX; 696 break; 697 default: 698 log("SYSERR: Unknown subcommand %d to index_boot!", mode); 699 exit(1); 700 } 701 702 if (mini_mud) 703 index_filename = MINDEX_FILE; 704 else 705 index_filename = INDEX_FILE; 706 707 snprintf(buf2, sizeof(buf2), "%s%s", prefix, index_filename); 708 if (!(db_index = fopen(buf2, "r"))) { 709 log("SYSERR: opening index file '%s': %s", buf2, strerror(errno)); 710 exit(1); 711 } 712 713 /* first, count the number of records in the file so we can malloc */ 714 fscanf(db_index, "%s\n", buf1); 715 while (*buf1 != '$') { 716 snprintf(buf2, sizeof(buf2), "%s%s", prefix, buf1); 717 if (!(db_file = fopen(buf2, "r"))) { 718 log("SYSERR: File '%s' listed in '%s/%s': %s", buf2, prefix, 719 index_filename, strerror(errno)); 720 fscanf(db_index, "%s\n", buf1); 721 continue; 722 } else { 723 if (mode == DB_BOOT_ZON) 724 rec_count++; 725 else if (mode == DB_BOOT_HLP) 726 rec_count += count_alias_records(db_file); 727 else 728 rec_count += count_hash_records(db_file); 729 } 730 731 fclose(db_file); 732 fscanf(db_index, "%s\n", buf1); 733 } 734 735 /* Exit if 0 records, unless this is shops */ 736 if (!rec_count) { 737 if (mode == DB_BOOT_SHP) 738 return; 739 log("SYSERR: boot error - 0 records counted in %s/%s.", prefix, 740 index_filename); 741 exit(1); 742 } 743 744 /* 745 * NOTE: "bytes" does _not_ include strings or other later malloc'd things. 746 */ 747 switch (mode) { 748 case DB_BOOT_WLD: 749 CREATE(world, struct room_data, rec_count); 750 size[0] = sizeof(struct room_data) * rec_count; 751 log(" %d rooms, %d bytes.", rec_count, size[0]); 752 break; 753 case DB_BOOT_MOB: 754 CREATE(mob_proto, struct char_data, rec_count); 755 CREATE(mob_index, struct index_data, rec_count); 756 size[0] = sizeof(struct index_data) * rec_count; 757 size[1] = sizeof(struct char_data) * rec_count; 758 log(" %d mobs, %d bytes in index, %d bytes in prototypes.", rec_count, size[0], size[1]); 759 break; 760 case DB_BOOT_OBJ: 761 CREATE(obj_proto, struct obj_data, rec_count); 762 CREATE(obj_index, struct index_data, rec_count); 763 size[0] = sizeof(struct index_data) * rec_count; 764 size[1] = sizeof(struct obj_data) * rec_count; 765 log(" %d objs, %d bytes in index, %d bytes in prototypes.", rec_count, size[0], size[1]); 766 break; 767 case DB_BOOT_ZON: 768 CREATE(zone_table, struct zone_data, rec_count); 769 size[0] = sizeof(struct zone_data) * rec_count; 770 log(" %d zones, %d bytes.", rec_count, size[0]); 771 break; 772 case DB_BOOT_HLP: 773 CREATE(help_table, struct help_index_element, rec_count); 774 size[0] = sizeof(struct help_index_element) * rec_count; 775 log(" %d entries, %d bytes.", rec_count, size[0]); 776 break; 777 } 778 779 rewind(db_index); 780 fscanf(db_index, "%s\n", buf1); 781 while (*buf1 != '$') { 782 snprintf(buf2, sizeof(buf2), "%s%s", prefix, buf1); 783 if (!(db_file = fopen(buf2, "r"))) { 784 log("SYSERR: %s: %s", buf2, strerror(errno)); 785 exit(1); 786 } 787 switch (mode) { 788 case DB_BOOT_WLD: 789 case DB_BOOT_OBJ: 790 case DB_BOOT_MOB: 791 discrete_load(db_file, mode, buf2); 792 break; 793 case DB_BOOT_ZON: 794 load_zones(db_file, buf2); 795 break; 796 case DB_BOOT_HLP: 797 /* 798 * If you think about it, we have a race here. Although, this is the 799 * "point-the-gun-at-your-own-foot" type of race. 800 */ 801 load_help(db_file); 802 break; 803 case DB_BOOT_SHP: 804 boot_the_shops(db_file, buf2, rec_count); 805 break; 806 } 807 808 fclose(db_file); 809 fscanf(db_index, "%s\n", buf1); 810 } 811 fclose(db_index); 812 813 /* sort the help index */ 814 if (mode == DB_BOOT_HLP) { 815 qsort(help_table, top_of_helpt, sizeof(struct help_index_element), hsort); 816 top_of_helpt--; 817 } 818 } 819 820 821 void discrete_load(FILE *fl, int mode, char *filename) 822 { 823 int nr = -1, last; 824 char line[READ_SIZE]; 825 826 const char *modes[] = {"world", "mob", "obj"}; 827 828 for (;;) { 829 /* 830 * we have to do special processing with the obj files because they have 831 * no end-of-record marker :( 832 */ 833 if (mode != DB_BOOT_OBJ || nr < 0) 834 if (!get_line(fl, line)) { 835 if (nr == -1) { 836 log("SYSERR: %s file %s is empty!", modes[mode], filename); 837 } else { 838 log("SYSERR: Format error in %s after %s #%d\n" 839 "...expecting a new %s, but file ended!\n" 840 "(maybe the file is not terminated with '$'?)", filename, 841 modes[mode], nr, modes[mode]); 842 } 843 exit(1); 844 } 845 if (*line == '$') 846 return; 847 848 if (*line == '#') { 849 last = nr; 850 if (sscanf(line, "#%d", &nr) != 1) { 851 log("SYSERR: Format error after %s #%d", modes[mode], last); 852 exit(1); 853 } 854 if (nr >= 99999) 855 return; 856 else 857 switch (mode) { 858 case DB_BOOT_WLD: 859 parse_room(fl, nr); 860 break; 861 case DB_BOOT_MOB: 862 parse_mobile(fl, nr); 863 break; 864 case DB_BOOT_OBJ: 865 strlcpy(line, parse_object(fl, nr), sizeof(line)); 866 break; 867 } 868 } else { 869 log("SYSERR: Format error in %s file %s near %s #%d", modes[mode], 870 filename, modes[mode], nr); 871 log("SYSERR: ... offending line: '%s'", line); 872 exit(1); 873 } 874 } 875 } 876 877 bitvector_t asciiflag_conv(char *flag) 878 { 879 bitvector_t flags = 0; 880 int is_num = TRUE; 881 char *p; 882 883 for (p = flag; *p; p++) { 884 if (islower(*p)) 885 flags |= 1 << (*p - 'a'); 886 else if (isupper(*p)) 887 flags |= 1 << (26 + (*p - 'A')); 888 889 if (!isdigit(*p)) 890 is_num = FALSE; 891 } 892 893 if (is_num) 894 flags = atol(flag); 895 896 return (flags); 897 } 898 899 900 /* load the rooms */ 901 void parse_room(FILE *fl, int virtual_nr) 902 { 903 static int room_nr = 0, zone = 0; 904 int t[10], i; 905 char line[READ_SIZE], flags[128], buf2[MAX_STRING_LENGTH], buf[128]; 906 struct extra_descr_data *new_descr; 907 908 /* This really had better fit or there are other problems. */ 909 snprintf(buf2, sizeof(buf2), "room #%d", virtual_nr); 910 911 if (virtual_nr < zone_table[zone].bot) { 912 log("SYSERR: Room #%d is below zone %d.", virtual_nr, zone); 913 exit(1); 914 } 915 while (virtual_nr > zone_table[zone].top) 916 if (++zone > top_of_zone_table) { 917 log("SYSERR: Room %d is outside of any zone.", virtual_nr); 918 exit(1); 919 } 920 world[room_nr].zone = zone; 921 world[room_nr].number = virtual_nr; 922 world[room_nr].name = fread_string(fl, buf2); 923 world[room_nr].description = fread_string(fl, buf2); 924 925 if (!get_line(fl, line)) { 926 log("SYSERR: Expecting roomflags/sector type of room #%d but file ended!", 927 virtual_nr); 928 exit(1); 929 } 930 931 if (sscanf(line, " %d %s %d ", t, flags, t + 2) != 3) { 932 log("SYSERR: Format error in roomflags/sector type of room #%d", 933 virtual_nr); 934 exit(1); 935 } 936 /* t[0] is the zone number; ignored with the zone-file system */ 937 938 world[room_nr].room_flags = asciiflag_conv(flags); 939 sprintf(flags, "object #%d", virtual_nr); /* sprintf: OK (until 399-bit integers) */ 940 check_bitvector_names(world[room_nr].room_flags, room_bits_count, flags, "room"); 941 942 world[room_nr].sector_type = t[2]; 943 944 world[room_nr].func = NULL; 945 world[room_nr].contents = NULL; 946 world[room_nr].people = NULL; 947 world[room_nr].light = 0; /* Zero light sources */ 948 949 for (i = 0; i < NUM_OF_DIRS; i++) 950 world[room_nr].dir_option[i] = NULL; 951 952 world[room_nr].ex_description = NULL; 953 954 snprintf(buf, sizeof(buf), "SYSERR: Format error in room #%d (expecting D/E/S)", virtual_nr); 955 956 for (;;) { 957 if (!get_line(fl, line)) { 958 log("%s", buf); 959 exit(1); 960 } 961 switch (*line) { 962 case 'D': 963 setup_dir(fl, room_nr, atoi(line + 1)); 964 break; 965 case 'E': 966 CREATE(new_descr, struct extra_descr_data, 1); 967 new_descr->keyword = fread_string(fl, buf2); 968 new_descr->description = fread_string(fl, buf2); 969 new_descr->next = world[room_nr].ex_description; 970 world[room_nr].ex_description = new_descr; 971 break; 972 case 'S': /* end of room */ 973 top_of_world = room_nr++; 974 return; 975 default: 976 log("%s", buf); 977 exit(1); 978 } 979 } 980 } 981 982 983 984 /* read direction data */ 985 void setup_dir(FILE *fl, int room, int dir) 986 { 987 int t[5]; 988 char line[READ_SIZE], buf2[128]; 989 990 snprintf(buf2, sizeof(buf2), "room #%d, direction D%d", GET_ROOM_VNUM(room), dir); 991 992 CREATE(world[room].dir_option[dir], struct room_direction_data, 1); 993 world[room].dir_option[dir]->general_description = fread_string(fl, buf2); 994 world[room].dir_option[dir]->keyword = fread_string(fl, buf2); 995 996 if (!get_line(fl, line)) { 997 log("SYSERR: Format error, %s", buf2); 998 exit(1); 999 } 1000 if (sscanf(line, " %d %d %d ", t, t + 1, t + 2) != 3) { 1001 log("SYSERR: Format error, %s", buf2); 1002 exit(1); 1003 } 1004 if (t[0] == 1) 1005 world[room].dir_option[dir]->exit_info = EX_ISDOOR; 1006 else if (t[0] == 2) 1007 world[room].dir_option[dir]->exit_info = EX_ISDOOR | EX_PICKPROOF; 1008 else 1009 world[room].dir_option[dir]->exit_info = 0; 1010 1011 world[room].dir_option[dir]->key = t[1]; 1012 world[room].dir_option[dir]->to_room = t[2]; 1013 } 1014 1015 1016 /* make sure the start rooms exist & resolve their vnums to rnums */ 1017 void check_start_rooms(void) 1018 { 1019 if ((r_mortal_start_room = real_room(mortal_start_room)) == NOWHERE) { 1020 log("SYSERR: Mortal start room does not exist. Change in config.c."); 1021 exit(1); 1022 } 1023 if ((r_immort_start_room = real_room(immort_start_room)) == NOWHERE) { 1024 if (!mini_mud) 1025 log("SYSERR: Warning: Immort start room does not exist. Change in config.c."); 1026 r_immort_start_room = r_mortal_start_room; 1027 } 1028 if ((r_frozen_start_room = real_room(frozen_start_room)) == NOWHERE) { 1029 if (!mini_mud) 1030 log("SYSERR: Warning: Frozen start room does not exist. Change in config.c."); 1031 r_frozen_start_room = r_mortal_start_room; 1032 } 1033 } 1034 1035 1036 /* resolve all vnums into rnums in the world */ 1037 void renum_world(void) 1038 { 1039 int room, door; 1040 1041 for (room = 0; room <= top_of_world; room++) 1042 for (door = 0; door < NUM_OF_DIRS; door++) 1043 if (world[room].dir_option[door]) 1044 if (world[room].dir_option[door]->to_room != NOWHERE) 1045 world[room].dir_option[door]->to_room = 1046 real_room(world[room].dir_option[door]->to_room); 1047 } 1048 1049 1050 #define ZCMD zone_table[zone].cmd[cmd_no] 1051 1052 /* 1053 * "resulve vnums into rnums in the zone reset tables" 1054 * 1055 * Or in English: Once all of the zone reset tables have been loaded, we 1056 * resolve the virtual numbers into real numbers all at once so we don't have 1057 * to do it repeatedly while the game is running. This does make adding any 1058 * room, mobile, or object a little more difficult while the game is running. 1059 * 1060 * NOTE 1: Assumes NOWHERE == NOBODY == NOTHING. 1061 * NOTE 2: Assumes sizeof(room_rnum) >= (sizeof(mob_rnum) and sizeof(obj_rnum)) 1062 */ 1063 void renum_zone_table(void) 1064 { 1065 int cmd_no; 1066 room_rnum a, b, c, olda, oldb, oldc; 1067 zone_rnum zone; 1068 char buf[128]; 1069 1070 for (zone = 0; zone <= top_of_zone_table; zone++) 1071 for (cmd_no = 0; ZCMD.command != 'S'; cmd_no++) { 1072 a = b = c = 0; 1073 olda = ZCMD.arg1; 1074 oldb = ZCMD.arg2; 1075 oldc = ZCMD.arg3; 1076 switch (ZCMD.command) { 1077 case 'M': 1078 a = ZCMD.arg1 = real_mobile(ZCMD.arg1); 1079 c = ZCMD.arg3 = real_room(ZCMD.arg3); 1080 break; 1081 case 'O': 1082 a = ZCMD.arg1 = real_object(ZCMD.arg1); 1083 if (ZCMD.arg3 != NOWHERE) 1084 c = ZCMD.arg3 = real_room(ZCMD.arg3); 1085 break; 1086 case 'G': 1087 a = ZCMD.arg1 = real_object(ZCMD.arg1); 1088 break; 1089 case 'E': 1090 a = ZCMD.arg1 = real_object(ZCMD.arg1); 1091 break; 1092 case 'P': 1093 a = ZCMD.arg1 = real_object(ZCMD.arg1); 1094 c = ZCMD.arg3 = real_object(ZCMD.arg3); 1095 break; 1096 case 'D': 1097 a = ZCMD.arg1 = real_room(ZCMD.arg1); 1098 break; 1099 case 'R': /* rem obj from room */ 1100 a = ZCMD.arg1 = real_room(ZCMD.arg1); 1101 b = ZCMD.arg2 = real_object(ZCMD.arg2); 1102 break; 1103 } 1104 if (a == NOWHERE || b == NOWHERE || c == NOWHERE) { 1105 if (!mini_mud) { 1106 snprintf(buf, sizeof(buf), "Invalid vnum %d, cmd disabled", 1107 a == NOWHERE ? olda : b == NOWHERE ? oldb : oldc); 1108 log_zone_error(zone, cmd_no, buf); 1109 } 1110 ZCMD.command = '*'; 1111 } 1112 } 1113 } 1114 1115 1116 1117 void parse_simple_mob(FILE *mob_f, int i, int nr) 1118 { 1119 int j, t[10]; 1120 char line[READ_SIZE]; 1121 1122 mob_proto[i].real_abils.str = 11; 1123 mob_proto[i].real_abils.intel = 11; 1124 mob_proto[i].real_abils.wis = 11; 1125 mob_proto[i].real_abils.dex = 11; 1126 mob_proto[i].real_abils.con = 11; 1127 mob_proto[i].real_abils.cha = 11; 1128 1129 if (!get_line(mob_f, line)) { 1130 log("SYSERR: Format error in mob #%d, file ended after S flag!", nr); 1131 exit(1); 1132 } 1133 1134 if (sscanf(line, " %d %d %d %dd%d+%d %dd%d+%d ", 1135 t, t + 1, t + 2, t + 3, t + 4, t + 5, t + 6, t + 7, t + 8) != 9) { 1136 log("SYSERR: Format error in mob #%d, first line after S flag\n" 1137 "...expecting line of form '# # # #d#+# #d#+#'", nr); 1138 exit(1); 1139 } 1140 1141 GET_LEVEL(mob_proto + i) = t[0]; 1142 GET_HITROLL(mob_proto + i) = 20 - t[1]; 1143 GET_AC(mob_proto + i) = 10 * t[2]; 1144 1145 /* max hit = 0 is a flag that H, M, V is xdy+z */ 1146 GET_MAX_HIT(mob_proto + i) = 0; 1147 GET_HIT(mob_proto + i) = t[3]; 1148 GET_MANA(mob_proto + i) = t[4]; 1149 GET_MOVE(mob_proto + i) = t[5]; 1150 1151 GET_MAX_MANA(mob_proto + i) = 10; 1152 GET_MAX_MOVE(mob_proto + i) = 50; 1153 1154 mob_proto[i].mob_specials.damnodice = t[6]; 1155 mob_proto[i].mob_specials.damsizedice = t[7]; 1156 GET_DAMROLL(mob_proto + i) = t[8]; 1157 1158 if (!get_line(mob_f, line)) { 1159 log("SYSERR: Format error in mob #%d, second line after S flag\n" 1160 "...expecting line of form '# #', but file ended!", nr); 1161 exit(1); 1162 } 1163 1164 if (sscanf(line, " %d %d ", t, t + 1) != 2) { 1165 log("SYSERR: Format error in mob #%d, second line after S flag\n" 1166 "...expecting line of form '# #'", nr); 1167 exit(1); 1168 } 1169 1170 GET_GOLD(mob_proto + i) = t[0]; 1171 GET_EXP(mob_proto + i) = t[1]; 1172 1173 if (!get_line(mob_f, line)) { 1174 log("SYSERR: Format error in last line of mob #%d\n" 1175 "...expecting line of form '# # #', but file ended!", nr); 1176 exit(1); 1177 } 1178 1179 if (sscanf(line, " %d %d %d ", t, t + 1, t + 2) != 3) { 1180 log("SYSERR: Format error in last line of mob #%d\n" 1181 "...expecting line of form '# # #'", nr); 1182 exit(1); 1183 } 1184 1185 GET_POS(mob_proto + i) = t[0]; 1186 GET_DEFAULT_POS(mob_proto + i) = t[1]; 1187 GET_SEX(mob_proto + i) = t[2]; 1188 1189 GET_CLASS(mob_proto + i) = 0; 1190 GET_WEIGHT(mob_proto + i) = 200; 1191 GET_HEIGHT(mob_proto + i) = 198; 1192 1193 /* 1194 * these are now save applies; base save numbers for MOBs are now from 1195 * the warrior save table. 1196 */ 1197 for (j = 0; j < 5; j++) 1198 GET_SAVE(mob_proto + i, j) = 0; 1199 } 1200 1201 1202 /* 1203 * interpret_espec is the function that takes espec keywords and values 1204 * and assigns the correct value to the mob as appropriate. Adding new 1205 * e-specs is absurdly easy -- just add a new CASE statement to this 1206 * function! No other changes need to be made anywhere in the code. 1207 * 1208 * CASE : Requires a parameter through 'value'. 1209 * BOOL_CASE : Being specified at all is its value. 1210 */ 1211 1212 #define CASE(test) \ 1213 if (value && !matched && !str_cmp(keyword, test) && (matched = TRUE)) 1214 1215 #define BOOL_CASE(test) \ 1216 if (!value && !matched && !str_cmp(keyword, test) && (matched = TRUE)) 1217 1218 #define RANGE(low, high) \ 1219 (num_arg = MAX((low), MIN((high), (num_arg)))) 1220 1221 void interpret_espec(const char *keyword, const char *value, int i, int nr) 1222 { 1223 int num_arg = 0, matched = FALSE; 1224 1225 /* 1226 * If there isn't a colon, there is no value. While Boolean options are 1227 * possible, we don't actually have any. Feel free to make some. 1228 */ 1229 if (value) 1230 num_arg = atoi(value); 1231 1232 CASE("BareHandAttack") { 1233 RANGE(0, 99); 1234 mob_proto[i].mob_specials.attack_type = num_arg; 1235 } 1236 1237 CASE("Str") { 1238 RANGE(3, 25); 1239 mob_proto[i].real_abils.str = num_arg; 1240 } 1241 1242 CASE("StrAdd") { 1243 RANGE(0, 100); 1244 mob_proto[i].real_abils.str_add = num_arg; 1245 } 1246 1247 CASE("Int") { 1248 RANGE(3, 25); 1249 mob_proto[i].real_abils.intel = num_arg; 1250 } 1251 1252 CASE("Wis") { 1253 RANGE(3, 25); 1254 mob_proto[i].real_abils.wis = num_arg; 1255 } 1256 1257 CASE("Dex") { 1258 RANGE(3, 25); 1259 mob_proto[i].real_abils.dex = num_arg; 1260 } 1261 1262 CASE("Con") { 1263 RANGE(3, 25); 1264 mob_proto[i].real_abils.con = num_arg; 1265 } 1266 1267 CASE("Cha") { 1268 RANGE(3, 25); 1269 mob_proto[i].real_abils.cha = num_arg; 1270 } 1271 1272 if (!matched) { 1273 log("SYSERR: Warning: unrecognized espec keyword %s in mob #%d", 1274 keyword, nr); 1275 } 1276 } 1277 1278 #undef CASE 1279 #undef BOOL_CASE 1280 #undef RANGE 1281 1282 void parse_espec(char *buf, int i, int nr) 1283 { 1284 char *ptr; 1285 1286 if ((ptr = strchr(buf, ':')) != NULL) { 1287 *(ptr++) = '\0'; 1288 while (isspace(*ptr)) 1289 ptr++; 1290 } 1291 interpret_espec(buf, ptr, i, nr); 1292 } 1293 1294 1295 void parse_enhanced_mob(FILE *mob_f, int i, int nr) 1296 { 1297 char line[READ_SIZE]; 1298 1299 parse_simple_mob(mob_f, i, nr); 1300 1301 while (get_line(mob_f, line)) { 1302 if (!strcmp(line, "E")) /* end of the enhanced section */ 1303 return; 1304 else if (*line == '#') { /* we've hit the next mob, maybe? */ 1305 log("SYSERR: Unterminated E section in mob #%d", nr); 1306 exit(1); 1307 } else 1308 parse_espec(line, i, nr); 1309 } 1310 1311 log("SYSERR: Unexpected end of file reached after mob #%d", nr); 1312 exit(1); 1313 } 1314 1315 1316 void parse_mobile(FILE *mob_f, int nr) 1317 { 1318 static int i = 0; 1319 int j, t[10]; 1320 char line[READ_SIZE], *tmpptr, letter; 1321 char f1[128], f2[128], buf2[128]; 1322 1323 mob_index[i].vnum = nr; 1324 mob_index[i].number = 0; 1325 mob_index[i].func = NULL; 1326 1327 clear_char(mob_proto + i); 1328 1329 /* 1330 * Mobiles should NEVER use anything in the 'player_specials' structure. 1331 * The only reason we have every mob in the game share this copy of the 1332 * structure is to save newbie coders from themselves. -gg 2/25/98 1333 */ 1334 mob_proto[i].player_specials = &dummy_mob; 1335 sprintf(buf2, "mob vnum %d", nr); /* sprintf: OK (for 'buf2 >= 19') */ 1336 1337 /***** String data *****/ 1338 mob_proto[i].player.name = fread_string(mob_f, buf2); 1339 tmpptr = mob_proto[i].player.short_descr = fread_string(mob_f, buf2); 1340 if (tmpptr && *tmpptr) 1341 if (!str_cmp(fname(tmpptr), "a") || !str_cmp(fname(tmpptr), "an") || 1342 !str_cmp(fname(tmpptr), "the")) 1343 *tmpptr = LOWER(*tmpptr); 1344 mob_proto[i].player.long_descr = fread_string(mob_f, buf2); 1345 mob_proto[i].player.description = fread_string(mob_f, buf2); 1346 GET_TITLE(mob_proto + i) = NULL; 1347 1348 /* *** Numeric data *** */ 1349 if (!get_line(mob_f, line)) { 1350 log("SYSERR: Format error after string section of mob #%d\n" 1351 "...expecting line of form '# # # {S | E}', but file ended!", nr); 1352 exit(1); 1353 } 1354 1355 #ifdef CIRCLE_ACORN /* Ugh. */ 1356 if (sscanf(line, "%s %s %d %s", f1, f2, t + 2, &letter) != 4) { 1357 #else 1358 if (sscanf(line, "%s %s %d %c", f1, f2, t + 2, &letter) != 4) { 1359 #endif 1360 log("SYSERR: Format error after string section of mob #%d\n" 1361 "...expecting line of form '# # # {S | E}'", nr); 1362 exit(1); 1363 } 1364 1365 MOB_FLAGS(mob_proto + i) = asciiflag_conv(f1); 1366 SET_BIT(MOB_FLAGS(mob_proto + i), MOB_ISNPC); 1367 if (MOB_FLAGGED(mob_proto + i, MOB_NOTDEADYET)) { 1368 /* Rather bad to load mobiles with this bit already set. */ 1369 log("SYSERR: Mob #%d has reserved bit MOB_NOTDEADYET set.", nr); 1370 REMOVE_BIT(MOB_FLAGS(mob_proto + i), MOB_NOTDEADYET); 1371 } 1372 check_bitvector_names(MOB_FLAGS(mob_proto + i), action_bits_count, buf2, "mobile"); 1373 1374 AFF_FLAGS(mob_proto + i) = asciiflag_conv(f2); 1375 check_bitvector_names(AFF_FLAGS(mob_proto + i), affected_bits_count, buf2, "mobile affect"); 1376 1377 GET_ALIGNMENT(mob_proto + i) = t[2]; 1378 1379 /* AGGR_TO_ALIGN is ignored if the mob is AGGRESSIVE. */ 1380 if (MOB_FLAGGED(mob_proto + i, MOB_AGGRESSIVE) && MOB_FLAGGED(mob_proto + i, MOB_AGGR_GOOD | MOB_AGGR_EVIL | MOB_AGGR_NEUTRAL)) 1381 log("SYSERR: Mob #%d both Aggressive and Aggressive_to_Alignment.", nr); 1382 1383 switch (UPPER(letter)) { 1384 case 'S': /* Simple monsters */ 1385 parse_simple_mob(mob_f, i, nr); 1386 break; 1387 case 'E': /* Circle3 Enhanced monsters */ 1388 parse_enhanced_mob(mob_f, i, nr); 1389 break; 1390 /* add new mob types here.. */ 1391 default: 1392 log("SYSERR: Unsupported mob type '%c' in mob #%d", letter, nr); 1393 exit(1); 1394 } 1395 1396 mob_proto[i].aff_abils = mob_proto[i].real_abils; 1397 1398 for (j = 0; j < NUM_WEARS; j++) 1399 mob_proto[i].equipment[j] = NULL; 1400 1401 mob_proto[i].nr = i; 1402 mob_proto[i].desc = NULL; 1403 1404 top_of_mobt = i++; 1405 } 1406 1407 1408 1409 1410 /* read all objects from obj file; generate index and prototypes */ 1411 char *parse_object(FILE *obj_f, int nr) 1412 { 1413 static int i = 0; 1414 static char line[READ_SIZE]; 1415 int t[10], j, retval; 1416 char *tmpptr; 1417 char f1[READ_SIZE], f2[READ_SIZE], buf2[128]; 1418 struct extra_descr_data *new_descr; 1419 1420 obj_index[i].vnum = nr; 1421 obj_index[i].number = 0; 1422 obj_index[i].func = NULL; 1423 1424 clear_object(obj_proto + i); 1425 obj_proto[i].item_number = i; 1426 1427 sprintf(buf2, "object #%d", nr); /* sprintf: OK (for 'buf2 >= 19') */ 1428 1429 /* *** string data *** */ 1430 if ((obj_proto[i].name = fread_string(obj_f, buf2)) == NULL) { 1431 log("SYSERR: Null obj name or format error at or near %s", buf2); 1432 exit(1); 1433 } 1434 tmpptr = obj_proto[i].short_description = fread_string(obj_f, buf2); 1435 if (tmpptr && *tmpptr) 1436 if (!str_cmp(fname(tmpptr), "a") || !str_cmp(fname(tmpptr), "an") || 1437 !str_cmp(fname(tmpptr), "the")) 1438 *tmpptr = LOWER(*tmpptr); 1439 1440 tmpptr = obj_proto[i].description = fread_string(obj_f, buf2); 1441 if (tmpptr && *tmpptr) 1442 CAP(tmpptr); 1443 obj_proto[i].action_description = fread_string(obj_f, buf2); 1444 1445 /* *** numeric data *** */ 1446 if (!get_line(obj_f, line)) { 1447 log("SYSERR: Expecting first numeric line of %s, but file ended!", buf2); 1448 exit(1); 1449 } 1450 if ((retval = sscanf(line, " %d %s %s", t, f1, f2)) != 3) { 1451 log("SYSERR: Format error in first numeric line (expecting 3 args, got %d), %s", retval, buf2); 1452 exit(1); 1453 } 1454 1455 /* Object flags checked in check_object(). */ 1456 GET_OBJ_TYPE(obj_proto + i) = t[0]; 1457 GET_OBJ_EXTRA(obj_proto + i) = asciiflag_conv(f1); 1458 GET_OBJ_WEAR(obj_proto + i) = asciiflag_conv(f2); 1459 1460 if (!get_line(obj_f, line)) { 1461 log("SYSERR: Expecting second numeric line of %s, but file ended!", buf2); 1462 exit(1); 1463 } 1464 if ((retval = sscanf(line, "%d %d %d %d", t, t + 1, t + 2, t + 3)) != 4) { 1465 log("SYSERR: Format error in second numeric line (expecting 4 args, got %d), %s", retval, buf2); 1466 exit(1); 1467 } 1468 GET_OBJ_VAL(obj_proto + i, 0) = t[0]; 1469 GET_OBJ_VAL(obj_proto + i, 1) = t[1]; 1470 GET_OBJ_VAL(obj_proto + i, 2) = t[2]; 1471 GET_OBJ_VAL(obj_proto + i, 3) = t[3]; 1472 1473 if (!get_line(obj_f, line)) { 1474 log("SYSERR: Expecting third numeric line of %s, but file ended!", buf2); 1475 exit(1); 1476 } 1477 if ((retval = sscanf(line, "%d %d %d", t, t + 1, t + 2)) != 3) { 1478 log("SYSERR: Format error in third numeric line (expecting 3 args, got %d), %s", retval, buf2); 1479 exit(1); 1480 } 1481 GET_OBJ_WEIGHT(obj_proto + i) = t[0]; 1482 GET_OBJ_COST(obj_proto + i) = t[1]; 1483 GET_OBJ_RENT(obj_proto + i) = t[2]; 1484 1485 /* check to make sure that weight of containers exceeds curr. quantity */ 1486 if (GET_OBJ_TYPE(obj_proto + i) == ITEM_DRINKCON || GET_OBJ_TYPE(obj_proto + i) == ITEM_FOUNTAIN) { 1487 if (GET_OBJ_WEIGHT(obj_proto + i) < GET_OBJ_VAL(obj_proto + i, 1)) 1488 GET_OBJ_WEIGHT(obj_proto + i) = GET_OBJ_VAL(obj_proto + i, 1) + 5; 1489 } 1490 1491 /* *** extra descriptions and affect fields *** */ 1492 1493 for (j = 0; j < MAX_OBJ_AFFECT; j++) { 1494 obj_proto[i].affected[j].location = APPLY_NONE; 1495 obj_proto[i].affected[j].modifier = 0; 1496 } 1497 1498 strcat(buf2, ", after numeric constants\n" /* strcat: OK (for 'buf2 >= 87') */ 1499 "...expecting 'E', 'A', '$', or next object number"); 1500 j = 0; 1501 1502 for (;;) { 1503 if (!get_line(obj_f, line)) { 1504 log("SYSERR: Format error in %s", buf2); 1505 exit(1); 1506 } 1507 switch (*line) { 1508 case 'E': 1509 CREATE(new_descr, struct extra_descr_data, 1); 1510 new_descr->keyword = fread_string(obj_f, buf2); 1511 new_descr->description = fread_string(obj_f, buf2); 1512 new_descr->next = obj_proto[i].ex_description; 1513 obj_proto[i].ex_description = new_descr; 1514 break; 1515 case 'A': 1516 if (j >= MAX_OBJ_AFFECT) { 1517 log("SYSERR: Too many A fields (%d max), %s", MAX_OBJ_AFFECT, buf2); 1518 exit(1); 1519 } 1520 if (!get_line(obj_f, line)) { 1521 log("SYSERR: Format error in 'A' field, %s\n" 1522 "...expecting 2 numeric constants but file ended!", buf2); 1523 exit(1); 1524 } 1525 1526 if ((retval = sscanf(line, " %d %d ", t, t + 1)) != 2) { 1527 log("SYSERR: Format error in 'A' field, %s\n" 1528 "...expecting 2 numeric arguments, got %d\n" 1529 "...offending line: '%s'", buf2, retval, line); 1530 exit(1); 1531 } 1532 obj_proto[i].affected[j].location = t[0]; 1533 obj_proto[i].affected[j].modifier = t[1]; 1534 j++; 1535 break; 1536 case '$': 1537 case '#': 1538 check_object(obj_proto + i); 1539 top_of_objt = i++; 1540 return (line); 1541 default: 1542 log("SYSERR: Format error in (%c): %s", *line, buf2); 1543 exit(1); 1544 } 1545 } 1546 } 1547 1548 1549 #define Z zone_table[zone] 1550 1551 /* load the zone table and command tables */ 1552 void load_zones(FILE *fl, char *zonename) 1553 { 1554 static zone_rnum zone = 0; 1555 int cmd_no, num_of_cmds = 0, line_num = 0, tmp, error; 1556 char *ptr, buf[READ_SIZE], zname[READ_SIZE], buf2[MAX_STRING_LENGTH]; 1557 1558 strlcpy(zname, zonename, sizeof(zname)); 1559 1560 /* Skip first 3 lines lest we mistake the zone name for a command. */ 1561 for (tmp = 0; tmp < 3; tmp++) 1562 get_line(fl, buf); 1563 1564 /* More accurate count. Previous was always 4 or 5 too high. -gg 2001/1/17 1565 * Note that if a new zone command is added to reset_zone(), this string 1566 * will need to be updated to suit. - ae. 1567 */ 1568 while (get_line(fl, buf)) 1569 if ((strchr("MOPGERD", buf[0]) && buf[1] == ' ') || (buf[0] == 'S' && buf[1] == '\0')) 1570 num_of_cmds++; 1571 1572 rewind(fl); 1573 1574 if (num_of_cmds == 0) { 1575 log("SYSERR: %s is empty!", zname); 1576 exit(1); 1577 } else 1578 CREATE(Z.cmd, struct reset_com, num_of_cmds); 1579 1580 line_num += get_line(fl, buf); 1581 1582 if (sscanf(buf, "#%hd", &Z.number) != 1) { 1583 log("SYSERR: Format error in %s, line %d", zname, line_num); 1584 exit(1); 1585 } 1586 snprintf(buf2, sizeof(buf2), "beginning of zone #%d", Z.number); 1587 1588 line_num += get_line(fl, buf); 1589 if ((ptr = strchr(buf, '~')) != NULL) /* take off the '~' if it's there */ 1590 *ptr = '\0'; 1591 Z.name = strdup(buf); 1592 1593 line_num += get_line(fl, buf); 1594 if (sscanf(buf, " %hd %hd %d %d ", &Z.bot, &Z.top, &Z.lifespan, &Z.reset_mode) != 4) { 1595 log("SYSERR: Format error in numeric constant line of %s", zname); 1596 exit(1); 1597 } 1598 if (Z.bot > Z.top) { 1599 log("SYSERR: Zone %d bottom (%d) > top (%d).", Z.number, Z.bot, Z.top); 1600 exit(1); 1601 } 1602 1603 cmd_no = 0; 1604 1605 for (;;) { 1606 if ((tmp = get_line(fl, buf)) == 0) { 1607 log("SYSERR: Format error in %s - premature end of file", zname); 1608 exit(1); 1609 } 1610 line_num += tmp; 1611 ptr = buf; 1612 skip_spaces(&ptr); 1613 1614 if ((ZCMD.command = *ptr) == '*') 1615 continue; 1616 1617 ptr++; 1618 1619 if (ZCMD.command == 'S' || ZCMD.command == '$') { 1620 ZCMD.command = 'S'; 1621 break; 1622 } 1623 error = 0; 1624 if (strchr("MOEPD", ZCMD.command) == NULL) { /* a 3-arg command */ 1625 if (sscanf(ptr, " %d %d %d ", &tmp, &ZCMD.arg1, &ZCMD.arg2) != 3) 1626 error = 1; 1627 } else { 1628 if (sscanf(ptr, " %d %d %d %d ", &tmp, &ZCMD.arg1, &ZCMD.arg2, 1629 &ZCMD.arg3) != 4) 1630 error = 1; 1631 } 1632 1633 ZCMD.if_flag = tmp; 1634 1635 if (error) { 1636 log("SYSERR: Format error in %s, line %d: '%s'", zname, line_num, buf); 1637 exit(1); 1638 } 1639 ZCMD.line = line_num; 1640 cmd_no++; 1641 } 1642 1643 if (num_of_cmds != cmd_no + 1) { 1644 log("SYSERR: Zone command count mismatch for %s. Estimated: %d, Actual: %d", zname, num_of_cmds, cmd_no + 1); 1645 exit(1); 1646 } 1647 1648 top_of_zone_table = zone++; 1649 } 1650 1651 #undef Z 1652 1653 1654 void get_one_line(FILE *fl, char *buf) 1655 { 1656 if (fgets(buf, READ_SIZE, fl) == NULL) { 1657 log("SYSERR: error reading help file: not terminated with $?"); 1658 exit(1); 1659 } 1660 1661 buf[strlen(buf) - 1] = '\0'; /* take off the trailing \n */ 1662 } 1663 1664 1665 void free_help(void) 1666 { 1667 int hp; 1668 1669 if (!help_table) 1670 return; 1671 1672 for (hp = 0; hp <= top_of_helpt; hp++) { 1673 if (help_table[hp].keyword) 1674 free(help_table[hp].keyword); 1675 if (help_table[hp].entry && !help_table[hp].duplicate) 1676 free(help_table[hp].entry); 1677 } 1678 1679 free(help_table); 1680 help_table = NULL; 1681 top_of_helpt = 0; 1682 } 1683 1684 1685 void load_help(FILE *fl) 1686 { 1687 #if defined(CIRCLE_MACINTOSH) 1688 static char key[READ_SIZE + 1], next_key[READ_SIZE + 1], entry[32384]; /* too big for stack? */ 1689 #else 1690 char key[READ_SIZE + 1], next_key[READ_SIZE + 1], entry[32384]; 1691 #endif 1692 size_t entrylen; 1693 char line[READ_SIZE + 1], *scan; 1694 struct help_index_element el; 1695 1696 /* get the first keyword line */ 1697 get_one_line(fl, key); 1698 while (*key != '$') { 1699 strcat(key, "\r\n"); /* strcat: OK (READ_SIZE - "\n" + "\r\n" == READ_SIZE + 1) */ 1700 entrylen = strlcpy(entry, key, sizeof(entry)); 1701 1702 /* read in the corresponding help entry */ 1703 get_one_line(fl, line); 1704 while (*line != '#' && entrylen < sizeof(entry) - 1) { 1705 entrylen += strlcpy(entry + entrylen, line, sizeof(entry) - entrylen); 1706 1707 if (entrylen + 2 < sizeof(entry) - 1) { 1708 strcpy(entry + entrylen, "\r\n"); /* strcpy: OK (size checked above) */ 1709 entrylen += 2; 1710 } 1711 get_one_line(fl, line); 1712 } 1713 1714 if (entrylen >= sizeof(entry) - 1) { 1715 int keysize; 1716 const char *truncmsg = "\r\n*TRUNCATED*\r\n"; 1717 1718 strcpy(entry + sizeof(entry) - strlen(truncmsg) - 1, truncmsg); /* strcpy: OK (assuming sane 'entry' size) */ 1719 1720 keysize = strlen(key) - 2; 1721 log("SYSERR: Help entry exceeded buffer space: %.*s", keysize, key); 1722 1723 /* If we ran out of buffer space, eat the rest of the entry. */ 1724 while (*line != '#') 1725 get_one_line(fl, line); 1726 } 1727 1728 /* now, add the entry to the index with each keyword on the keyword line */ 1729 el.duplicate = 0; 1730 el.entry = strdup(entry); 1731 scan = one_word(key, next_key); 1732 while (*next_key) { 1733 el.keyword = strdup(next_key); 1734 help_table[top_of_helpt++] = el; 1735 el.duplicate++; 1736 scan = one_word(scan, next_key); 1737 } 1738 1739 /* get next keyword line (or $) */ 1740 get_one_line(fl, key); 1741 } 1742 } 1743 1744 1745 int hsort(const void *a, const void *b) 1746 { 1747 const struct help_index_element *a1, *b1; 1748 1749 a1 = (const struct help_index_element *) a; 1750 b1 = (const struct help_index_element *) b; 1751 1752 return (str_cmp(a1->keyword, b1->keyword)); 1753 } 1754 1755 1756 /************************************************************************* 1757 * procedures for resetting, both play-time and boot-time * 1758 *************************************************************************/ 1759 1760 1761 int vnum_mobile(char *searchname, struct char_data *ch) 1762 { 1763 int nr, found = 0; 1764 1765 for (nr = 0; nr <= top_of_mobt; nr++) 1766 if (isname(searchname, mob_proto[nr].player.name)) 1767 send_to_char(ch, "%3d. [%5d] %s\r\n", ++found, mob_index[nr].vnum, mob_proto[nr].player.short_descr); 1768 1769 return (found); 1770 } 1771 1772 1773 1774 int vnum_object(char *searchname, struct char_data *ch) 1775 { 1776 int nr, found = 0; 1777 1778 for (nr = 0; nr <= top_of_objt; nr++) 1779 if (isname(searchname, obj_proto[nr].name)) 1780 send_to_char(ch, "%3d. [%5d] %s\r\n", ++found, obj_index[nr].vnum, obj_proto[nr].short_description); 1781 1782 return (found); 1783 } 1784 1785 1786 /* create a character, and add it to the char list */ 1787 struct char_data *create_char(void) 1788 { 1789 struct char_data *ch; 1790 1791 CREATE(ch, struct char_data, 1); 1792 clear_char(ch); 1793 ch->next = character_list; 1794 character_list = ch; 1795 1796 return (ch); 1797 } 1798 1799 1800 /* create a new mobile from a prototype */ 1801 struct char_data *read_mobile(mob_vnum nr, int type) /* and mob_rnum */ 1802 { 1803 mob_rnum i; 1804 struct char_data *mob; 1805 1806 if (type == VIRTUAL) { 1807 if ((i = real_mobile(nr)) == NOBODY) { 1808 log("WARNING: Mobile vnum %d does not exist in database.", nr); 1809 return (NULL); 1810 } 1811 } else 1812 i = nr; 1813 1814 CREATE(mob, struct char_data, 1); 1815 clear_char(mob); 1816 *mob = mob_proto[i]; 1817 mob->next = character_list; 1818 character_list = mob; 1819 1820 if (!mob->points.max_hit) { 1821 mob->points.max_hit = dice(mob->points.hit, mob->points.mana) + 1822 mob->points.move; 1823 } else 1824 mob->points.max_hit = rand_number(mob->points.hit, mob->points.mana); 1825 1826 mob->points.hit = mob->points.max_hit; 1827 mob->points.mana = mob->points.max_mana; 1828 mob->points.move = mob->points.max_move; 1829 1830 mob->player.time.birth = time(0); 1831 mob->player.time.played = 0; 1832 mob->player.time.logon = time(0); 1833 1834 mob_index[i].number++; 1835 1836 return (mob); 1837 } 1838 1839 1840 /* create an object, and add it to the object list */ 1841 struct obj_data *create_obj(void) 1842 { 1843 struct obj_data *obj; 1844 1845 CREATE(obj, struct obj_data, 1); 1846 clear_object(obj); 1847 obj->next = object_list; 1848 object_list = obj; 1849 1850 return (obj); 1851 } 1852 1853 1854 /* create a new object from a prototype */ 1855 struct obj_data *read_object(obj_vnum nr, int type) /* and obj_rnum */ 1856 { 1857 struct obj_data *obj; 1858 obj_rnum i = type == VIRTUAL ? real_object(nr) : nr; 1859 1860 if (i == NOTHING || i > top_of_objt) { 1861 log("Object (%c) %d does not exist in database.", type == VIRTUAL ? 'V' : 'R', nr); 1862 return (NULL); 1863 } 1864 1865 CREATE(obj, struct obj_data, 1); 1866 clear_object(obj); 1867 *obj = obj_proto[i]; 1868 obj->next = object_list; 1869 object_list = obj; 1870 1871 obj_index[i].number++; 1872 1873 return (obj); 1874 } 1875 1876 1877 1878 #define ZO_DEAD 999 1879 1880 /* update zone ages, queue for reset if necessary, and dequeue when possible */ 1881 void zone_update(void) 1882 { 1883 int i; 1884 struct reset_q_element *update_u, *temp; 1885 static int timer = 0; 1886 1887 /* jelson 10/22/92 */ 1888 if (((++timer * PULSE_ZONE) / PASSES_PER_SEC) >= 60) { 1889 /* one minute has passed */ 1890 /* 1891 * NOT accurate unless PULSE_ZONE is a multiple of PASSES_PER_SEC or a 1892 * factor of 60 1893 */ 1894 1895 timer = 0; 1896 1897 /* since one minute has passed, increment zone ages */ 1898 for (i = 0; i <= top_of_zone_table; i++) { 1899 if (zone_table[i].age < zone_table[i].lifespan && 1900 zone_table[i].reset_mode) 1901 (zone_table[i].age)++; 1902 1903 if (zone_table[i].age >= zone_table[i].lifespan && 1904 zone_table[i].age < ZO_DEAD && zone_table[i].reset_mode) { 1905 /* enqueue zone */ 1906 1907 CREATE(update_u, struct reset_q_element, 1); 1908 1909 update_u->zone_to_reset = i; 1910 update_u->next = 0; 1911 1912 if (!reset_q.head) 1913 reset_q.head = reset_q.tail = update_u; 1914 else { 1915 reset_q.tail->next = update_u; 1916 reset_q.tail = update_u; 1917 } 1918 1919 zone_table[i].age = ZO_DEAD; 1920 } 1921 } 1922 } /* end - one minute has passed */ 1923 1924 1925 /* dequeue zones (if possible) and reset */ 1926 /* this code is executed every 10 seconds (i.e. PULSE_ZONE) */ 1927 for (update_u = reset_q.head; update_u; update_u = update_u->next) 1928 if (zone_table[update_u->zone_to_reset].reset_mode == 2 || 1929 is_empty(update_u->zone_to_reset)) { 1930 reset_zone(update_u->zone_to_reset); 1931 mudlog(CMP, LVL_GOD, FALSE, "Auto zone reset: %s", zone_table[update_u->zone_to_reset].name); 1932 /* dequeue */ 1933 if (update_u == reset_q.head) 1934 reset_q.head = reset_q.head->next; 1935 else { 1936 for (temp = reset_q.head; temp->next != update_u; 1937 temp = temp->next); 1938 1939 if (!update_u->next) 1940 reset_q.tail = temp; 1941 1942 temp->next = update_u->next; 1943 } 1944 1945 free(update_u); 1946 break; 1947 } 1948 } 1949 1950 void log_zone_error(zone_rnum zone, int cmd_no, const char *message) 1951 { 1952 mudlog(NRM, LVL_GOD, TRUE, "SYSERR: zone file: %s", message); 1953 mudlog(NRM, LVL_GOD, TRUE, "SYSERR: ...offending cmd: '%c' cmd in zone #%d, line %d", 1954 ZCMD.command, zone_table[zone].number, ZCMD.line); 1955 } 1956 1957 #define ZONE_ERROR(message) \ 1958 { log_zone_error(zone, cmd_no, message); last_cmd = 0; } 1959 1960 /* execute the reset command table of a given zone */ 1961 void reset_zone(zone_rnum zone) 1962 { 1963 int cmd_no, last_cmd = 0; 1964 struct char_data *mob = NULL; 1965 struct obj_data *obj, *obj_to; 1966 1967 for (cmd_no = 0; ZCMD.command != 'S'; cmd_no++) { 1968 1969 if (ZCMD.if_flag && !last_cmd) 1970 continue; 1971 1972 /* This is the list of actual zone commands. If any new 1973 * zone commands are added to the game, be certain to update 1974 * the list of commands in load_zone() so that the counting 1975 * will still be correct. - ae. 1976 */ 1977 switch (ZCMD.command) { 1978 case '*': /* ignore command */ 1979 last_cmd = 0; 1980 break; 1981 1982 case 'M': /* read a mobile */ 1983 if (mob_index[ZCMD.arg1].number < ZCMD.arg2) { 1984 mob = read_mobile(ZCMD.arg1, REAL); 1985 char_to_room(mob, ZCMD.arg3); 1986 last_cmd = 1; 1987 } else 1988 last_cmd = 0; 1989 break; 1990 1991 case 'O': /* read an object */ 1992 if (obj_index[ZCMD.arg1].number < ZCMD.arg2) { 1993 if (ZCMD.arg3 != NOWHERE) { 1994 obj = read_object(ZCMD.arg1, REAL); 1995 obj_to_room(obj, ZCMD.arg3); 1996 last_cmd = 1; 1997 } else { 1998 obj = read_object(ZCMD.arg1, REAL); 1999 IN_ROOM(obj) = NOWHERE; 2000 last_cmd = 1; 2001 } 2002 } else 2003 last_cmd = 0; 2004 break; 2005 2006 case 'P': /* object to object */ 2007 if (obj_index[ZCMD.arg1].number < ZCMD.arg2) { 2008 obj = read_object(ZCMD.arg1, REAL); 2009 if (!(obj_to = get_obj_num(ZCMD.arg3))) { 2010 ZONE_ERROR("target obj not found, command disabled"); 2011 ZCMD.command = '*'; 2012 break; 2013 } 2014 obj_to_obj(obj, obj_to); 2015 last_cmd = 1; 2016 } else 2017 last_cmd = 0; 2018 break; 2019 2020 case 'G': /* obj_to_char */ 2021 if (!mob) { 2022 ZONE_ERROR("attempt to give obj to non-existant mob, command disabled"); 2023 ZCMD.command = '*'; 2024 break; 2025 } 2026 if (obj_index[ZCMD.arg1].number < ZCMD.arg2) { 2027 obj = read_object(ZCMD.arg1, REAL); 2028 obj_to_char(obj, mob); 2029 last_cmd = 1; 2030 } else 2031 last_cmd = 0; 2032 break; 2033 2034 case 'E': /* object to equipment list */ 2035 if (!mob) { 2036 ZONE_ERROR("trying to equip non-existant mob, command disabled"); 2037 ZCMD.command = '*'; 2038 break; 2039 } 2040 if (obj_index[ZCMD.arg1].number < ZCMD.arg2) { 2041 if (ZCMD.arg3 < 0 || ZCMD.arg3 >= NUM_WEARS) { 2042 ZONE_ERROR("invalid equipment pos number"); 2043 } else { 2044 obj = read_object(ZCMD.arg1, REAL); 2045 equip_char(mob, obj, ZCMD.arg3); 2046 last_cmd = 1; 2047 } 2048 } else 2049 last_cmd = 0; 2050 break; 2051 2052 case 'R': /* rem obj from room */ 2053 if ((obj = get_obj_in_list_num(ZCMD.arg2, world[ZCMD.arg1].contents)) != NULL) 2054 extract_obj(obj); 2055 last_cmd = 1; 2056 break; 2057 2058 2059 case 'D': /* set state of door */ 2060 if (ZCMD.arg2 < 0 || ZCMD.arg2 >= NUM_OF_DIRS || 2061 (world[ZCMD.arg1].dir_option[ZCMD.arg2] == NULL)) { 2062 ZONE_ERROR("door does not exist, command disabled"); 2063 ZCMD.command = '*'; 2064 } else 2065 switch (ZCMD.arg3) { 2066 case 0: 2067 REMOVE_BIT(world[ZCMD.arg1].dir_option[ZCMD.arg2]->exit_info, 2068 EX_LOCKED); 2069 REMOVE_BIT(world[ZCMD.arg1].dir_option[ZCMD.arg2]->exit_info, 2070 EX_CLOSED); 2071 break; 2072 case 1: 2073 SET_BIT(world[ZCMD.arg1].dir_option[ZCMD.arg2]->exit_info, 2074 EX_CLOSED); 2075 REMOVE_BIT(world[ZCMD.arg1].dir_option[ZCMD.arg2]->exit_info, 2076 EX_LOCKED); 2077 break; 2078 case 2: 2079 SET_BIT(world[ZCMD.arg1].dir_option[ZCMD.arg2]->exit_info, 2080 EX_LOCKED); 2081 SET_BIT(world[ZCMD.arg1].dir_option[ZCMD.arg2]->exit_info, 2082 EX_CLOSED); 2083 break; 2084 } 2085 last_cmd = 1; 2086 break; 2087 2088 default: 2089 ZONE_ERROR("unknown cmd in reset table; cmd disabled"); 2090 ZCMD.command = '*'; 2091 break; 2092 } 2093 } 2094 2095 zone_table[zone].age = 0; 2096 } 2097 2098 2099 2100 /* for use in reset_zone; return TRUE if zone 'nr' is free of PC's */ 2101 int is_empty(zone_rnum zone_nr) 2102 { 2103 struct descriptor_data *i; 2104 2105 for (i = descriptor_list; i; i = i->next) { 2106 if (STATE(i) != CON_PLAYING) 2107 continue; 2108 if (IN_ROOM(i->character) == NOWHERE) 2109 continue; 2110 if (GET_LEVEL(i->character) >= LVL_IMMORT) 2111 continue; 2112 if (world[IN_ROOM(i->character)].zone != zone_nr) 2113 continue; 2114 2115 return (0); 2116 } 2117 2118 return (1); 2119 } 2120 2121 2122 2123 2124 2125 /************************************************************************* 2126 * stuff related to the save/load player system * 2127 *************************************************************************/ 2128 2129 2130 long get_ptable_by_name(const char *name) 2131 { 2132 int i; 2133 2134 for (i = 0; i <= top_of_p_table; i++) 2135 if (!str_cmp(player_table[i].name, name)) 2136 return (i); 2137 2138 return (-1); 2139 } 2140 2141 2142 long get_id_by_name(const char *name) 2143 { 2144 int i; 2145 2146 for (i = 0; i <= top_of_p_table; i++) 2147 if (!str_cmp(player_table[i].name, name)) 2148 return (player_table[i].id); 2149 2150 return (-1); 2151 } 2152 2153 2154 char *get_name_by_id(long id) 2155 { 2156 int i; 2157 2158 for (i = 0; i <= top_of_p_table; i++) 2159 if (player_table[i].id == id) 2160 return (player_table[i].name); 2161 2162 return (NULL); 2163 } 2164 2165 2166 /* Load a char, TRUE if loaded, FALSE if not */ 2167 int load_char(const char *name, struct char_file_u *char_element) 2168 { 2169 int player_i; 2170 2171 if ((player_i = get_ptable_by_name(name)) >= 0) { 2172 fseek(player_fl, player_i * sizeof(struct char_file_u), SEEK_SET); 2173 fread(char_element, sizeof(struct char_file_u), 1, player_fl); 2174 return (player_i); 2175 } else 2176 return (-1); 2177 } 2178 2179 2180 2181 2182 /* 2183 * write the vital data of a player to the player file 2184 * 2185 * And that's it! No more fudging around with the load room. 2186 * Unfortunately, 'host' modifying is still here due to lack 2187 * of that variable in the char_data structure. 2188 */ 2189 void save_char(struct char_data *ch) 2190 { 2191 struct char_file_u st; 2192 2193 if (IS_NPC(ch) || !ch->desc || GET_PFILEPOS(ch) < 0) 2194 return; 2195 2196 char_to_store(ch, &st); 2197 2198 strncpy(st.host, ch->desc->host, HOST_LENGTH); /* strncpy: OK (s.host:HOST_LENGTH+1) */ 2199 st.host[HOST_LENGTH] = '\0'; 2200 2201 fseek(player_fl, GET_PFILEPOS(ch) * sizeof(struct char_file_u), SEEK_SET); 2202 fwrite(&st, sizeof(struct char_file_u), 1, player_fl); 2203 } 2204 2205 2206 2207 /* copy data from the file structure to a char struct */ 2208 void store_to_char(struct char_file_u *st, struct char_data *ch) 2209 { 2210 int i; 2211 2212 /* to save memory, only PC's -- not MOB's -- have player_specials */ 2213 if (ch->player_specials == NULL) 2214 CREATE(ch->player_specials, struct player_special_data, 1); 2215 2216 GET_SEX(ch) = st->sex; 2217 GET_CLASS(ch) = st->chclass; 2218 GET_LEVEL(ch) = st->level; 2219 2220 ch->player.short_descr = NULL; 2221 ch->player.long_descr = NULL; 2222 ch->player.title = strdup(st->title); 2223 ch->player.description = strdup(st->description); 2224 2225 ch->player.hometown = st->hometown; 2226 ch->player.time.birth = st->birth; 2227 ch->player.time.played = st->played; 2228 ch->player.time.logon = time(0); 2229 2230 ch->player.weight = st->weight; 2231 ch->player.height = st->height; 2232 2233 ch->real_abils = st->abilities; 2234 ch->aff_abils = st->abilities; 2235 ch->points = st->points; 2236 ch->char_specials.saved = st->char_specials_saved; 2237 ch->player_specials->saved = st->player_specials_saved; 2238 POOFIN(ch) = NULL; 2239 POOFOUT(ch) = NULL; 2240 GET_LAST_TELL(ch) = NOBODY; 2241 2242 if (ch->points.max_mana < 100) 2243 ch->points.max_mana = 100; 2244 2245 ch->char_specials.carry_weight = 0; 2246 ch->char_specials.carry_items = 0; 2247 ch->points.armor = 100; 2248 ch->points.hitroll = 0; 2249 ch->points.damroll = 0; 2250 2251 if (ch->player.name) 2252 free(ch->player.name); 2253 ch->player.name = strdup(st->name); 2254 strlcpy(ch->player.passwd, st->pwd, sizeof(ch->player.passwd)); 2255 2256 /* Add all spell effects */ 2257 for (i = 0; i < MAX_AFFECT; i++) { 2258 if (st->affected[i].type) 2259 affect_to_char(ch, &st->affected[i]); 2260 } 2261 2262 /* 2263 * If you're not poisioned and you've been away for more than an hour of 2264 * real time, we'll set your HMV back to full 2265 */ 2266 2267 if (!AFF_FLAGGED(ch, AFF_POISON) && 2268 time(0) - st->last_logon >= SECS_PER_REAL_HOUR) { 2269 GET_HIT(ch) = GET_MAX_HIT(ch); 2270 GET_MOVE(ch) = GET_MAX_MOVE(ch); 2271 GET_MANA(ch) = GET_MAX_MANA(ch); 2272 } 2273 } /* store_to_char */ 2274 2275 2276 2277 2278 /* copy vital data from a players char-structure to the file structure */ 2279 void char_to_store(struct char_data *ch, struct char_file_u *st) 2280 { 2281 int i; 2282 struct affected_type *af; 2283 struct obj_data *char_eq[NUM_WEARS]; 2284 2285 /* Unaffect everything a character can be affected by */ 2286 2287 for (i = 0; i < NUM_WEARS; i++) { 2288 if (GET_EQ(ch, i)) 2289 char_eq[i] = unequip_char(ch, i); 2290 else 2291 char_eq[i] = NULL; 2292 } 2293 2294 for (af = ch->affected, i = 0; i < MAX_AFFECT; i++) { 2295 if (af) { 2296 st->affected[i] = *af; 2297 st->affected[i].next = 0; 2298 af = af->next; 2299 } else { 2300 st->affected[i].type = 0; /* Zero signifies not used */ 2301 st->affected[i].duration = 0; 2302 st->affected[i].modifier = 0; 2303 st->affected[i].location = 0; 2304 st->affected[i].bitvector = 0; 2305 st->affected[i].next = 0; 2306 } 2307 } 2308 2309 2310 /* 2311 * remove the affections so that the raw values are stored; otherwise the 2312 * effects are doubled when the char logs back in. 2313 */ 2314 2315 while (ch->affected) 2316 affect_remove(ch, ch->affected); 2317 2318 if ((i >= MAX_AFFECT) && af && af->next) 2319 log("SYSERR: WARNING: OUT OF STORE ROOM FOR AFFECTED TYPES!!!"); 2320 2321 ch->aff_abils = ch->real_abils; 2322 2323 st->birth = ch->player.time.birth; 2324 st->played = ch->player.time.played; 2325 st->played += time(0) - ch->player.time.logon; 2326 st->last_logon = time(0); 2327 2328 ch->player.time.played = st->played; 2329 ch->player.time.logon = time(0); 2330 2331 st->hometown = ch->player.hometown; 2332 st->weight = GET_WEIGHT(ch); 2333 st->height = GET_HEIGHT(ch); 2334 st->sex = GET_SEX(ch); 2335 st->chclass = GET_CLASS(ch); 2336 st->level = GET_LEVEL(ch); 2337 st->abilities = ch->real_abils; 2338 st->points = ch->points; 2339 st->char_specials_saved = ch->char_specials.saved; 2340 st->player_specials_saved = ch->player_specials->saved; 2341 2342 st->points.armor = 100; 2343 st->points.hitroll = 0; 2344 st->points.damroll = 0; 2345 2346 if (GET_TITLE(ch)) 2347 strlcpy(st->title, GET_TITLE(ch), MAX_TITLE_LENGTH); 2348 else 2349 *st->title = '\0'; 2350 2351 if (ch->player.description) { 2352 if (strlen(ch->player.description) >= sizeof(st->description)) { 2353 log("SYSERR: char_to_store: %s's description length: %d, max: %d! " 2354 "Truncated.", GET_PC_NAME(ch), strlen(ch->player.description), 2355 sizeof(st->description)); 2356 ch->player.description[sizeof(st->description) - 3] = '\0'; 2357 strcat(ch->player.description, "\r\n"); /* strcat: OK (previous line makes room) */ 2358 } 2359 strcpy(st->description, ch->player.description); /* strcpy: OK (checked above) */ 2360 } else 2361 *st->description = '\0'; 2362 2363 strcpy(st->name, GET_NAME(ch)); /* strcpy: OK (that's what GET_NAME came from) */ 2364 strcpy(st->pwd, GET_PASSWD(ch)); /* strcpy: OK (that's what GET_PASSWD came from) */ 2365 2366 /* add spell and eq affections back in now */ 2367 for (i = 0; i < MAX_AFFECT; i++) { 2368 if (st->affected[i].type) 2369 affect_to_char(ch, &st->affected[i]); 2370 } 2371 2372 for (i = 0; i < NUM_WEARS; i++) { 2373 if (char_eq[i]) 2374 equip_char(ch, char_eq[i], i); 2375 } 2376 /* affect_total(ch); unnecessary, I think !?! */ 2377 } /* Char to store */ 2378 2379 2380 2381 void save_etext(struct char_data *ch) 2382 { 2383 /* this will be really cool soon */ 2384 } 2385 2386 2387 /* 2388 * Create a new entry in the in-memory index table for the player file. 2389 * If the name already exists, by overwriting a deleted character, then 2390 * we re-use the old position. 2391 */ 2392 int create_entry(char *name) 2393 { 2394 int i, pos; 2395 2396 if (top_of_p_table == -1) { /* no table */ 2397 CREATE(player_table, struct player_index_element, 1); 2398 pos = top_of_p_table = 0; 2399 } else if ((pos = get_ptable_by_name(name)) == -1) { /* new name */ 2400 i = ++top_of_p_table + 1; 2401 2402 RECREATE(player_table, struct player_index_element, i); 2403 pos = top_of_p_table; 2404 } 2405 2406 CREATE(player_table[pos].name, char, strlen(name) + 1); 2407 2408 /* copy lowercase equivalent of name to table field */ 2409 for (i = 0; (player_table[pos].name[i] = LOWER(name[i])); i++) 2410 /* Nothing */; 2411 2412 return (pos); 2413 } 2414 2415 2416 2417 /************************************************************************ 2418 * funcs of a (more or less) general utility nature * 2419 ************************************************************************/ 2420 2421 2422 /* read and allocate space for a '~'-terminated string from a given file */ 2423 char *fread_string(FILE *fl, const char *error) 2424 { 2425 char buf[MAX_STRING_LENGTH], tmp[513]; 2426 char *point; 2427 int done = 0, length = 0, templength; 2428 2429 *buf = '\0'; 2430 2431 do { 2432 if (!fgets(tmp, 512, fl)) { 2433 log("SYSERR: fread_string: format error at or near %s", error); 2434 exit(1); 2435 } 2436 /* If there is a '~', end the string; else put an "\r\n" over the '\n'. */ 2437 if ((point = strchr(tmp, '~')) != NULL) { 2438 *point = '\0'; 2439 done = 1; 2440 } else { 2441 point = tmp + strlen(tmp) - 1; 2442 *(point++) = '\r'; 2443 *(point++) = '\n'; 2444 *point = '\0'; 2445 } 2446 2447 templength = strlen(tmp); 2448 2449 if (length + templength >= MAX_STRING_LENGTH) { 2450 log("SYSERR: fread_string: string too large (db.c)"); 2451 log("%s", error); 2452 exit(1); 2453 } else { 2454 strcat(buf + length, tmp); /* strcat: OK (size checked above) */ 2455 length += templength; 2456 } 2457 } while (!done); 2458 2459 /* allocate space for the new string and copy it */ 2460 return (strlen(buf) ? strdup(buf) : NULL); 2461 } 2462 2463 2464 /* release memory allocated for a char struct */ 2465 void free_char(struct char_data *ch) 2466 { 2467 int i; 2468 struct alias_data *a; 2469 2470 if (ch->player_specials != NULL && ch->player_specials != &dummy_mob) { 2471 while ((a = GET_ALIASES(ch)) != NULL) { 2472 GET_ALIASES(ch) = (GET_ALIASES(ch))->next; 2473 free_alias(a); 2474 } 2475 if (ch->player_specials->poofin) 2476 free(ch->player_specials->poofin); 2477 if (ch->player_specials->poofout) 2478 free(ch->player_specials->poofout); 2479 free(ch->player_specials); 2480 if (IS_NPC(ch)) 2481 log("SYSERR: Mob %s (#%d) had player_specials allocated!", GET_NAME(ch), GET_MOB_VNUM(ch)); 2482 } 2483 if (!IS_NPC(ch) || (IS_NPC(ch) && GET_MOB_RNUM(ch) == NOBODY)) { 2484 /* if this is a player, or a non-prototyped non-player, free all */ 2485 if (GET_NAME(ch)) 2486 free(GET_NAME(ch)); 2487 if (ch->player.title) 2488 free(ch->player.title); 2489 if (ch->player.short_descr) 2490 free(ch->player.short_descr); 2491 if (ch->player.long_descr) 2492 free(ch->player.long_descr); 2493 if (ch->player.description) 2494 free(ch->player.description); 2495 } else if ((i = GET_MOB_RNUM(ch)) != NOBODY) { 2496 /* otherwise, free strings only if the string is not pointing at proto */ 2497 if (ch->player.name && ch->player.name != mob_proto[i].player.name) 2498 free(ch->player.name); 2499 if (ch->player.title && ch->player.title != mob_proto[i].player.title) 2500 free(ch->player.title); 2501 if (ch->player.short_descr && ch->player.short_descr != mob_proto[i].player.short_descr) 2502 free(ch->player.short_descr); 2503 if (ch->player.long_descr && ch->player.long_descr != mob_proto[i].player.long_descr) 2504 free(ch->player.long_descr); 2505 if (ch->player.description && ch->player.description != mob_proto[i].player.description) 2506 free(ch->player.description); 2507 } 2508 while (ch->affected) 2509 affect_remove(ch, ch->affected); 2510 2511 if (ch->desc) 2512 ch->desc->character = NULL; 2513 2514 free(ch); 2515 } 2516 2517 2518 2519 2520 /* release memory allocated for an obj struct */ 2521 void free_obj(struct obj_data *obj) 2522 { 2523 int nr; 2524 2525 if ((nr = GET_OBJ_RNUM(obj)) == NOTHING) { 2526 if (obj->name) 2527 free(obj->name); 2528 if (obj->description) 2529 free(obj->description); 2530 if (obj->short_description) 2531 free(obj->short_description); 2532 if (obj->action_description) 2533 free(obj->action_description); 2534 if (obj->ex_description) 2535 free_extra_descriptions(obj->ex_description); 2536 } else { 2537 if (obj->name && obj->name != obj_proto[nr].name) 2538 free(obj->name); 2539 if (obj->description && obj->description != obj_proto[nr].description) 2540 free(obj->description); 2541 if (obj->short_description && obj->short_description != obj_proto[nr].short_description) 2542 free(obj->short_description); 2543 if (obj->action_description && obj->action_description != obj_proto[nr].action_description) 2544 free(obj->action_description); 2545 if (obj->ex_description && obj->ex_description != obj_proto[nr].ex_description) 2546 free_extra_descriptions(obj->ex_description); 2547 } 2548 2549 free(obj); 2550 } 2551 2552 2553 /* 2554 * Steps: 2555 * 1: Read contents of a text file. 2556 * 2: Make sure no one is using the pointer in paging. 2557 * 3: Allocate space. 2558 * 4: Point 'buf' to it. 2559 * 2560 * We don't want to free() the string that someone may be 2561 * viewing in the pager. page_string() keeps the internal 2562 * strdup()'d copy on ->showstr_head and it won't care 2563 * if we delete the original. Otherwise, strings are kept 2564 * on ->showstr_vector but we'll only match if the pointer 2565 * is to the string we're interested in and not a copy. 2566 * 2567 * If someone is reading a global copy we're trying to 2568 * replace, give everybody using it a different copy so 2569 * as to avoid special cases. 2570 */ 2571 int file_to_string_alloc(const char *name, char **buf) 2572 { 2573 int temppage; 2574 char temp[MAX_STRING_LENGTH]; 2575 struct descriptor_data *in_use; 2576 2577 for (in_use = descriptor_list; in_use; in_use = in_use->next) 2578 if (in_use->showstr_vector && *in_use->showstr_vector == *buf) 2579 return (-1); 2580 2581 /* Lets not free() what used to be there unless we succeeded. */ 2582 if (file_to_string(name, temp) < 0) 2583 return (-1); 2584 2585 for (in_use = descriptor_list; in_use; in_use = in_use->next) { 2586 if (!in_use->showstr_count || *in_use->showstr_vector != *buf) 2587 continue; 2588 2589 /* Let's be nice and leave them at the page they were on. */ 2590 temppage = in_use->showstr_page; 2591 paginate_string((in_use->showstr_head = strdup(*in_use->showstr_vector)), in_use); 2592 in_use->showstr_page = temppage; 2593 } 2594 2595 if (*buf) 2596 free(*buf); 2597 2598 *buf = strdup(temp); 2599 return (0); 2600 } 2601 2602 2603 /* read contents of a text file, and place in buf */ 2604 int file_to_string(const char *name, char *buf) 2605 { 2606 FILE *fl; 2607 char tmp[READ_SIZE + 3]; 2608 int len; 2609 2610 *buf = '\0'; 2611 2612 if (!(fl = fopen(name, "r"))) { 2613 log("SYSERR: reading %s: %s", name, strerror(errno)); 2614 return (-1); 2615 } 2616 2617 for (;;) { 2618 if (!fgets(tmp, READ_SIZE, fl)) /* EOF check */ 2619 break; 2620 if ((len = strlen(tmp)) > 0) 2621 tmp[len - 1] = '\0'; /* take off the trailing \n */ 2622 strcat(tmp, "\r\n"); /* strcat: OK (tmp:READ_SIZE+3) */ 2623 2624 if (strlen(buf) + strlen(tmp) + 1 > MAX_STRING_LENGTH) { 2625 log("SYSERR: %s: string too big (%d max)", name, MAX_STRING_LENGTH); 2626 *buf = '\0'; 2627 fclose(fl); 2628 return (-1); 2629 } 2630 strcat(buf, tmp); /* strcat: OK (size checked above) */ 2631 } 2632 2633 fclose(fl); 2634 2635 return (0); 2636 } 2637 2638 2639 2640 /* clear some of the the working variables of a char */ 2641 void reset_char(struct char_data *ch) 2642 { 2643 int i; 2644 2645 for (i = 0; i < NUM_WEARS; i++) 2646 GET_EQ(ch, i) = NULL; 2647 2648 ch->followers = NULL; 2649 ch->master = NULL; 2650 IN_ROOM(ch) = NOWHERE; 2651 ch->carrying = NULL; 2652 ch->next = NULL; 2653 ch->next_fighting = NULL; 2654 ch->next_in_room = NULL; 2655 FIGHTING(ch) = NULL; 2656 ch->char_specials.position = POS_STANDING; 2657 ch->mob_specials.default_pos = POS_STANDING; 2658 ch->char_specials.carry_weight = 0; 2659 ch->char_specials.carry_items = 0; 2660 2661 if (GET_HIT(ch) <= 0) 2662 GET_HIT(ch) = 1; 2663 if (GET_MOVE(ch) <= 0) 2664 GET_MOVE(ch) = 1; 2665 if (GET_MANA(ch) <= 0) 2666 GET_MANA(ch) = 1; 2667 2668 GET_LAST_TELL(ch) = NOBODY; 2669 } 2670 2671 2672 2673 /* clear ALL the working variables of a char; do NOT free any space alloc'ed */ 2674 void clear_char(struct char_data *ch) 2675 { 2676 memset((char *) ch, 0, sizeof(struct char_data)); 2677 2678 IN_ROOM(ch) = NOWHERE; 2679 GET_PFILEPOS(ch) = -1; 2680 GET_MOB_RNUM(ch) = NOBODY; 2681 GET_WAS_IN(ch) = NOWHERE; 2682 GET_POS(ch) = POS_STANDING; 2683 ch->mob_specials.default_pos = POS_STANDING; 2684 2685 GET_AC(ch) = 100; /* Basic Armor */ 2686 if (ch->points.max_mana < 100) 2687 ch->points.max_mana = 100; 2688 } 2689 2690 2691 void clear_object(struct obj_data *obj) 2692 { 2693 memset((char *) obj, 0, sizeof(struct obj_data)); 2694 2695 obj->item_number = NOTHING; 2696 IN_ROOM(obj) = NOWHERE; 2697 obj->worn_on = NOWHERE; 2698 } 2699 2700 2701 2702 2703 /* 2704 * Called during character creation after picking character class 2705 * (and then never again for that character). 2706 */ 2707 void init_char(struct char_data *ch) 2708 { 2709 int i; 2710 2711 /* create a player_special structure */ 2712 if (ch->player_specials == NULL) 2713 CREATE(ch->player_specials, struct player_special_data, 1); 2714 2715 /* *** if this is our first player --- he be God *** */ 2716 if (top_of_p_table == 0) { 2717 GET_LEVEL(ch) = LVL_IMPL; 2718 GET_EXP(ch) = 7000000; 2719 2720 /* The implementor never goes through do_start(). */ 2721 GET_MAX_HIT(ch) = 500; 2722 GET_MAX_MANA(ch) = 100; 2723 GET_MAX_MOVE(ch) = 82; 2724 GET_HIT(ch) = GET_MAX_HIT(ch); 2725 GET_MANA(ch) = GET_MAX_MANA(ch); 2726 GET_MOVE(ch) = GET_MAX_MOVE(ch); 2727 } 2728 2729 set_title(ch, NULL); 2730 ch->player.short_descr = NULL; 2731 ch->player.long_descr = NULL; 2732 ch->player.description = NULL; 2733 2734 ch->player.time.birth = time(0); 2735 ch->player.time.logon = time(0); 2736 ch->player.time.played = 0; 2737 2738 GET_HOME(ch) = 1; 2739 GET_AC(ch) = 100; 2740 2741 for (i = 0; i < MAX_TONGUE; i++) 2742 GET_TALK(ch, i) = 0; 2743 2744 /* 2745 * make favors for sex -- or in English, we bias the height and weight of the 2746 * character depending on what gender they've chosen for themselves. While it 2747 * is possible to have a tall, heavy female it's not as likely as a male. 2748 * 2749 * Height is in centimeters. Weight is in pounds. The only place they're 2750 * ever printed (in stock code) is SPELL_IDENTIFY. 2751 */ 2752 if (GET_SEX(ch) == SEX_MALE) { 2753 GET_WEIGHT(ch) = rand_number(120, 180); 2754 GET_HEIGHT(ch) = rand_number(160, 200); /* 5'4" - 6'8" */ 2755 } else { 2756 GET_WEIGHT(ch) = rand_number(100, 160); 2757 GET_HEIGHT(ch) = rand_number(150, 180); /* 5'0" - 6'0" */ 2758 } 2759 2760 if ((i = get_ptable_by_name(GET_NAME(ch))) != -1) 2761 player_table[i].id = GET_IDNUM(ch) = ++top_idnum; 2762 else 2763 log("SYSERR: init_char: Character '%s' not found in player table.", GET_NAME(ch)); 2764 2765 for (i = 1; i <= MAX_SKILLS; i++) { 2766 if (GET_LEVEL(ch) < LVL_IMPL) 2767 SET_SKILL(ch, i, 0); 2768 else 2769 SET_SKILL(ch, i, 100); 2770 } 2771 2772 AFF_FLAGS(ch) = 0; 2773 2774 for (i = 0; i < 5; i++) 2775 GET_SAVE(ch, i) = 0; 2776 2777 ch->real_abils.intel = 25; 2778 ch->real_abils.wis = 25; 2779 ch->real_abils.dex = 25; 2780 ch->real_abils.str = 25; 2781 ch->real_abils.str_add = 100; 2782 ch->real_abils.con = 25; 2783 ch->real_abils.cha = 25; 2784 2785 for (i = 0; i < 3; i++) 2786 GET_COND(ch, i) = (GET_LEVEL(ch) == LVL_IMPL ? -1 : 24); 2787 2788 GET_LOADROOM(ch) = NOWHERE; 2789 } 2790 2791 2792 2793 /* returns the real number of the room with given virtual number */ 2794 room_rnum real_room(room_vnum vnum) 2795 { 2796 room_rnum bot, top, mid; 2797 2798 bot = 0; 2799 top = top_of_world; 2800 2801 /* perform binary search on world-table */ 2802 for (;;) { 2803 mid = (bot + top) / 2; 2804 2805 if ((world + mid)->number == vnum) 2806 return (mid); 2807 if (bot >= top) 2808 return (NOWHERE); 2809 if ((world + mid)->number > vnum) 2810 top = mid - 1; 2811 else 2812 bot = mid + 1; 2813 } 2814 } 2815 2816 2817 2818 /* returns the real number of the monster with given virtual number */ 2819 mob_rnum real_mobile(mob_vnum vnum) 2820 { 2821 mob_rnum bot, top, mid; 2822 2823 bot = 0; 2824 top = top_of_mobt; 2825 2826 /* perform binary search on mob-table */ 2827 for (;;) { 2828 mid = (bot + top) / 2; 2829 2830 if ((mob_index + mid)->vnum == vnum) 2831 return (mid); 2832 if (bot >= top) 2833 return (NOBODY); 2834 if ((mob_index + mid)->vnum > vnum) 2835 top = mid - 1; 2836 else 2837 bot = mid + 1; 2838 } 2839 } 2840 2841 2842 /* returns the real number of the object with given virtual number */ 2843 obj_rnum real_object(obj_vnum vnum) 2844 { 2845 obj_rnum bot, top, mid; 2846 2847 bot = 0; 2848 top = top_of_objt; 2849 2850 /* perform binary search on obj-table */ 2851 for (;;) { 2852 mid = (bot + top) / 2; 2853 2854 if ((obj_index + mid)->vnum == vnum) 2855 return (mid); 2856 if (bot >= top) 2857 return (NOTHING); 2858 if ((obj_index + mid)->vnum > vnum) 2859 top = mid - 1; 2860 else 2861 bot = mid + 1; 2862 } 2863 } 2864 2865 2866 /* returns the real number of the zone with given virtual number */ 2867 room_rnum real_zone(room_vnum vnum) 2868 { 2869 room_rnum bot, top, mid; 2870 2871 bot = 0; 2872 top = top_of_zone_table; 2873 2874 /* perform binary search on zone-table */ 2875 for (;;) { 2876 mid = (bot + top) / 2; 2877 2878 if ((zone_table + mid)->number == vnum) 2879 return (mid); 2880 if (bot >= top) 2881 return (NOWHERE); 2882 if ((zone_table + mid)->number > vnum) 2883 top = mid - 1; 2884 else 2885 bot = mid + 1; 2886 } 2887 } 2888 2889 2890 /* 2891 * Extend later to include more checks. 2892 * 2893 * TODO: Add checks for unknown bitvectors. 2894 */ 2895 int check_object(struct obj_data *obj) 2896 { 2897 char objname[MAX_INPUT_LENGTH + 32]; 2898 int error = FALSE; 2899 2900 if (GET_OBJ_WEIGHT(obj) < 0 && (error = TRUE)) 2901 log("SYSERR: Object #%d (%s) has negative weight (%d).", 2902 GET_OBJ_VNUM(obj), obj->short_description, GET_OBJ_WEIGHT(obj)); 2903 2904 if (GET_OBJ_RENT(obj) < 0 && (error = TRUE)) 2905 log("SYSERR: Object #%d (%s) has negative cost/day (%d).", 2906 GET_OBJ_VNUM(obj), obj->short_description, GET_OBJ_RENT(obj)); 2907 2908 snprintf(objname, sizeof(objname), "Object #%d (%s)", GET_OBJ_VNUM(obj), obj->short_description); 2909 error |= check_bitvector_names(GET_OBJ_WEAR(obj), wear_bits_count, objname, "object wear"); 2910 error |= check_bitvector_names(GET_OBJ_EXTRA(obj), extra_bits_count, objname, "object extra"); 2911 error |= check_bitvector_names(GET_OBJ_AFFECT(obj), affected_bits_count, objname, "object affect"); 2912 2913 switch (GET_OBJ_TYPE(obj)) { 2914 case ITEM_DRINKCON: 2915 { 2916 char onealias[MAX_INPUT_LENGTH], *space = strrchr(obj->name, ' '); 2917 2918 strlcpy(onealias, space ? space + 1 : obj->name, sizeof(onealias)); 2919 if (search_block(onealias, drinknames, TRUE) < 0 && (error = TRUE)) 2920 log("SYSERR: Object #%d (%s) doesn't have drink type as last alias. (%s)", 2921 GET_OBJ_VNUM(obj), obj->short_description, obj->name); 2922 } 2923 /* Fall through. */ 2924 case ITEM_FOUNTAIN: 2925 if (GET_OBJ_VAL(obj, 1) > GET_OBJ_VAL(obj, 0) && (error = TRUE)) 2926 log("SYSERR: Object #%d (%s) contains (%d) more than maximum (%d).", 2927 GET_OBJ_VNUM(obj), obj->short_description, 2928 GET_OBJ_VAL(obj, 1), GET_OBJ_VAL(obj, 0)); 2929 break; 2930 case ITEM_SCROLL: 2931 case ITEM_POTION: 2932 error |= check_object_level(obj, 0); 2933 error |= check_object_spell_number(obj, 1); 2934 error |= check_object_spell_number(obj, 2); 2935 error |= check_object_spell_number(obj, 3); 2936 break; 2937 case ITEM_WAND: 2938 case ITEM_STAFF: 2939 error |= check_object_level(obj, 0); 2940 error |= check_object_spell_number(obj, 3); 2941 if (GET_OBJ_VAL(obj, 2) > GET_OBJ_VAL(obj, 1) && (error = TRUE)) 2942 log("SYSERR: Object #%d (%s) has more charges (%d) than maximum (%d).", 2943 GET_OBJ_VNUM(obj), obj->short_description, 2944 GET_OBJ_VAL(obj, 2), GET_OBJ_VAL(obj, 1)); 2945 break; 2946 } 2947 2948 return (error); 2949 } 2950 2951 int check_object_spell_number(struct obj_data *obj, int val) 2952 { 2953 int error = FALSE; 2954 const char *spellname; 2955 2956 if (GET_OBJ_VAL(obj, val) == -1) /* i.e.: no spell */ 2957 return (error); 2958 2959 /* 2960 * Check for negative spells, spells beyond the top define, and any 2961 * spell which is actually a skill. 2962 */ 2963 if (GET_OBJ_VAL(obj, val) < 0) 2964 error = TRUE; 2965 if (GET_OBJ_VAL(obj, val) > TOP_SPELL_DEFINE) 2966 error = TRUE; 2967 if (GET_OBJ_VAL(obj, val) > MAX_SPELLS && GET_OBJ_VAL(obj, val) <= MAX_SKILLS) 2968 error = TRUE; 2969 if (error) 2970 log("SYSERR: Object #%d (%s) has out of range spell #%d.", 2971 GET_OBJ_VNUM(obj), obj->short_description, GET_OBJ_VAL(obj, val)); 2972 2973 /* 2974 * This bug has been fixed, but if you don't like the special behavior... 2975 */ 2976 #if 0 2977 if (GET_OBJ_TYPE(obj) == ITEM_STAFF && 2978 HAS_SPELL_ROUTINE(GET_OBJ_VAL(obj, val), MAG_AREAS | MAG_MASSES)) 2979 log("... '%s' (#%d) uses %s spell '%s'.", 2980 obj->short_description, GET_OBJ_VNUM(obj), 2981 HAS_SPELL_ROUTINE(GET_OBJ_VAL(obj, val), MAG_AREAS) ? "area" : "mass", 2982 skill_name(GET_OBJ_VAL(obj, val))); 2983 #endif 2984 2985 if (scheck) /* Spell names don't exist in syntax check mode. */ 2986 return (error); 2987 2988 /* Now check for unnamed spells. */ 2989 spellname = skill_name(GET_OBJ_VAL(obj, val)); 2990 2991 if ((spellname == unused_spellname || !str_cmp("UNDEFINED", spellname)) && (error = TRUE)) 2992 log("SYSERR: Object #%d (%s) uses '%s' spell #%d.", 2993 GET_OBJ_VNUM(obj), obj->short_description, spellname, 2994 GET_OBJ_VAL(obj, val)); 2995 2996 return (error); 2997 } 2998 2999 int check_object_level(struct obj_data *obj, int val) 3000 { 3001 int error = FALSE; 3002 3003 if ((GET_OBJ_VAL(obj, val) < 0 || GET_OBJ_VAL(obj, val) > LVL_IMPL) && (error = TRUE)) 3004 log("SYSERR: Object #%d (%s) has out of range level #%d.", 3005 GET_OBJ_VNUM(obj), obj->short_description, GET_OBJ_VAL(obj, val)); 3006 3007 return (error); 3008 } 3009 3010 int check_bitvector_names(bitvector_t bits, size_t namecount, const char *whatami, const char *whatbits) 3011 { 3012 unsigned int flagnum; 3013 bool error = FALSE; 3014 3015 /* See if any bits are set above the ones we know about. */ 3016 if (bits <= (~(bitvector_t)0 >> (sizeof(bitvector_t) * 8 - namecount))) 3017 return (FALSE); 3018 3019 for (flagnum = namecount; flagnum < sizeof(bitvector_t) * 8; flagnum++) 3020 if ((1 << flagnum) & bits) { 3021 log("SYSERR: %s has unknown %s flag, bit %d (0 through %d known).", whatami, whatbits, flagnum, namecount - 1); 3022 error = TRUE; 3023 } 3024 3025 return (error); 3026 } 3027