Crossfire Server, Branch 1.12
R12190
|
00001 /* 00002 * static char *rcsid_readable_c = 00003 * "$Id: readable.c 11578 2009-02-23 22:02:27Z lalo $"; 00004 */ 00005 00006 /* 00007 CrossFire, A Multiplayer game for X-windows 00008 00009 Copyright (C) 2002 Mark Wedel & Crossfire Development Team 00010 Copyright (C) 1992 Frank Tore Johansen 00011 00012 This program is free software; you can redistribute it and/or modify 00013 it under the terms of the GNU General Public License as published by 00014 the Free Software Foundation; either version 2 of the License, or 00015 (at your option) any later version. 00016 00017 This program is distributed in the hope that it will be useful, 00018 but WITHOUT ANY WARRANTY; without even the implied warranty of 00019 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00020 GNU General Public License for more details. 00021 00022 You should have received a copy of the GNU General Public License 00023 along with this program; if not, write to the Free Software 00024 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 00025 00026 The authors can be reached via e-mail at crossfire-devel@real-time.com 00027 */ 00028 00038 /* laid down initial file - dec 1995. -b.t. thomas@astro.psu.edu */ 00039 00040 #include <stdio.h> 00041 #include <global.h> 00042 #include <book.h> 00043 #include <living.h> 00044 #include <spells.h> 00045 00046 /* Define this if you want to archive book titles by contents. 00047 * This option should enforce UNIQUE combinations of titles,authors and 00048 * msg contents during and *between *game sessions. 00049 * Note: a slight degeneracy exists since books are archived based on an integer 00050 * index value calculated from the message text (similar to alchemy formulae). 00051 * Sometimes two widely different messages have the same index value (rare). In 00052 * this case, it is possible to occasionally generate 2 books with same title and 00053 * different message content. Not really a bug, but rather a feature. This action 00054 * should keeps player on their toes ;). 00055 * Also, note that there is *finite *space available for archiving message and titles. 00056 * Once this space is used, books will stop being archived. Not a serious problem 00057 * under the current regime, since there are generally fewer possible (random) 00058 * messages than space available on the titlelists. 00059 * One exception (for sure) are the monster messages. But no worries, you should 00060 * see all of the monster info in some order (but not all possble combinations) 00061 * before the monster titlelist space is run out. You can increase titlelist 00062 * space by increasing the array sizes for the monster book_authours and book_names 00063 * (see max_titles[] array and include/read.h). Since the unique_book algorthm is 00064 * kinda stupid, this program *may *slow down program execution if defined (but I don't 00065 * think its a significant problem, at least, I have no problems running this option 00066 * on a Sparc 10! Also, once archive title lists are filled and/or all possible msg 00067 * combinations have been generated, unique_book isnt called anymore. It takes 5-10 00068 * sessions for this to happen). 00069 * Final note: the game remembers book/title/msg combinations from reading the 00070 * file lib/bookarch. If you REMOVE this file, you will lose your archive. So 00071 * be sure to copy it over to the new lib directory when you change versions. 00072 * -b.t. 00073 */ 00074 00075 /* This flag is useful to see what kind of output messages are created */ 00076 /* #define BOOK_MSG_DEBUG */ 00077 00078 /* This flag is useful for debugging archiving action */ 00079 /* #define ARCHIVE_DEBUG */ 00080 00082 #define MAX_TITLE_CHECK 20 00083 00084 #define MSGTYPE_LIB 0 00085 #define MSGTYPE_MONSTER 1 00086 #define MSGTYPE_ARTIFACT 2 00087 #define MSGTYPE_SPELLPATH 3 00088 #define MSGTYPE_ALCHEMY 4 00089 #define MSGTYPE_GODS 5 00090 #define MSGTYPE_MSGFILE 6 00091 00097 #define arraysize(arrayname) (sizeof(arrayname)/sizeof(*(arrayname))) 00098 00099 /* Moved these structures from struct.h to this file in 0.94.3 - they 00100 * are not needed anyplace else, so why have them globally declared? 00101 */ 00102 00104 typedef struct titlestruct { 00105 const char *name; 00106 const char *authour; 00107 const char *archname; 00108 int level; 00109 size_t size; 00110 int msg_index; 00111 struct titlestruct *next; 00112 } title; 00113 00114 typedef struct titleliststruct { 00115 int number; 00116 struct titlestruct *first_book; 00117 struct titleliststruct *next; 00118 } titlelist; 00119 00121 typedef struct namebytype { 00122 const char *name; 00123 int type; 00124 } arttypename; 00125 00126 static void add_book(title *book, int type, const char *fname, int lineno); 00127 00132 static titlelist *booklist = NULL; 00133 00135 static objectlink *first_mon_info = NULL; 00136 00141 static int nrofmon = 0, need_to_write_bookarchive = 0; 00142 00147 static int nrofmsg = 0; 00148 00153 static linked_char *first_msg = NULL; 00154 00158 static const uint32 spellpathdef[NRSPELLPATHS] = { 00159 PATH_PROT, 00160 PATH_FIRE, 00161 PATH_FROST, 00162 PATH_ELEC, 00163 PATH_MISSILE, 00164 PATH_SELF, 00165 PATH_SUMMON, 00166 PATH_ABJURE, 00167 PATH_RESTORE, 00168 PATH_DETONATE, 00169 PATH_MIND, 00170 PATH_CREATE, 00171 PATH_TELE, 00172 PATH_INFO, 00173 PATH_TRANSMUTE, 00174 PATH_TRANSFER, 00175 PATH_TURNING, 00176 PATH_WOUNDING, 00177 PATH_DEATH, 00178 PATH_LIGHT 00179 }; 00180 00182 static const char *const path_book_name[] = { 00183 "codex", 00184 "compendium", 00185 "exposition", 00186 "tables", 00187 "treatise" 00188 }; 00189 00191 static const char *const path_author[] = { 00192 "aether", 00193 "astral byways", 00194 "connections", 00195 "the Grey Council", 00196 "deep pathways", 00197 "knowledge", 00198 "magic", 00199 "mystic ways", 00200 "pathways", 00201 "power", 00202 "spells", 00203 "transforms", 00204 "the mystic veil", 00205 "unknown spells" 00206 }; 00207 00214 static const arttypename art_name_array[] = { 00215 { "Helmet", HELMET }, 00216 { "Amulet", AMULET }, 00217 { "Shield", SHIELD }, 00218 { "Bracers", BRACERS }, 00219 { "Boots", BOOTS }, 00220 { "Cloak", CLOAK }, 00221 { "Gloves", GLOVES }, 00222 { "Gridle", GIRDLE }, 00223 { "Ring", RING }, 00224 { "Horn", HORN }, 00225 { "Missile Weapon", BOW }, 00226 { "Missile", ARROW }, 00227 { "Hand Weapon", WEAPON }, 00228 { "Artifact", SKILL }, 00229 { "Food", FOOD }, 00230 { "Body Armour", ARMOUR } 00231 }; 00232 00234 static const char *const art_book_name[] = { 00235 "collection", 00236 "file", 00237 "files", 00238 "guide", 00239 "handbook", 00240 "index", 00241 "inventory", 00242 "list", 00243 "listing", 00244 "record", 00245 "record book" 00246 }; 00247 00249 static const char *const art_author[] = { 00250 "ancient things", 00251 "artifacts", 00252 "Havlor", /* ancient warrior scribe :) */ 00253 "items", 00254 "lost artifacts", 00255 "the ancients", 00256 "useful things" 00257 }; 00258 00262 static const char *const mon_book_name[] = { 00263 "beastuary", 00264 "catalog", 00265 "compilation", 00266 "collection", 00267 "encyclopedia", 00268 "guide", 00269 "handbook", 00270 "list", 00271 "manual", 00272 "notes", 00273 "record", 00274 "register", 00275 "volume" 00276 }; 00277 00279 static const char *const mon_author[] = { 00280 "beasts", 00281 "creatures", 00282 "dezidens", 00283 "dwellers", 00284 "evil nature", 00285 "life", 00286 "monsters", 00287 "nature", 00288 "new life", 00289 "residents", 00290 "the spawn", 00291 "the living", 00292 "things" 00293 }; 00294 00298 static const char *const gods_book_name[] = { 00299 "devotional", 00300 "devout notes", 00301 "divine text", 00302 "divine work", 00303 "holy book", 00304 "holy record", 00305 "moral text", 00306 "sacred guide", 00307 "testament", 00308 "transcript" 00309 }; 00310 00312 static const char *const gods_author[] = { 00313 "cults", 00314 "joy", 00315 "lasting curse", 00316 "madness", 00317 "religions", 00318 "the dead", 00319 "the gods", 00320 "the heirophant", 00321 "the poor priest", 00322 "the priestess", 00323 "pain", 00324 "white" 00325 }; 00326 00330 static const char *const formula_book_name[] = { 00331 "cookbook", 00332 "formulary", 00333 "lab book", 00334 "lab notes", 00335 "recipe book", 00336 "experiment record", 00337 "work plan", 00338 "design notes" 00339 }; 00340 00342 static const char *const formula_author[] = { 00343 "Albertus Magnus", 00344 "alchemy", 00345 "balms", 00346 "creation", 00347 "dusts", 00348 "magical manufacture", 00349 "making", 00350 "philosophical items", 00351 "potions", 00352 "powders", 00353 "the cauldron", 00354 "the lamp black", 00355 "transmutation", 00356 "waters" 00357 }; 00358 00364 static const char *const light_book_name[] = { 00365 "calendar", 00366 "datebook", 00367 "diary", 00368 "guidebook", 00369 "handbook", 00370 "ledger", 00371 "notes", 00372 "notebook", 00373 "octavo", 00374 "pamphlet", 00375 "practicum", 00376 "script", 00377 "transcript" 00378 }; 00379 00381 static const char *const heavy_book_name[] = { 00382 "catalog", 00383 "compendium", 00384 "guide", 00385 "manual", 00386 "opus", 00387 "tome", 00388 "treatise", 00389 "volume", 00390 "work" 00391 }; 00392 00394 static const char *const book_author[] = { 00395 "Abdulah", 00396 "Al'hezred", 00397 "Alywn", 00398 "Arundel", 00399 "Arvind", 00400 "Aerlingas", 00401 "Bacon", 00402 "Baliqendii", 00403 "Bosworth", 00404 "Beathis", 00405 "Bertil", 00406 "Cauchy", 00407 "Chakrabarti", 00408 "der Waalis", 00409 "Dirk", 00410 "Djwimii", 00411 "Eisenstaadt", 00412 "Fendris", 00413 "Frank", 00414 "Habbi", 00415 "Harlod", 00416 "Ichibod", 00417 "Janus", 00418 "June", 00419 "Magnuson", 00420 "Nandii", 00421 "Nitfeder", 00422 "Norris", 00423 "Parael", 00424 "Penhew", 00425 "Sophia", 00426 "Skilly", 00427 "Tahir", 00428 "Thockmorton", 00429 "Thomas", 00430 "van Helsing", 00431 "van Pelt", 00432 "Voormis", 00433 "Xavier", 00434 "Xeno", 00435 "Zardoz", 00436 "Zagy" 00437 }; 00438 00440 static const char *const book_descrpt[] = { 00441 "ancient", 00442 "cryptic", 00443 "cryptical", 00444 "dusty", 00445 "hiearchical", 00446 "grizzled", 00447 "gold-guilt", 00448 "great", 00449 "lost", 00450 "magnificent", 00451 "musty", 00452 "mythical", 00453 "mystical", 00454 "rustic", 00455 "stained", 00456 "silvered", 00457 "transcendental", 00458 "weathered" 00459 }; 00460 00467 static const readable_message_type readable_message_types[] = { 00468 /*subtype 0 */ { 0, 0 }, 00469 /* book messages subtypes */ 00470 /*subtype 1 */ { MSG_TYPE_BOOK, MSG_TYPE_BOOK_CLASP_1 }, 00471 { MSG_TYPE_BOOK, MSG_TYPE_BOOK_CLASP_2 }, 00472 { MSG_TYPE_BOOK, MSG_TYPE_BOOK_ELEGANT_1 }, 00473 { MSG_TYPE_BOOK, MSG_TYPE_BOOK_ELEGANT_2 }, 00474 { MSG_TYPE_BOOK, MSG_TYPE_BOOK_QUARTO_1 }, 00475 { MSG_TYPE_BOOK, MSG_TYPE_BOOK_QUARTO_2 }, 00476 { MSG_TYPE_BOOK, MSG_TYPE_BOOK_SPELL_EVOKER }, 00477 { MSG_TYPE_BOOK, MSG_TYPE_BOOK_SPELL_PRAYER }, 00478 { MSG_TYPE_BOOK, MSG_TYPE_BOOK_SPELL_PYRO }, 00479 /*subtype 10 */ { MSG_TYPE_BOOK, MSG_TYPE_BOOK_SPELL_SORCERER }, 00480 { MSG_TYPE_BOOK, MSG_TYPE_BOOK_SPELL_SUMMONER }, 00481 /* card messages subtypes*/ 00482 { MSG_TYPE_CARD, MSG_TYPE_CARD_SIMPLE_1 }, 00483 { MSG_TYPE_CARD, MSG_TYPE_CARD_SIMPLE_2 }, 00484 { MSG_TYPE_CARD, MSG_TYPE_CARD_SIMPLE_3 }, 00485 { MSG_TYPE_CARD, MSG_TYPE_CARD_ELEGANT_1 }, 00486 { MSG_TYPE_CARD, MSG_TYPE_CARD_ELEGANT_2 }, 00487 { MSG_TYPE_CARD, MSG_TYPE_CARD_ELEGANT_3 }, 00488 { MSG_TYPE_CARD, MSG_TYPE_CARD_STRANGE_1 }, 00489 { MSG_TYPE_CARD, MSG_TYPE_CARD_STRANGE_2 }, 00490 /*subtype 20 */ { MSG_TYPE_CARD, MSG_TYPE_CARD_STRANGE_3 }, 00491 { MSG_TYPE_CARD, MSG_TYPE_CARD_MONEY_1 }, 00492 { MSG_TYPE_CARD, MSG_TYPE_CARD_MONEY_2 }, 00493 { MSG_TYPE_CARD, MSG_TYPE_CARD_MONEY_3 }, 00494 00495 /* Paper messages subtypes */ 00496 { MSG_TYPE_PAPER, MSG_TYPE_PAPER_NOTE_1 }, 00497 { MSG_TYPE_PAPER, MSG_TYPE_PAPER_NOTE_2 }, 00498 { MSG_TYPE_PAPER, MSG_TYPE_PAPER_NOTE_3 }, 00499 { MSG_TYPE_PAPER, MSG_TYPE_PAPER_LETTER_OLD_1 }, 00500 { MSG_TYPE_PAPER, MSG_TYPE_PAPER_LETTER_OLD_2 }, 00501 { MSG_TYPE_PAPER, MSG_TYPE_PAPER_LETTER_NEW_1 }, 00502 /*subtype 30 */ { MSG_TYPE_PAPER, MSG_TYPE_PAPER_LETTER_NEW_2 }, 00503 { MSG_TYPE_PAPER, MSG_TYPE_PAPER_ENVELOPE_1 }, 00504 { MSG_TYPE_PAPER, MSG_TYPE_PAPER_ENVELOPE_2 }, 00505 { MSG_TYPE_PAPER, MSG_TYPE_PAPER_SCROLL_OLD_1 }, 00506 { MSG_TYPE_PAPER, MSG_TYPE_PAPER_SCROLL_OLD_2 }, 00507 { MSG_TYPE_PAPER, MSG_TYPE_PAPER_SCROLL_NEW_1 }, 00508 { MSG_TYPE_PAPER, MSG_TYPE_PAPER_SCROLL_NEW_2 }, 00509 { MSG_TYPE_PAPER, MSG_TYPE_PAPER_SCROLL_MAGIC }, 00510 00511 /* road signs messages subtypes */ 00512 { MSG_TYPE_SIGN, MSG_TYPE_SIGN_BASIC }, 00513 { MSG_TYPE_SIGN, MSG_TYPE_SIGN_DIR_LEFT }, 00514 /*subtype 40 */ { MSG_TYPE_SIGN, MSG_TYPE_SIGN_DIR_RIGHT }, 00515 { MSG_TYPE_SIGN, MSG_TYPE_SIGN_DIR_BOTH }, 00516 00517 /* stones and monument messages */ 00518 { MSG_TYPE_MONUMENT, MSG_TYPE_MONUMENT_STONE_1 }, 00519 { MSG_TYPE_MONUMENT, MSG_TYPE_MONUMENT_STONE_2 }, 00520 { MSG_TYPE_MONUMENT, MSG_TYPE_MONUMENT_STONE_3 }, 00521 { MSG_TYPE_MONUMENT, MSG_TYPE_MONUMENT_STATUE_1 }, 00522 { MSG_TYPE_MONUMENT, MSG_TYPE_MONUMENT_STATUE_2 }, 00523 { MSG_TYPE_MONUMENT, MSG_TYPE_MONUMENT_STATUE_3 }, 00524 { MSG_TYPE_MONUMENT, MSG_TYPE_MONUMENT_GRAVESTONE_1 }, 00525 { MSG_TYPE_MONUMENT, MSG_TYPE_MONUMENT_GRAVESTONE_2 }, 00526 /*subtype 50 */ { MSG_TYPE_MONUMENT, MSG_TYPE_MONUMENT_GRAVESTONE_3 }, 00527 { MSG_TYPE_MONUMENT, MSG_TYPE_MONUMENT_WALL_1 }, 00528 { MSG_TYPE_MONUMENT, MSG_TYPE_MONUMENT_WALL_2 }, 00529 { MSG_TYPE_MONUMENT, MSG_TYPE_MONUMENT_WALL_3 } 00530 }; 00531 00533 static const int last_readable_subtype = arraysize(readable_message_types); 00534 00536 static const int max_titles[6] = { 00537 (arraysize(light_book_name)+arraysize(heavy_book_name))*arraysize(book_author), /* MSGTYPE_LIB */ 00538 arraysize(mon_book_name)*arraysize(mon_author), /* MSGTYPE_MONSTER */ 00539 arraysize(art_book_name)*arraysize(art_author), /* MSGTYPE_ARTIFACT */ 00540 arraysize(path_book_name)*arraysize(path_author), /* MSGTYPE_SPELLPATH */ 00541 arraysize(formula_book_name)*arraysize(formula_author), /* MSGTYPE_ALCHEMY */ 00542 arraysize(gods_book_name)*arraysize(gods_author), /* MSGTYPE_GODS */ 00543 }; 00544 00545 /****************************************************************************** 00546 * 00547 * Start of misc. readable functions used by others functions in this file 00548 * 00549 *****************************************************************************/ 00550 00560 static titlelist *get_empty_booklist(void) { 00561 titlelist *bl = (titlelist *)malloc(sizeof(titlelist)); 00562 00563 if (bl == NULL) 00564 fatal(OUT_OF_MEMORY); 00565 bl->number = 0; 00566 bl->first_book = NULL; 00567 bl->next = NULL; 00568 return bl; 00569 } 00570 00580 static title *get_empty_book(void) { 00581 title *t = (title *)malloc(sizeof(title)); 00582 00583 if (t == NULL) 00584 fatal(OUT_OF_MEMORY); 00585 t->name = NULL; 00586 t->archname = NULL; 00587 t->authour = NULL; 00588 t->level = 0; 00589 t->size = 0; 00590 t->msg_index = 0; 00591 t->next = NULL; 00592 return t; 00593 } 00594 00605 static titlelist *get_titlelist(int i) { 00606 titlelist *tl; 00607 int number; 00608 00609 if (i < 0 || i >= (int)arraysize(max_titles)) { 00610 LOG(llevInfo, "Warning: invalid book index %d, using 0 instead\n", i); 00611 return booklist; 00612 } 00613 00614 for (tl = booklist, number = i; tl && number; tl = tl->next, number--) { 00615 if (!tl->next) 00616 tl->next = get_empty_booklist(); 00617 } 00618 00619 return tl; 00620 } 00621 00622 /* HANDMADE STRING FUNCTIONS.., perhaps these belong in another file 00623 * (shstr.c ?), but the quantity BOOK_BUF will need to be defined. */ 00624 00636 static int nstrtok(const char *buf1, const char *buf2) { 00637 char *tbuf, buf[MAX_BUF]; 00638 int number = 0; 00639 00640 if (!buf1 || !buf2) 00641 return 0; 00642 00643 snprintf(buf, sizeof(buf), "%s", buf1); 00644 for (tbuf = strtok(buf, buf2); tbuf; tbuf = strtok(NULL, buf2)) { 00645 number++; 00646 } 00647 return number; 00648 } 00649 00666 static char *strtoktolin(const char *buf1, const char *buf2, char *retbuf, size_t size) { 00667 int maxi, i = nstrtok(buf1, buf2); 00668 char *tbuf, buf[MAX_BUF]; 00669 00670 maxi = i; 00671 snprintf(buf, sizeof(buf), "%s", buf1); 00672 snprintf(retbuf, size, " "); 00673 for (tbuf = strtok(buf, buf2); tbuf && i > 0; tbuf = strtok(NULL, buf2)) { 00674 snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "%s", tbuf); 00675 i--; 00676 if (i == 1 && maxi > 1) 00677 snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), " and "); 00678 else if (i > 0 && maxi > 1) 00679 snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), ", "); 00680 else 00681 snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "."); 00682 } 00683 return retbuf; 00684 } 00685 00696 int book_overflow(const char *buf1, const char *buf2, size_t booksize) { 00697 if (buf_overflow(buf1, buf2, BOOK_BUF-2) /* 2 less so always room for trailing \n */ 00698 || buf_overflow(buf1, buf2, booksize)) 00699 return 1; 00700 return 0; 00701 } 00702 00703 /***************************************************************************** 00704 * 00705 * Start of initialization related functions. 00706 * 00707 ****************************************************************************/ 00708 00714 static void init_msgfile(void) { 00715 FILE *fp; 00716 char buf[MAX_BUF], msgbuf[HUGE_BUF], fname[MAX_BUF], *cp; 00717 int comp; 00718 static int did_init_msgfile = 0; 00719 00720 if (did_init_msgfile) 00721 return; 00722 did_init_msgfile = 1; 00723 00724 snprintf(fname, sizeof(fname), "%s/messages", settings.datadir); 00725 LOG(llevDebug, "Reading messages from %s...\n", fname); 00726 00727 fp = open_and_uncompress(fname, 0, &comp); 00728 if (fp != NULL) { 00729 linked_char *tmp = NULL; 00730 int lineno; 00731 int error_lineno; 00732 00733 error_lineno = 0; 00734 for (lineno = 1; fgets(buf, MAX_BUF, fp) != NULL; lineno++) { 00735 if (*buf == '#') 00736 continue; 00737 cp = strchr(buf, '\n'); 00738 if (cp != NULL) { 00739 while (cp > buf && (cp[-1] == ' ' || cp[-1] == '\t')) 00740 cp--; 00741 *cp = '\0'; 00742 } 00743 if (tmp != NULL) { 00744 if (strcmp(buf, "ENDMSG") == 0) { 00745 if (strlen(msgbuf) > BOOK_BUF) { 00746 LOG(llevDebug, "Warning: this string exceeded max book buf size:\n"); 00747 LOG(llevDebug, " %s\n", msgbuf); 00748 } 00749 tmp->name = add_string(msgbuf); 00750 tmp->next = first_msg; 00751 first_msg = tmp; 00752 nrofmsg++; 00753 tmp = NULL; 00754 } else if (!buf_overflow(msgbuf, buf, HUGE_BUF-1)) { 00755 strcat(msgbuf, buf); 00756 strcat(msgbuf, "\n"); 00757 } else if (error_lineno != 0) { 00758 LOG(llevInfo, "Warning: truncating book at %s, line %d\n", fname, error_lineno); 00759 error_lineno = 0; 00760 } 00761 } else if (strcmp(buf, "MSG") == 0) { 00762 error_lineno = lineno; 00763 tmp = (linked_char *)malloc(sizeof(linked_char)); 00764 strcpy(msgbuf, " "); /* reset msgbuf for new message */ 00765 } else { 00766 LOG(llevInfo, "Warning: syntax error at %s, line %d\n", fname, lineno); 00767 } 00768 } 00769 close_and_delete(fp, comp); 00770 } 00771 00772 #ifdef BOOK_MSG_DEBUG 00773 LOG(llevDebug, "init_info_listfile() got %d messages.\n", nrofmsg); 00774 #endif 00775 LOG(llevDebug, "done messages.\n"); 00776 } 00777 00784 static void init_book_archive(void) { 00785 FILE *fp; 00786 int comp, nroftitle = 0; 00787 char buf[MAX_BUF], fname[MAX_BUF], *cp; 00788 static int did_init_barch = 0; 00789 00790 if (did_init_barch) 00791 return; 00792 did_init_barch = 1; 00793 00794 if (!booklist) 00795 booklist = get_empty_booklist(); 00796 00797 snprintf(fname, sizeof(fname), "%s/bookarch", settings.localdir); 00798 LOG(llevDebug, " Reading bookarch from %s...\n", fname); 00799 00800 fp = open_and_uncompress(fname, 0, &comp); 00801 if (fp != NULL) { 00802 int type; 00803 size_t i; 00804 titlelist *bl; 00805 int lineno; 00806 title *book; 00807 int skipping; 00808 00809 skipping = 0; 00810 book = NULL; 00811 type = -1; 00812 for (lineno = 1; fgets(buf, MAX_BUF, fp) != NULL; lineno++) { 00813 int len; 00814 int value; 00815 00816 if (*buf == '#') 00817 continue; 00818 cp = strchr(buf, '\n'); 00819 if (cp != NULL) { 00820 while (cp > buf && (cp[-1] == ' ' || cp[-1] == '\t')) 00821 cp--; 00822 *cp = '\0'; 00823 } 00824 cp = buf; 00825 if (strncmp(buf, "title ", 6) == 0) { 00826 skipping = 0; 00827 cp = buf+6; 00828 while (*cp == ' ' || *cp == '\t') 00829 cp++; 00830 if (*cp == '\0') { 00831 LOG(llevInfo, "Warning: missing book title at %s, line %d\n", fname, lineno); 00832 book = NULL; 00833 } else { 00834 book = get_empty_book(); /* init new book entry */ 00835 book->name = add_string(cp); 00836 type = -1; 00837 nroftitle++; 00838 } 00839 } else if (book == NULL) { 00840 if (!skipping) { 00841 skipping = 1; 00842 LOG(llevInfo, "Warning: expecting 'title' at %s, line %d\n", fname, lineno); 00843 } 00844 } else if (strncmp(buf, "authour ", 8) == 0) { 00845 cp = buf+8; 00846 while (*cp == ' ' || *cp == '\t') 00847 cp++; 00848 if (*cp == '\0') { 00849 LOG(llevInfo, "Warning: missing book authour at %s, line %d\n", fname, lineno); 00850 } else { 00851 book->authour = add_string(cp); 00852 } 00853 } else if (strncmp(buf, "arch ", 5) == 0) { 00854 cp = buf+5; 00855 while (*cp == ' ' || *cp == '\t') 00856 cp++; 00857 if (*cp == '\0') { 00858 LOG(llevInfo, "Warning: missing book arch at %s, line %d\n", fname, lineno); 00859 } else { 00860 book->archname = add_string(cp); 00861 } 00862 } else if (sscanf(buf, "level %d%n", &value, &len) == 1 && len == (int)strlen(buf)) { 00863 book->level = value; 00864 } else if (sscanf(buf, "type %d%n", &value, &len) == 1 && len == (int)strlen(buf)) { 00865 type = value; 00866 } else if (sscanf(buf, "size %d%n", &value, &len) == 1 && len == (int)strlen(buf)) { 00867 book->size = value; 00868 } else if (sscanf(buf, "index %d%n", &value, &len) == 1 && len == (int)strlen(buf)) { 00869 book->msg_index = value; 00870 } else if (strcmp(buf, "end") == 0) { /* link it */ 00871 add_book(book, type, fname, lineno); 00872 book = NULL; 00873 type = -1; 00874 } else { 00875 LOG(llevInfo, "Warning: syntax error at %s, line %d\n", fname, lineno); 00876 } 00877 } 00878 if (book != NULL) { 00879 LOG(llevInfo, "Warning: missing 'end' at %s, line %d\n", fname, lineno); 00880 add_book(book, type, fname, lineno); 00881 } 00882 LOG(llevDebug, " book archives(used/avail):\n"); 00883 for (bl = booklist, i = 0; bl != NULL && i < arraysize(max_titles); bl = bl->next, i++) { 00884 LOG(llevDebug, "(%d/%d)\n", bl->number, max_titles[i]); 00885 } 00886 close_and_delete(fp, comp); 00887 } 00888 00889 #ifdef BOOK_MSG_DEBUG 00890 LOG(llevDebug, "\n init_book_archive() got %d titles.\n", nroftitle); 00891 #endif 00892 LOG(llevDebug, " done.\n"); 00893 } 00894 00902 static void add_book(title *book, int type, const char *fname, int lineno) { 00903 titlelist *bl; 00904 00905 if (type == -1) { 00906 LOG(llevInfo, "Warning: book with no type at %s, line %d; using type 0\n", fname, lineno); 00907 type = 0; 00908 } 00909 00910 bl = get_titlelist(type); 00911 book->next = bl->first_book; 00912 bl->first_book = book; 00913 bl->number++; 00914 } 00915 00920 static void init_mon_info(void) { 00921 archetype *at; 00922 static int did_init_mon_info = 0; 00923 00924 if (did_init_mon_info) 00925 return; 00926 did_init_mon_info = 1; 00927 00928 for (at = first_archetype; at != NULL; at = at->next) { 00929 if (QUERY_FLAG(&at->clone, FLAG_MONSTER) 00930 && (!QUERY_FLAG(&at->clone, FLAG_CHANGING) || QUERY_FLAG(&at->clone, FLAG_UNAGGRESSIVE))) { 00931 objectlink *mon = (objectlink *)malloc(sizeof(objectlink)); 00932 if (!mon) { 00933 LOG(llevError, "init_mon_info: malloc failed!\n"); 00934 abort(); 00935 } 00936 mon->ob = &at->clone; 00937 mon->id = nrofmon; 00938 mon->next = first_mon_info; 00939 first_mon_info = mon; 00940 nrofmon++; 00941 } 00942 } 00943 LOG(llevDebug, "init_mon_info() got %d monsters\n", nrofmon); 00944 } 00945 00952 void init_readable(void) { 00953 static int did_this = 0; 00954 00955 if (did_this) 00956 return; 00957 did_this = 1; 00958 00959 LOG(llevDebug, "Initializing reading data...\n"); 00960 init_msgfile(); 00961 init_book_archive(); 00962 init_mon_info(); 00963 LOG(llevDebug, " done reading data\n"); 00964 } 00965 00966 /***************************************************************************** 00967 * 00968 * This is the start of the administrative functions when creating 00969 * new books (ie, updating title and the like) 00970 * 00971 *****************************************************************************/ 00972 00984 static title *find_title(const object *book, int msgtype) { 00985 title *t; 00986 titlelist *tl; 00987 size_t length; 00988 int index; 00989 00990 if (msgtype < 0) 00991 return (title *)NULL; 00992 00993 tl = get_titlelist(msgtype); 00994 if (!tl) 00995 return (title *)NULL; 00996 00997 length = strlen(book->msg); 00998 index = strtoint(book->msg); 00999 for (t = tl->first_book; t; t = t->next) 01000 if (t->size == length && t->msg_index == index) { 01001 #ifdef ARCHIVE_DEBUG 01002 LOG(llevDebug, "Found title match (list %d): %s %s (%d)\n", msgtype, t->name, t->authour, t->msg_index); 01003 #endif 01004 return t; 01005 } 01006 01007 return (title *)NULL; 01008 } 01009 01023 static void new_text_name(object *book, int msgtype) { 01024 const char *name; 01025 01026 if (book->type != BOOK) 01027 return; 01028 01029 switch (msgtype) { 01030 case MSGTYPE_MONSTER: 01031 name = mon_book_name[RANDOM()%arraysize(mon_book_name)]; 01032 break; 01033 01034 case MSGTYPE_ARTIFACT: 01035 name = art_book_name[RANDOM()%arraysize(art_book_name)]; 01036 break; 01037 01038 case MSGTYPE_SPELLPATH: 01039 name = path_book_name[RANDOM()%arraysize(path_book_name)]; 01040 break; 01041 01042 case MSGTYPE_ALCHEMY: 01043 name = formula_book_name[RANDOM()%arraysize(formula_book_name)]; 01044 break; 01045 01046 case MSGTYPE_GODS: 01047 name = gods_book_name[RANDOM()%arraysize(gods_book_name)]; 01048 break; 01049 01050 case MSGTYPE_MSGFILE: 01051 default: 01052 if (book->weight > 2000) { /* based on weight */ 01053 name = heavy_book_name[RANDOM()%arraysize(heavy_book_name)]; 01054 } else { 01055 name = light_book_name[RANDOM()%arraysize(light_book_name)]; 01056 } 01057 break; 01058 } 01059 free_string(book->name); 01060 book->name = add_string(name); 01061 } 01062 01072 static void add_author(object *op, int msgtype) { 01073 char title[MAX_BUF]; 01074 const char *name; 01075 01076 if (msgtype < 0 || strlen(op->msg) < 5) 01077 return; 01078 01079 switch (msgtype) { 01080 case MSGTYPE_MONSTER: 01081 name = mon_author[RANDOM()%arraysize(mon_author)]; 01082 break; 01083 01084 case MSGTYPE_ARTIFACT: 01085 name = art_author[RANDOM()%arraysize(art_author)]; 01086 break; 01087 01088 case MSGTYPE_SPELLPATH: 01089 name = path_author[RANDOM()%arraysize(path_author)]; 01090 break; 01091 01092 case MSGTYPE_ALCHEMY: 01093 name = formula_author[RANDOM()%arraysize(formula_author)]; 01094 break; 01095 01096 case MSGTYPE_GODS: 01097 name = gods_author[RANDOM()%arraysize(gods_author)]; 01098 break; 01099 01100 case MSGTYPE_MSGFILE: 01101 default: 01102 name = book_author[RANDOM()%arraysize(book_author)]; 01103 } 01104 01105 snprintf(title, sizeof(title), "of %s", name); 01106 op->title = add_string(title); 01107 } 01108 01120 static int unique_book(const object *book, int msgtype) { 01121 title *test; 01122 01123 if (!booklist) 01124 return 1; /* No archival entries! Must be unique! */ 01125 01126 /* Go through the booklist. If the author and name match, not unique so 01127 * return 0. 01128 */ 01129 for (test = get_titlelist(msgtype)->first_book; test; test = test->next) { 01130 if (!strcmp(test->name, book->name) && !strcmp(book->title, test->authour)) 01131 return 0; 01132 } 01133 return 1; 01134 } 01135 01144 static void add_book_to_list(const object *book, int msgtype) { 01145 titlelist *tl = get_titlelist(msgtype); 01146 title *t; 01147 01148 if (!tl) { 01149 LOG(llevError, "add_book_to_list can't get booklist!\n"); 01150 return; 01151 } 01152 01153 t = get_empty_book(); 01154 t->name = add_string(book->name); 01155 t->authour = add_string(book->title); 01156 t->size = strlen(book->msg); 01157 t->msg_index = strtoint(book->msg); 01158 t->archname = add_string(book->arch->name); 01159 t->level = book->level; 01160 01161 t->next = tl->first_book; 01162 tl->first_book = t; 01163 tl->number++; 01164 01165 /* We have stuff we need to write now */ 01166 need_to_write_bookarchive = 1; 01167 01168 #ifdef ARCHIVE_DEBUG 01169 LOG(llevDebug, "Archiving new title: %s %s (%d)\n", book->name, book->title, msgtype); 01170 #endif 01171 } 01172 01186 static void change_book(object *book, int msgtype) { 01187 titlelist *tl; 01188 title *t; 01189 int tries; 01190 01191 if (book->type != BOOK) { 01192 LOG(llevError, "change_book_name() called w/ illegal obj type.\n"); 01193 return; 01194 } 01195 01196 tl = get_titlelist(msgtype); 01197 t = NULL; 01198 tries = 0; 01199 01200 /* look to see if our msg already been archived. If so, alter 01201 * the book to match the archival text. If we fail to match, 01202 * then we archive the new title/name/msg combo if there is 01203 * room on the titlelist. 01204 */ 01205 01206 if (strlen(book->msg) > 5 && (t = find_title(book, msgtype))) { 01207 object *tmpbook; 01208 01209 /* alter book properties */ 01210 tmpbook = create_archetype(t->archname); 01211 if (tmpbook->msg) 01212 free_string(tmpbook->msg); 01213 tmpbook->msg = add_string(book->msg); 01214 copy_object(tmpbook, book); 01215 free_object(tmpbook); 01216 01217 book->title = add_string(t->authour); 01218 free_string(book->name); 01219 book->name = add_string(t->name); 01220 book->level = t->level; 01221 } else { /* Don't have any default title, so lets make up a new one */ 01222 int numb, maxnames = max_titles[msgtype]; 01223 const char *old_title; 01224 const char *old_name; 01225 01226 old_title = book->title ? add_string(book->title) : NULL; 01227 old_name = add_string(book->name); 01228 01229 /* some pre-generated books have title already set (from 01230 * maps), also don't bother looking for unique title if 01231 * we already used up all the available names! */ 01232 01233 if (!tl) { 01234 LOG(llevError, "change_book_name(): can't find title list\n"); 01235 numb = 0; 01236 } else 01237 numb = tl->number; 01238 01239 if (numb == maxnames) { 01240 #ifdef ARCHIVE_DEBUG 01241 LOG(llevDebug, "titles for list %d full (%d possible).\n", msgtype, maxnames); 01242 #endif 01243 if (old_title != NULL) 01244 free_string(old_title); 01245 free_string(old_name); 01246 return; 01247 } 01248 /* shouldnt change map-maker books */ 01249 if (!book->title) 01250 do { 01251 /* random book name */ 01252 new_text_name(book, msgtype); 01253 add_author(book, msgtype); /* random author */ 01254 tries++; 01255 } while (!unique_book(book, msgtype) && tries < MAX_TITLE_CHECK); 01256 01257 /* Now deal with 2 cases. 01258 * 1) If no space for a new title exists lets just restore 01259 * the old book properties. Remember, if the book had 01260 * matchd an older entry on the titlelist, we shouldnt 01261 * have called this routine in the first place! 01262 * 2) If we got a unique title, we need to add it to 01263 * the list. 01264 */ 01265 01266 if (tries == MAX_TITLE_CHECK) { 01267 #ifdef ARCHIVE_DEBUG 01268 LOG(llevDebug, "Failed to obtain unique title for %s %s (names:%d/%d)\n", book->name, book->title, numb, maxnames); 01269 #endif 01270 /* restore old book properties here */ 01271 free_string(book->name); 01272 free_string(book->title); 01273 book->title = old_title != NULL ? add_string(old_title) : NULL; 01274 01275 if (RANDOM()%4) { 01276 /* Lets give the book a description to individualize it some */ 01277 char new_name[MAX_BUF]; 01278 01279 snprintf(new_name, MAX_BUF, "%s %s", book_descrpt[RANDOM()%arraysize(book_descrpt)], old_name); 01280 book->name = add_string(new_name); 01281 } else { 01282 book->name = add_string(old_name); 01283 } 01284 } else if (book->title && strlen(book->msg) > 5) { /* archive if long msg texts */ 01285 add_book_to_list(book, msgtype); 01286 } 01287 01288 if (old_title != NULL) 01289 free_string(old_title); 01290 free_string(old_name); 01291 } 01292 } 01293 01294 /***************************************************************************** 01295 * 01296 * This is the start of the area that generates the actual contents 01297 * of the book. 01298 * 01299 *****************************************************************************/ 01300 01301 /***************************************************************************** 01302 * Monster msg generation code. 01303 ****************************************************************************/ 01304 01316 object *get_random_mon(int level) { 01317 objectlink *mon; 01318 int i, monnr; 01319 01320 /* safety check. Problem w/ init_mon_info list? */ 01321 if (!nrofmon || !first_mon_info) 01322 return (object *)NULL; 01323 01324 if (!level) { 01325 /* lets get a random monster from the mon_info linked list */ 01326 monnr = RANDOM()%nrofmon; 01327 01328 for (mon = first_mon_info, i = 0; mon; mon = mon->next, i++) 01329 if (i == monnr) 01330 break; 01331 01332 if (!mon) { 01333 LOG(llevError, "get_random_mon: Didn't find a monster when we should have\n"); 01334 return NULL; 01335 } 01336 return mon->ob; 01337 } 01338 01339 /* Case where we are searching by level. Redone 971225 to be clearer 01340 * and more random. Before, it looks like it took a random monster from 01341 * the list, and then returned the first monster after that which was 01342 * appropriate level. This wasn't very random because if you had a 01343 * bunch of low level monsters and then a high level one, if the random 01344 * determine took one of the low level ones, it would just forward to the 01345 * high level one and return that. Thus, monsters that immediately followed 01346 * a bunch of low level monsters would be more heavily returned. It also 01347 * means some of the dragons would be poorly represented, since they 01348 * are a group of high level monsters all around each other. 01349 */ 01350 01351 /* First count number of monsters meeting level criteria */ 01352 for (mon = first_mon_info, i = 0; mon; mon = mon->next) 01353 if (mon->ob->level >= level) 01354 i++; 01355 01356 if (i == 0) { 01357 LOG(llevError, "get_random_mon() couldn't return monster for level %d\n", level); 01358 return NULL; 01359 } 01360 01361 monnr = RANDOM()%i; 01362 for (mon = first_mon_info; mon; mon = mon->next) 01363 if (mon->ob->level >= level && monnr-- == 0) 01364 return mon->ob; 01365 01366 LOG(llevError, "get_random_mon(): didn't find a monster when we should have\n"); 01367 return NULL; 01368 } 01369 01383 static char *mon_desc(const object *mon, char *buf, size_t size) { 01384 snprintf(buf, size, " *** %s ***\n", mon->name); 01385 describe_item(mon, NULL, buf+strlen(buf), size-strlen(buf)); 01386 return buf; 01387 } 01388 01400 static object *get_next_mon(const object *tmp) { 01401 objectlink *mon; 01402 01403 for (mon = first_mon_info; mon; mon = mon->next) 01404 if (mon->ob == tmp) 01405 break; 01406 01407 /* didn't find a match */ 01408 if (!mon) 01409 return NULL; 01410 if (mon->next) 01411 return mon->next->ob; 01412 else 01413 return first_mon_info->ob; 01414 } 01415 01429 static char *mon_info_msg(int level, char *buf, size_t booksize) { 01430 char tmpbuf[HUGE_BUF], desc[MAX_BUF]; 01431 object *tmp; 01432 01433 /*preamble */ 01434 snprintf(buf, booksize, "This beastiary contains:"); 01435 01436 /* lets print info on as many monsters as will fit in our 01437 * document. 01438 * 8-96 Had to change this a bit, otherwise there would 01439 * have been an impossibly large number of combinations 01440 * of text! (and flood out the available number of titles 01441 * in the archive in a snap!) -b.t. 01442 */ 01443 for (tmp = get_random_mon(level*3); tmp; tmp = get_next_mon(tmp)) { 01444 /* monster description */ 01445 snprintf(tmpbuf, sizeof(tmpbuf), "\n---\n%s", mon_desc(tmp, desc, sizeof(desc))); 01446 01447 if (book_overflow(buf, tmpbuf, booksize)) 01448 break; 01449 snprintf(buf+strlen(buf), booksize-strlen(buf), "%s", tmpbuf); 01450 } 01451 01452 #ifdef BOOK_MSG_DEBUG 01453 LOG(llevDebug, "\n mon_info_msg() created strng: %d\n", strlen(retbuf)); 01454 fprintf(logfile, " MADE THIS:\n%s\n", retbuf); 01455 #endif 01456 01457 return buf; 01458 } 01459 01460 /***************************************************************************** 01461 * Artifact msg generation code. 01462 ****************************************************************************/ 01463 01477 static char *artifact_msg(int level, char *retbuf, size_t booksize) { 01478 artifactlist *al; 01479 artifact *art; 01480 int chance, i, type, index; 01481 int book_entries = level > 5 ? RANDOM()%3+RANDOM()%3+2 : RANDOM()%level+1; 01482 char buf[BOOK_BUF], sbuf[MAX_BUF]; 01483 object *tmp; 01484 01485 /* values greater than 5 create msg buffers that are too big! */ 01486 if (book_entries > 5) 01487 book_entries = 5; 01488 01489 /* lets determine what kind of artifact type randomly. 01490 * Right now legal artifacts only come from those listed 01491 * in art_name_array. Also, we check to be sure an artifactlist 01492 * for that type exists! 01493 */ 01494 i = 0; 01495 do { 01496 index = RANDOM()%arraysize(art_name_array); 01497 type = art_name_array[index].type; 01498 al = find_artifactlist(type); 01499 i++; 01500 } while (al == NULL && i < 10); 01501 01502 if (i == 10) { /* Unable to find a message */ 01503 snprintf(retbuf, booksize, "None"); 01504 return retbuf; 01505 } 01506 01507 /* There is no reason to start on the artifact list at the beginning. Lets 01508 * take our starting position randomly... */ 01509 art = al->items; 01510 for (i = RANDOM()%level+RANDOM()%2+1; i > 0; i--) { 01511 if (art == NULL) 01512 art = al->items; /* hmm, out of stuff, loop back around */ 01513 art = art->next; 01514 } 01515 01516 /* Ok, lets print out the contents */ 01517 snprintf(retbuf, booksize, "Herein %s detailed %s...\n", book_entries > 1 ? "are" : "is", book_entries > 1 ? "some artifacts" : "an artifact"); 01518 01519 /* artifact msg attributes loop. Lets keep adding entries to the 'book' 01520 * as long as we have space up to the allowed max # (book_entires) 01521 */ 01522 while (book_entries > 0) { 01523 if (art == NULL) 01524 art = al->items; 01525 01526 /* separator of items */ 01527 snprintf(buf, sizeof(buf), "---\n"); 01528 01529 /* Name */ 01530 if (art->allowed != NULL && strcmp(art->allowed->name, "All")) { 01531 linked_char *temp, *next = art->allowed; 01532 01533 do { 01534 temp = next; 01535 next = next->next; 01536 } while (next != (linked_char *)NULL && RANDOM()%2); 01537 snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), " A %s of %s", temp->name, art->item->name); 01538 } else { /* default name is used */ 01539 /* use the base 'generic' name for our artifact */ 01540 snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), " The %s of %s", art_name_array[index].name, art->item->name); 01541 } 01542 01543 /* chance of finding */ 01544 chance = 100*((float)art->chance/al->total_chance); 01545 if (chance >= 20) 01546 snprintf(sbuf, sizeof(sbuf), "an uncommon"); 01547 else if (chance >= 10) 01548 snprintf(sbuf, sizeof(sbuf), "an unusual"); 01549 else if (chance >= 5) 01550 snprintf(sbuf, sizeof(sbuf), "a rare"); 01551 else 01552 snprintf(sbuf, sizeof(sbuf), "a very rare"); 01553 snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), " is %s\n", sbuf); 01554 01555 /* value of artifact */ 01556 snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), " item with a value that is %d times normal.\n", 01557 art->item->value); 01558 01559 /* include the message about the artifact, if exists, and book 01560 * level is kinda high */ 01561 if (art->item->msg && RANDOM()%4+1 < level 01562 && !(strlen(art->item->msg)+strlen(buf) > BOOK_BUF)) 01563 snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%s", art->item->msg); 01564 01565 /* properties of the artifact */ 01566 tmp = get_object(); 01567 add_abilities(tmp, art->item); 01568 tmp->type = type; 01569 SET_FLAG(tmp, FLAG_IDENTIFIED); 01570 describe_item(tmp, NULL, sbuf, sizeof(sbuf)); 01571 if (strlen(sbuf) > 1) 01572 snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), " Properties of this artifact include:\n %s\n", sbuf); 01573 free_object(tmp); 01574 /* add the buf if it will fit */ 01575 if (book_overflow(retbuf, buf, booksize)) 01576 break; 01577 snprintf(retbuf+strlen(retbuf), booksize-strlen(retbuf), "%s", buf); 01578 01579 art = art->next; 01580 book_entries--; 01581 } 01582 01583 #ifdef BOOK_MSG_DEBUG 01584 LOG(llevDebug, "artifact_msg() created strng: %d\n", strlen(retbuf)); 01585 fprintf(logfile, " MADE THIS:\n%s", retbuf); 01586 #endif 01587 return retbuf; 01588 } 01589 01590 /***************************************************************************** 01591 * Spellpath message generation 01592 *****************************************************************************/ 01593 01607 static char *spellpath_msg(int level, char *retbuf, size_t booksize) { 01608 int path = RANDOM()%NRSPELLPATHS, prayers = RANDOM()%2; 01609 int did_first_sp = 0; 01610 uint32 pnum = spellpathdef[path]; 01611 archetype *at; 01612 01613 /* Preamble */ 01614 snprintf(retbuf, booksize, "Herein are detailed the names of %s\n", prayers ? "prayers" : "incantations"); 01615 01616 snprintf(retbuf+strlen(retbuf), booksize-strlen(retbuf), "belonging to the path of %s:\n", spellpathnames[path]); 01617 01618 for (at = first_archetype; at != NULL; at = at->next) { 01619 /* Determine if this is an appropriate spell. Must 01620 * be of matching path, must be of appropriate type (prayer 01621 * or not), and must be within the valid level range. 01622 */ 01623 if (at->clone.type == SPELL 01624 && at->clone.path_attuned&pnum 01625 && ((at->clone.stats.grace && prayers) || (at->clone.stats.sp && !prayers)) 01626 && at->clone.level < level*8) { 01627 if (book_overflow(retbuf, at->clone.name, booksize)) 01628 break; 01629 01630 if (did_first_sp) 01631 snprintf(retbuf+strlen(retbuf), booksize-strlen(retbuf), ",\n"); 01632 did_first_sp = 1; 01633 snprintf(retbuf+strlen(retbuf), booksize-strlen(retbuf), "%s", at->clone.name); 01634 } 01635 } 01636 /* Geez, no spells were generated. */ 01637 if (!did_first_sp) { 01638 if (RANDOM()%4) /* usually, lets make a recursive call... */ 01639 return spellpath_msg(level, retbuf, booksize); 01640 /* give up, cause knowing no spells exist for path is info too. */ 01641 snprintf(retbuf+strlen(retbuf), booksize-strlen(retbuf), "\n - no known spells exist -\n"); 01642 } else { 01643 snprintf(retbuf+strlen(retbuf), booksize-strlen(retbuf), "\n"); 01644 } 01645 return retbuf; 01646 } 01647 01656 static void make_formula_book(object *book, int level) { 01657 char retbuf[BOOK_BUF], title[MAX_BUF]; 01658 recipelist *fl; 01659 recipe *formula; 01660 int chance; 01661 const char *op_name; 01662 archetype *at; 01663 01664 /* the higher the book level, the more complex (ie number of 01665 * ingredients) the formula can be. 01666 */ 01667 fl = get_formulalist((RANDOM()%level)/3+1); 01668 if (!fl) 01669 fl = get_formulalist(1); /* safety */ 01670 01671 if (fl->total_chance == 0) { 01672 book->msg = add_string(" <indecipherable text>\n"); 01673 new_text_name(book, MSGTYPE_ALCHEMY); 01674 add_author(book, MSGTYPE_ALCHEMY); 01675 return; 01676 } 01677 01678 /* get a random formula, weighted by its bookchance */ 01679 chance = RANDOM()%fl->total_chance; 01680 for (formula = fl->items; formula != NULL; formula = formula->next) { 01681 chance -= formula->chance; 01682 if (chance <= 0) 01683 break; 01684 } 01685 01686 if (!formula || formula->arch_names <= 0) { 01687 book->msg = add_string(" <indecipherable text>\n"); 01688 new_text_name(book, MSGTYPE_ALCHEMY); 01689 add_author(book, MSGTYPE_ALCHEMY); 01690 return; 01691 } 01692 01693 /* looks like a formula was found. Base the amount 01694 * of information on the booklevel and the spellevel 01695 * of the formula. */ 01696 01697 /* preamble */ 01698 snprintf(retbuf, sizeof(retbuf), "Herein is described a project using %s:\n", formula->skill ? formula->skill : "an unknown skill"); 01699 01700 op_name = formula->arch_name[RANDOM()%formula->arch_names]; 01701 at = find_archetype(op_name); 01702 if (at == (archetype *)NULL) { 01703 LOG(llevError, "formula_msg() can't find arch %s for formula.\n", op_name); 01704 book->msg = add_string(" <indecipherable text>\n"); 01705 new_text_name(book, MSGTYPE_ALCHEMY); 01706 add_author(book, MSGTYPE_ALCHEMY); 01707 return; 01708 } 01709 op_name = at->clone.name; 01710 01711 /* item name */ 01712 if (strcmp(formula->title, "NONE")) { 01713 snprintf(retbuf+strlen(retbuf), sizeof(retbuf)-strlen(retbuf), "The %s of %s", op_name, formula->title); 01714 /* This results in things like pile of philo. sulfur. 01715 * while philo. sulfur may look better, without this, 01716 * you get things like 'the wise' because its missing the 01717 * water of section. 01718 */ 01719 snprintf(title, sizeof(title), "%s: %s of %s", formula_book_name[RANDOM()%arraysize(formula_book_name)], op_name, formula->title); 01720 } else { 01721 snprintf(retbuf+strlen(retbuf), sizeof(retbuf)-strlen(retbuf), "The %s", op_name); 01722 snprintf(title, sizeof(title), "%s: %s", formula_book_name[RANDOM()%arraysize(formula_book_name)], op_name); 01723 if (at->clone.title) { 01724 snprintf(retbuf+strlen(retbuf), sizeof(retbuf)-strlen(retbuf), " %s", at->clone.title); 01725 snprintf(title+strlen(title), sizeof(title)-strlen(title), " %s", at->clone.title); 01726 } 01727 } 01728 /* Lets name the book something meaningful ! */ 01729 if (book->name) 01730 free_string(book->name); 01731 book->name = add_string(title); 01732 if (book->title) { 01733 free_string(book->title); 01734 book->title = NULL; 01735 } 01736 01737 /* ingredients to make it */ 01738 if (formula->ingred != NULL) { 01739 linked_char *next; 01740 archetype *at; 01741 char name[MAX_BUF]; 01742 01743 at = find_archetype(formula->cauldron); 01744 if (at) 01745 query_name(&at->clone, name, MAX_BUF); 01746 else 01747 snprintf(name, sizeof(name), "an unknown place"); 01748 01749 snprintf(retbuf+strlen(retbuf), sizeof(retbuf)-strlen(retbuf), 01750 " may be made at %s using the following ingredients:\n", name); 01751 01752 for (next = formula->ingred; next != NULL; next = next->next) { 01753 snprintf(retbuf+strlen(retbuf), sizeof(retbuf)-strlen(retbuf), "%s\n", next->name); 01754 } 01755 } else 01756 LOG(llevError, "formula_msg() no ingredient list for object %s of %s\n", op_name, formula->title); 01757 if (retbuf[strlen(retbuf)-1] != '\n') 01758 snprintf(retbuf+strlen(retbuf), sizeof(retbuf)-strlen(retbuf), "\n"); 01759 if (book->msg) 01760 free_string(book->msg); 01761 book->msg = add_string(retbuf); 01762 } 01763 01774 static char *msgfile_msg(int level, size_t booksize) { 01775 static char retbuf[BOOK_BUF]; 01776 int i, msgnum; 01777 linked_char *msg = NULL; 01778 01779 /* get a random message for the 'book' from linked list */ 01780 if (nrofmsg > 1) { 01781 msg = first_msg; 01782 msgnum = RANDOM()%nrofmsg; 01783 for (i = 0; msg && i < nrofmsg && i != msgnum; i++) 01784 msg = msg->next; 01785 } 01786 01787 if (msg && !book_overflow(retbuf, msg->name, booksize)) 01788 snprintf(retbuf, sizeof(retbuf), "%s", msg->name); 01789 else 01790 snprintf(retbuf, sizeof(retbuf), "\n <undecipherable text>"); 01791 01792 #ifdef BOOK_MSG_DEBUG 01793 LOG(llevDebug, "\n info_list_msg() created strng: %d\n", strlen(retbuf)); 01794 LOG(llevDebug, " MADE THIS:\n%s\n", retbuf); 01795 #endif 01796 01797 return retbuf; 01798 } 01799 01813 static char *god_info_msg(int level, char *retbuf, size_t booksize) { 01814 const char *name; 01815 char buf[BOOK_BUF]; 01816 int i; 01817 size_t retlen, buflen; 01818 size_t introlen; 01819 const object *god = pntr_to_god_obj(get_rand_god()); 01820 char en[BOOK_BUF]; 01821 01822 if (booksize > BOOK_BUF) { 01823 LOG(llevError, "common/readable.c:god_info_msg() - passed in booksize (%lu) is larger than book buffer (%d)\n", (unsigned long)booksize, BOOK_BUF); 01824 booksize = BOOK_BUF; 01825 } 01826 01827 if (!god) 01828 return (char *)NULL; /* oops, problems... */ 01829 name = god->name; 01830 01831 /* preamble.. */ 01832 snprintf(retbuf, BOOK_BUF, "This document contains knowledge concerning the diety %s", name); 01833 01834 retlen = strlen(retbuf); 01835 01836 /* Always have as default information the god's descriptive terms. */ 01837 if (nstrtok(god->msg, ",") > 0) { 01838 safe_strcat(retbuf, ", known as", &retlen, BOOK_BUF); 01839 safe_strcat(retbuf, strtoktolin(god->msg, ",", buf, sizeof(buf)), &retlen, BOOK_BUF); 01840 } else 01841 safe_strcat(retbuf, "...", &retlen, BOOK_BUF); 01842 01843 safe_strcat(retbuf, "\n ---\n", &retlen, BOOK_BUF); 01844 01845 introlen = retlen; /* so we will know if no new info is added later */ 01846 01847 /* Information about the god is random, and based on the level of the 01848 * 'book'. This goes through this loop 'level' times, reducing level by 01849 * 1 each time. If the info provided is filled up, we exit the loop. 01850 * otherwise, buf is appended to the existing book buffer. 01851 */ 01852 01853 while (level > 0) { 01854 buf[0]=' '; 01855 buf[1]='\0'; 01856 if (level == 2 && RANDOM()%2) { 01857 /* enemy god */ 01858 01859 if (god->title) 01860 snprintf(buf, BOOK_BUF, "The gods %s and %s are enemies.\n ---\n", name, god->title); 01861 } 01862 if (level == 3 && RANDOM()%2) { 01863 /* enemy race, what the god's holy word effects */ 01864 const char *enemy = god->slaying; 01865 01866 if (enemy && !(god->path_denied&PATH_TURNING) 01867 && (i = nstrtok(enemy, ",")) > 0) { 01868 char tmpbuf[MAX_BUF]; 01869 01870 snprintf(buf, BOOK_BUF, "The holy words of %s have the power to slay creatures belonging to the ", name); 01871 if (i > 1) 01872 snprintf(tmpbuf, MAX_BUF, "following races:%s\n ---\n", strtoktolin(enemy, ",", en, sizeof(en))); 01873 else 01874 snprintf(tmpbuf, MAX_BUF, "race of%s\n ---\n", strtoktolin(enemy, ",", en, sizeof(en))); 01875 01876 buflen = strlen(buf); 01877 safe_strcat(buf, tmpbuf, &buflen, BOOK_BUF); 01878 } 01879 } 01880 if (level == 4 && RANDOM()%2) { 01881 /* Priest of god gets these protect,vulnerable... */ 01882 01883 char cp[BOOK_BUF]; 01884 describe_resistance(god, 1, cp, BOOK_BUF); 01885 01886 if (*cp) { /* This god does have protections */ 01887 snprintf(buf, BOOK_BUF, "%s has a potent aura which is extended to faithful priests. The effects of this aura include:\n%s\n ---\n", name, cp); 01888 } 01889 } 01890 if (level == 5 && RANDOM()%2) { 01891 /* aligned race, summoning */ 01892 const char *race = god->race; /* aligned race */ 01893 01894 if (race && !(god->path_denied&PATH_SUMMON)) { 01895 i = nstrtok(race, ","); 01896 if (i > 0) { 01897 char tmpbuf[MAX_BUF]; 01898 01899 snprintf(buf, BOOK_BUF, "Creatures sacred to %s include the\n", name); 01900 01901 if (i > 1) 01902 snprintf(tmpbuf, MAX_BUF, "following races:%s\n ---\n", strtoktolin(race, ",", en, sizeof(en))); 01903 else 01904 snprintf(tmpbuf, MAX_BUF, "race of %s\n ---\n", strtoktolin(race, ",", en, sizeof(en))); 01905 01906 buflen = strlen(buf); 01907 safe_strcat(buf, tmpbuf, &buflen, BOOK_BUF); 01908 } 01909 } 01910 } 01911 if (level == 6 && RANDOM()%2) { 01912 /* blessing,curse properties of the god */ 01913 01914 char cp[MAX_BUF]; 01915 describe_resistance(god, 1, cp, MAX_BUF); 01916 01917 if (*cp) { /* This god does have protections */ 01918 snprintf(buf, MAX_BUF, "\nThe priests of %s are known to be able to bestow a blessing which makes the recipient %s\n ---\n", name, cp); 01919 } 01920 } 01921 if (level == 8 && RANDOM()%2) { 01922 /* immunity, holy possession */ 01923 int has_effect = 0, tmpvar; 01924 char tmpbuf[MAX_BUF]; 01925 01926 snprintf(buf, MAX_BUF, "\nThe priests of %s are known to make cast a mighty prayer of possession which gives the recipient", name); 01927 01928 for (tmpvar = 0; tmpvar < NROFATTACKS; tmpvar++) { 01929 if (god->resist[tmpvar] == 100) { 01930 has_effect = 1; 01931 snprintf(tmpbuf, MAX_BUF, "Immunity to %s", attacktype_desc[tmpvar]); 01932 } 01933 } 01934 if (has_effect) { 01935 buflen = strlen(buf); 01936 safe_strcat(buf, tmpbuf, &buflen, BOOK_BUF); 01937 safe_strcat(buf, "\n ---\n", &buflen, BOOK_BUF); 01938 } else { 01939 buf[0]=' '; 01940 buf[1]='\0'; 01941 } 01942 } 01943 if (level == 12 && RANDOM()%2) { 01944 /* spell paths */ 01945 int has_effect = 0; 01946 01947 snprintf(buf, MAX_BUF, "It is rarely known fact that the priests of %s are mystically transformed. Effects of this include:\n", name); 01948 buflen = strlen(buf); 01949 01950 if (god->path_attuned) { 01951 has_effect = 1; 01952 DESCRIBE_PATH_SAFE(buf, god->path_attuned, "Attuned", &buflen, BOOK_BUF); 01953 } 01954 if (god->path_repelled) { 01955 has_effect = 1; 01956 DESCRIBE_PATH_SAFE(buf, god->path_repelled, "Repelled", &buflen, BOOK_BUF); 01957 } 01958 if (god->path_denied) { 01959 has_effect = 1; 01960 DESCRIBE_PATH_SAFE(buf, god->path_denied, "Denied", &buflen, BOOK_BUF); 01961 } 01962 if (has_effect) { 01963 safe_strcat(buf, "\n ---\n", &buflen, BOOK_BUF); 01964 } else { 01965 buf[0]=' '; 01966 buf[1]='\0'; 01967 } 01968 } 01969 01970 /* check to be sure new buffer size dont exceed either 01971 * the maximum buffer size, or the 'natural' size of the 01972 * book... 01973 */ 01974 if (book_overflow(retbuf, buf, booksize)) 01975 break; 01976 if (strlen(buf) > 1) 01977 safe_strcat(retbuf, buf, &retlen, BOOK_BUF); 01978 01979 level--; 01980 } 01981 if (retlen == introlen) { 01982 /* we got no information beyond the preamble! */ 01983 safe_strcat(retbuf, " Unfortunately the rest of the information is hopelessly garbled!\n ---\n", &retlen, BOOK_BUF); 01984 } 01985 #ifdef BOOK_MSG_DEBUG 01986 LOG(llevDebug, "\n god_info_msg() created strng: %d\n", strlen(retbuf)); 01987 fprintf(logfile, " MADE THIS:\n%s", retbuf); 01988 #endif 01989 return retbuf; 01990 } 01991 02010 void tailor_readable_ob(object *book, int msg_type) { 02011 char msgbuf[BOOK_BUF]; 02012 int level = book->level ? RANDOM()%book->level+1 : 1; 02013 size_t book_buf_size; 02014 02015 /* safety */ 02016 if (book->type != BOOK) 02017 return; 02018 02019 if (level <= 0) 02020 return; /* if no level no point in doing any more... */ 02021 02022 /* Max text length this book can have. */ 02023 book_buf_size = BOOKSIZE(book); 02024 book_buf_size -= strlen("\n"); /* Keep enough for final \n. */ 02025 02026 /* &&& The message switch &&& */ 02027 /* Below all of the possible types of messages in the "book"s. 02028 */ 02029 /* 02030 * IF you add a new type of book msg, you will have to do several things. 02031 * 1) make sure there is an entry in the msg switch below! 02032 * 2) make sure there is an entry in max_titles[] array. 02033 * 3) make sure there are entries for your case in new_text_title() 02034 * and add_authour(). 02035 * 4) you may want separate authour/book name arrays in read.h 02036 */ 02037 02038 msg_type = msg_type > 0 ? msg_type : RANDOM()%6; 02039 switch (msg_type) { 02040 case MSGTYPE_MONSTER: 02041 mon_info_msg(level, msgbuf, book_buf_size); 02042 break; 02043 02044 case MSGTYPE_ARTIFACT: 02045 artifact_msg(level, msgbuf, book_buf_size); 02046 break; 02047 02048 case MSGTYPE_SPELLPATH: /* grouping incantations/prayers by path */ 02049 spellpath_msg(level, msgbuf, book_buf_size); 02050 break; 02051 02052 case MSGTYPE_ALCHEMY: /* describe an alchemy formula */ 02053 make_formula_book(book, level); 02054 /* make_formula_book already gives title */ 02055 return; 02056 break; 02057 02058 case MSGTYPE_GODS: /* bits of information about a god */ 02059 god_info_msg(level, msgbuf, book_buf_size); 02060 break; 02061 02062 case MSGTYPE_LIB: /* use info list in lib/ */ 02063 default: 02064 strcpy(msgbuf, msgfile_msg(level, book_buf_size)); 02065 break; 02066 } 02067 02068 strcat(msgbuf, "\n"); /* safety -- we get ugly map saves/crashes w/o this */ 02069 if (strlen(msgbuf) > strlen("\n")) { 02070 if (book->msg) 02071 free_string(book->msg); 02072 book->msg = add_string(msgbuf); 02073 /* lets give the "book" a new name, which may be a compound word */ 02074 change_book(book, msg_type); 02075 } 02076 } 02077 02078 /***************************************************************************** 02079 * 02080 * Cleanup routine for readable stuff. 02081 * 02082 *****************************************************************************/ 02083 02084 void free_all_readable(void) { 02085 titlelist *tlist, *tnext; 02086 title *title1, *titlenext; 02087 linked_char *lmsg, *nextmsg; 02088 objectlink *monlink, *nextmon; 02089 02090 LOG(llevDebug, "freeing all book information\n"); 02091 02092 for (tlist = booklist; tlist != NULL; tlist = tnext) { 02093 tnext = tlist->next; 02094 for (title1 = tlist->first_book; title1; title1 = titlenext) { 02095 titlenext = title1->next; 02096 if (title1->name) 02097 free_string(title1->name); 02098 if (title1->authour) 02099 free_string(title1->authour); 02100 if (title1->archname) 02101 free_string(title1->archname); 02102 free(title1); 02103 } 02104 free(tlist); 02105 } 02106 for (lmsg = first_msg; lmsg; lmsg = nextmsg) { 02107 nextmsg = lmsg->next; 02108 if (lmsg->name) 02109 free_string(lmsg->name); 02110 free(lmsg); 02111 } 02112 for (monlink = first_mon_info; monlink; monlink = nextmon) { 02113 nextmon = monlink->next; 02114 free(monlink); 02115 } 02116 } 02117 02118 /***************************************************************************** 02119 * 02120 * Writeback routine for updating the bookarchive. 02121 * 02122 ****************************************************************************/ 02123 02127 void write_book_archive(void) { 02128 FILE *fp; 02129 int index; 02130 char fname[MAX_BUF]; 02131 title *book; 02132 titlelist *bl; 02133 02134 /* If nothing changed, don't write anything */ 02135 if (!need_to_write_bookarchive) 02136 return; 02137 02138 snprintf(fname, sizeof(fname), "%s/bookarch", settings.localdir); 02139 LOG(llevDebug, "Updating book archive: %s...\n", fname); 02140 02141 fp = fopen(fname, "w"); 02142 if (fp == NULL) { 02143 LOG(llevDebug, "Can't open book archive file %s\n", fname); 02144 return; 02145 } 02146 02147 for (bl = get_titlelist(0), index = 0; bl; bl = bl->next, index++) { 02148 for (book = bl->first_book; book; book = book->next) 02149 if (book && book->authour) { 02150 fprintf(fp, "title %s\n", book->name); 02151 fprintf(fp, "authour %s\n", book->authour); 02152 fprintf(fp, "arch %s\n", book->archname); 02153 fprintf(fp, "level %d\n", book->level); 02154 fprintf(fp, "type %d\n", index); 02155 /* C89 doesn't have %zu... */ 02156 fprintf(fp, "size %lu\n", (unsigned long)book->size); 02157 fprintf(fp, "index %d\n", book->msg_index); 02158 fprintf(fp, "end\n"); 02159 } 02160 } 02161 if (ferror(fp)) { 02162 LOG(llevError, "Error during book archive save.\n"); 02163 fclose(fp); 02164 return; 02165 } 02166 if (fclose(fp) != 0) { 02167 LOG(llevError, "Error during book archive save.\n"); 02168 return; 02169 } 02170 chmod(fname, SAVE_MODE); 02171 need_to_write_bookarchive = 0; 02172 } 02173 02181 const readable_message_type *get_readable_message_type(object *readable) { 02182 uint8 subtype = readable->subtype; 02183 02184 if (subtype > last_readable_subtype) 02185 return &readable_message_types[0]; 02186 return &readable_message_types[subtype]; 02187 }