Crossfire Server, Branch 1.12  R12190
readable.c
Go to the documentation of this file.
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 }