ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/readable.C
Revision: 1.5
Committed: Sun Sep 10 16:00:23 2006 UTC (17 years, 8 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.4: +1437 -1436 lines
Log Message:
indent

File Contents

# User Rev Content
1 root 1.5
2 elmex 1.1 /*
3     * static char *rcsid_readable_c =
4 root 1.5 * "$Id: readable.C,v 1.4 2006-09-03 14:33:47 elmex Exp $";
5 elmex 1.1 */
6    
7     /*
8     CrossFire, A Multiplayer game for X-windows
9    
10     Copyright (C) 2002 Mark Wedel & Crossfire Development Team
11     Copyright (C) 1992 Frank Tore Johansen
12    
13     This program is free software; you can redistribute it and/or modify
14     it under the terms of the GNU General Public License as published by
15     the Free Software Foundation; either version 2 of the License, or
16     (at your option) any later version.
17    
18     This program is distributed in the hope that it will be useful,
19     but WITHOUT ANY WARRANTY; without even the implied warranty of
20     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21     GNU General Public License for more details.
22    
23     You should have received a copy of the GNU General Public License
24     along with this program; if not, write to the Free Software
25     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26    
27     The authors can be reached via e-mail at crossfire-devel@real-time.com
28     */
29    
30    
31     /* This file contains code relevant to the BOOKS hack -- designed
32     * to allow randomly occuring messages in non-magical texts.
33     */
34    
35     /* laid down initial file - dec 1995. -b.t. thomas@astro.psu.edu */
36    
37     #include <global.h>
38     #include <book.h>
39     #include <living.h>
40     #include <spells.h>
41    
42    
43     /* Define this if you want to archive book titles by contents.
44     * This option should enforce UNIQUE combinations of titles,authors and
45     * msg contents during and *between* game sessions.
46     * Note: a slight degeneracy exists since books are archived based on an integer
47     * index value calculated from the message text (similar to alchemy formulae).
48     * Sometimes two widely different messages have the same index value (rare). In
49     * this case, it is possible to occasionally generate 2 books with same title and
50     * different message content. Not really a bug, but rather a feature. This action
51     * should keeps player on their toes ;).
52     * Also, note that there is *finite* space available for archiving message and titles.
53     * Once this space is used, books will stop being archived. Not a serious problem
54     * under the current regime, since there are generally fewer possible (random)
55     * messages than space available on the titlelists.
56     * One exception (for sure) are the monster messages. But no worries, you should
57     * see all of the monster info in some order (but not all possble combinations)
58     * before the monster titlelist space is run out. You can increase titlelist
59     * space by increasing the array sizes for the monster book_authours and book_names
60     * (see max_titles[] array and include/read.h). Since the unique_book algorthm is
61     * kinda stupid, this program *may* slow down program execution if defined (but I don't
62     * think its a significant problem, at least, I have no problems running this option
63     * on a Sparc 10! Also, once archive title lists are filled and/or all possible msg
64     * combinations have been generated, unique_book isnt called anymore. It takes 5-10
65     * sessions for this to happen).
66     * Final note: the game remembers book/title/msg combinations from reading the
67     * file lib/bookarch. If you REMOVE this file, you will lose your archive. So
68     * be sure to copy it over to the new lib directory when you change versions.
69     * -b.t.
70     */
71    
72     /* This flag is useful to see what kind of output messages are created */
73 root 1.5
74 elmex 1.1 /* #define BOOK_MSG_DEBUG */
75    
76     /* This flag is useful for debuging archiving action */
77 root 1.5
78 elmex 1.1 /* #define ARCHIVE_DEBUG */
79    
80     /* Moved these structures from struct.h to this file in 0.94.3 - they
81     * are not needed anyplace else, so why have them globally declared?
82     */
83    
84     /* 'title' and 'titlelist' are used by the readable code */
85 root 1.5 typedef struct titlestruct:zero_initialised
86 root 1.3 {
87 root 1.5 shstr name; /* the name of the book */
88     shstr authour; /* the name of the book authour */
89     shstr archname; /* the archetype name of the book */
90     int level; /* level of difficulty of this message */
91     int size; /* size of the book message */
92     int msg_index; /* an index value derived from book message */
93     struct titlestruct *next;
94 elmex 1.1 } title;
95    
96 root 1.5 typedef struct titleliststruct:zero_initialised
97 root 1.3 {
98 root 1.5 int number; /* number of items in the list */
99     struct titlestruct *first_book; /* pointer to first book in this list */
100     struct titleliststruct *next; /* pointer to next book list */
101 elmex 1.1 } titlelist;
102    
103     /* special structure, used only by art_name_array[] */
104    
105     typedef struct namebytype
106 root 1.5 {
107     const char *name; /* generic name to call artifacts of this type */
108     int type; /* matching type */
109     }
110 elmex 1.1 arttypename;
111    
112    
113     /* booklist is the buffer of books read in from the bookarch file */
114     static titlelist *booklist = NULL;
115    
116     static objectlink *first_mon_info = NULL;
117    
118     /* these are needed for creation of a linked list of
119     * pointers to all (hostile) monster objects */
120    
121 root 1.5 static int nrofmon = 0, need_to_write_bookarchive = 0;
122 elmex 1.1
123    
124     /* this is needed to keep track of status of initialization
125     * of the message file */
126     static int nrofmsg = 0;
127    
128     /* first_msg is the started of the linked list of messages as read from
129     * the messages file
130     */
131     static linked_char *first_msg = NULL;
132    
133     /*
134     * Spellpath information
135     */
136    
137 root 1.5 static uint32 spellpathdef[NRSPELLPATHS] = {
138     PATH_PROT,
139     PATH_FIRE,
140     PATH_FROST,
141     PATH_ELEC,
142     PATH_MISSILE,
143     PATH_SELF,
144     PATH_SUMMON,
145     PATH_ABJURE,
146     PATH_RESTORE,
147     PATH_DETONATE,
148     PATH_MIND,
149     PATH_CREATE,
150     PATH_TELE,
151     PATH_INFO,
152     PATH_TRANSMUTE,
153     PATH_TRANSFER,
154     PATH_TURNING,
155     PATH_WOUNDING,
156     PATH_DEATH,
157     PATH_LIGHT
158 elmex 1.1 };
159    
160 root 1.5 static const char *const path_book_name[] = {
161     "codex",
162     "compendium",
163     "exposition",
164     "tables",
165     "treatise"
166 elmex 1.1 };
167    
168     /* used by spellpath texts */
169 root 1.5 static const char *const path_author[] = {
170     "aether",
171     "astral byways",
172     "connections",
173     "the Grey Council",
174     "deep pathways",
175     "knowledge",
176     "magic",
177     "mystic ways",
178     "pathways",
179     "power",
180     "spells",
181     "transforms",
182     "the mystic veil",
183     "unknown spells"
184 elmex 1.1 };
185    
186     /*
187     * Artiface/item information
188     */
189    
190     /* if it isnt listed here, then art_attr_msg will never generate
191     * a message for this type of artifact. -b.t. */
192    
193 root 1.5 static arttypename art_name_array[] = {
194     {"Helmet", HELMET},
195     {"Amulet", AMULET},
196     {"Shield", SHIELD},
197     {"Bracers", BRACERS},
198     {"Boots", BOOTS},
199     {"Cloak", CLOAK},
200     {"Gloves", GLOVES},
201     {"Gridle", GIRDLE},
202     {"Ring", RING},
203     {"Horn", HORN},
204     {"Missile Weapon", BOW},
205     {"Missile", ARROW},
206     {"Hand Weapon", WEAPON},
207     {"Artifact", SKILL},
208     {"Food", FOOD},
209     {"Body Armour", ARMOUR}
210 elmex 1.1 };
211    
212 root 1.5 static const char *const art_book_name[] = {
213     "collection",
214     "file",
215     "files",
216     "guide",
217     "handbook",
218     "index",
219     "inventory",
220     "list",
221     "listing",
222     "record",
223     "record book"
224 elmex 1.1 };
225    
226     /* used by artifact texts */
227 root 1.5 static const char *const art_author[] = {
228     "ancient things",
229     "artifacts",
230     "Havlor", /* ancient warrior scribe :) */
231     "items",
232     "lost artifacts",
233     "the ancients",
234     "useful things"
235 elmex 1.1 };
236    
237     /*
238     * Monster book information
239     */
240    
241 root 1.5 static const char *const mon_book_name[] = {
242     "beastuary",
243     "catalog",
244     "compilation",
245     "collection",
246     "encyclopedia",
247     "guide",
248     "handbook",
249     "list",
250     "manual",
251     "notes",
252     "record",
253     "register",
254     "volume"
255 elmex 1.1 };
256    
257    
258     /* used by monster beastuary texts */
259 root 1.5 static const char *const mon_author[] = {
260     "beasts",
261     "creatures",
262     "dezidens",
263     "dwellers",
264     "evil nature",
265     "life",
266     "monsters",
267     "nature",
268     "new life",
269     "residents",
270     "the spawn",
271     "the living",
272     "things"
273 elmex 1.1 };
274    
275     /*
276     * God book information
277     */
278    
279 root 1.5 static const char *const gods_book_name[] = {
280     "devotional",
281     "devout notes",
282     "divine text",
283     "divine work",
284     "holy book",
285     "holy record",
286     "moral text",
287     "sacred guide",
288     "testament",
289     "transcript"
290 elmex 1.1 };
291    
292     /* used by gods texts */
293 root 1.5 static const char *const gods_author[] = {
294     "cults",
295     "joy",
296     "lasting curse",
297     "madness",
298     "religions",
299     "the dead",
300     "the gods",
301     "the heirophant",
302     "the poor priest",
303     "the priestess",
304     "pain",
305     "white"
306 elmex 1.1 };
307    
308    
309     /*
310     * Alchemy (formula) information
311     */
312    
313 root 1.5 static const char *const formula_book_name[] = {
314     "cookbook",
315     "formulary",
316     "lab book",
317     "lab notes",
318     "recipe book",
319     "experiment record",
320     "work plan",
321     "design notes"
322 elmex 1.1 };
323    
324     /* this isn't used except for empty books */
325 root 1.5 static const char *const formula_author[] = {
326     "Albertus Magnus",
327     "alchemy",
328     "balms",
329     "creation",
330     "dusts",
331     "magical manufacture",
332     "making",
333     "philosophical items",
334     "potions",
335     "powders",
336     "the cauldron",
337     "the lamp black",
338     "transmutation",
339     "waters"
340 elmex 1.1 };
341    
342     /*
343     * Generic book information
344     */
345    
346     /* used by msg file and 'generic' books */
347 root 1.5 static const char *const light_book_name[] = {
348     "calendar",
349     "datebook",
350     "diary",
351     "guidebook",
352     "handbook",
353     "ledger",
354     "notes",
355     "notebook",
356     "octavo",
357     "pamphlet",
358     "practicum",
359     "script",
360     "transcript"
361 elmex 1.1 };
362    
363 root 1.5 static const char *const heavy_book_name[] = {
364     "catalog",
365     "compendium",
366     "guide",
367     "manual",
368     "opus",
369     "tome",
370     "treatise",
371     "volume",
372     "work"
373 elmex 1.1 };
374    
375    
376     /* used by 'generic' books */
377 root 1.5 static const char *const book_author[] = {
378     "Abdulah",
379     "Al'hezred",
380     "Alywn",
381     "Arundel",
382     "Arvind",
383     "Aerlingas",
384     "Bacon",
385     "Baliqendii",
386     "Bosworth",
387     "Beathis",
388     "Bertil",
389     "Cauchy",
390     "Chakrabarti",
391     "der Waalis",
392     "Dirk",
393     "Djwimii",
394     "Eisenstaadt",
395     "Fendris",
396     "Frank",
397     "Habbi",
398     "Harlod",
399     "Ichibod",
400     "Janus",
401     "June",
402     "Magnuson",
403     "Nandii",
404     "Nitfeder",
405     "Norris",
406     "Parael",
407     "Penhew",
408     "Sophia",
409     "Skilly",
410     "Tahir",
411     "Thockmorton",
412     "Thomas",
413     "van Helsing",
414     "van Pelt",
415     "Voormis",
416     "Xavier",
417     "Xeno",
418     "Zardoz",
419     "Zagy"
420 elmex 1.1 };
421    
422 root 1.5 static const char *const book_descrpt[] = {
423     "ancient",
424     "cryptic",
425     "cryptical",
426     "dusty",
427     "hiearchical",
428     "grizzled",
429     "gold-guilt",
430     "great",
431     "lost",
432     "magnificent",
433     "musty",
434     "mythical",
435     "mystical",
436     "rustic",
437     "stained",
438     "silvered",
439     "transcendental",
440     "weathered"
441 elmex 1.1 };
442 root 1.5
443 elmex 1.1 /* Each line of this array is a readable subtype
444     * Be careful to keep the order. If you add readable subtype, add them
445     * at the bottom of the list. Never delete a subtype because index is used as
446     * subtype paramater in arch files!
447     */
448 root 1.5 static readable_message_type readable_message_types[] = {
449     /*subtype 0 */ {0, 0},
450     /* book messages subtypes */
451     /*subtype 1 */ {MSG_TYPE_BOOK, MSG_TYPE_BOOK_CLASP_1},
452     {MSG_TYPE_BOOK, MSG_TYPE_BOOK_CLASP_2},
453     {MSG_TYPE_BOOK, MSG_TYPE_BOOK_ELEGANT_1},
454     {MSG_TYPE_BOOK, MSG_TYPE_BOOK_ELEGANT_2},
455     {MSG_TYPE_BOOK, MSG_TYPE_BOOK_QUARTO_1},
456     {MSG_TYPE_BOOK, MSG_TYPE_BOOK_QUARTO_2},
457     {MSG_TYPE_BOOK, MSG_TYPE_BOOK_SPELL_EVOKER},
458     {MSG_TYPE_BOOK, MSG_TYPE_BOOK_SPELL_PRAYER},
459     {MSG_TYPE_BOOK, MSG_TYPE_BOOK_SPELL_PYRO},
460     /*subtype 10 */ {MSG_TYPE_BOOK, MSG_TYPE_BOOK_SPELL_SORCERER},
461     {MSG_TYPE_BOOK, MSG_TYPE_BOOK_SPELL_SUMMONER},
462     /* card messages subtypes */
463     {MSG_TYPE_CARD, MSG_TYPE_CARD_SIMPLE_1},
464     {MSG_TYPE_CARD, MSG_TYPE_CARD_SIMPLE_2},
465     {MSG_TYPE_CARD, MSG_TYPE_CARD_SIMPLE_3},
466     {MSG_TYPE_CARD, MSG_TYPE_CARD_ELEGANT_1},
467     {MSG_TYPE_CARD, MSG_TYPE_CARD_ELEGANT_2},
468     {MSG_TYPE_CARD, MSG_TYPE_CARD_ELEGANT_3},
469     {MSG_TYPE_CARD, MSG_TYPE_CARD_STRANGE_1},
470     {MSG_TYPE_CARD, MSG_TYPE_CARD_STRANGE_2},
471     /*subtype 20 */ {MSG_TYPE_CARD, MSG_TYPE_CARD_STRANGE_3},
472     {MSG_TYPE_CARD, MSG_TYPE_CARD_MONEY_1},
473     {MSG_TYPE_CARD, MSG_TYPE_CARD_MONEY_2},
474     {MSG_TYPE_CARD, MSG_TYPE_CARD_MONEY_3},
475    
476     /* Paper messages subtypes */
477     {MSG_TYPE_PAPER, MSG_TYPE_PAPER_NOTE_1},
478     {MSG_TYPE_PAPER, MSG_TYPE_PAPER_NOTE_2},
479     {MSG_TYPE_PAPER, MSG_TYPE_PAPER_NOTE_3},
480     {MSG_TYPE_PAPER, MSG_TYPE_PAPER_LETTER_OLD_1},
481     {MSG_TYPE_PAPER, MSG_TYPE_PAPER_LETTER_OLD_2},
482     {MSG_TYPE_PAPER, MSG_TYPE_PAPER_LETTER_NEW_1},
483     /*subtype 30 */ {MSG_TYPE_PAPER, MSG_TYPE_PAPER_LETTER_NEW_2},
484     {MSG_TYPE_PAPER, MSG_TYPE_PAPER_ENVELOPE_1},
485     {MSG_TYPE_PAPER, MSG_TYPE_PAPER_ENVELOPE_2},
486     {MSG_TYPE_PAPER, MSG_TYPE_PAPER_SCROLL_OLD_1},
487     {MSG_TYPE_PAPER, MSG_TYPE_PAPER_SCROLL_OLD_2},
488     {MSG_TYPE_PAPER, MSG_TYPE_PAPER_SCROLL_NEW_1},
489     {MSG_TYPE_PAPER, MSG_TYPE_PAPER_SCROLL_NEW_2},
490     {MSG_TYPE_PAPER, MSG_TYPE_PAPER_SCROLL_MAGIC},
491    
492     /* road signs messages subtypes */
493     {MSG_TYPE_SIGN, MSG_TYPE_SIGN_BASIC},
494     {MSG_TYPE_SIGN, MSG_TYPE_SIGN_DIR_LEFT},
495     /*subtype 40 */ {MSG_TYPE_SIGN, MSG_TYPE_SIGN_DIR_RIGHT},
496     {MSG_TYPE_SIGN, MSG_TYPE_SIGN_DIR_BOTH},
497    
498     /* stones and monument messages */
499     {MSG_TYPE_MONUMENT, MSG_TYPE_MONUMENT_STONE_1},
500     {MSG_TYPE_MONUMENT, MSG_TYPE_MONUMENT_STONE_2},
501     {MSG_TYPE_MONUMENT, MSG_TYPE_MONUMENT_STONE_3},
502     {MSG_TYPE_MONUMENT, MSG_TYPE_MONUMENT_STATUE_1},
503     {MSG_TYPE_MONUMENT, MSG_TYPE_MONUMENT_STATUE_2},
504     {MSG_TYPE_MONUMENT, MSG_TYPE_MONUMENT_STATUE_3},
505     {MSG_TYPE_MONUMENT, MSG_TYPE_MONUMENT_GRAVESTONE_1},
506     {MSG_TYPE_MONUMENT, MSG_TYPE_MONUMENT_GRAVESTONE_2},
507     /*subtype 50 */ {MSG_TYPE_MONUMENT, MSG_TYPE_MONUMENT_GRAVESTONE_3},
508     {MSG_TYPE_MONUMENT, MSG_TYPE_MONUMENT_WALL_1},
509     {MSG_TYPE_MONUMENT, MSG_TYPE_MONUMENT_WALL_2},
510     {MSG_TYPE_MONUMENT, MSG_TYPE_MONUMENT_WALL_3}
511 elmex 1.1 };
512 root 1.5 int last_readable_subtype = sizeof (readable_message_types) / sizeof (readable_message_type);
513 elmex 1.1
514 root 1.5 static int max_titles[6] = {
515     ((sizeof (light_book_name) / sizeof (char *)) + (sizeof (heavy_book_name) / sizeof (char *))) * (sizeof (book_author) / sizeof (char *)),
516     (sizeof (mon_book_name) / sizeof (char *)) * (sizeof (mon_author) / sizeof (char *)),
517     (sizeof (art_book_name) / sizeof (char *)) * (sizeof (art_author) / sizeof (char *)),
518     (sizeof (path_book_name) / sizeof (char *)) * (sizeof (path_author) / sizeof (char *)),
519     (sizeof (formula_book_name) / sizeof (char *)) * (sizeof (formula_author) / sizeof (char *)),
520     (sizeof (gods_book_name) / sizeof (char *)) * (sizeof (gods_author) / sizeof (char *))
521 elmex 1.1 };
522    
523     /******************************************************************************
524     *
525     * Start of misc. readable functions used by others functions in this file
526     *
527     *****************************************************************************/
528    
529     static titlelist *
530     get_empty_booklist (void)
531     {
532 root 1.5 titlelist *bl = new titlelist;
533 root 1.3
534 root 1.5 bl->number = 0;
535     bl->first_book = NULL;
536     bl->next = NULL;
537     return bl;
538 elmex 1.1 }
539    
540 root 1.5 static title *
541 elmex 1.1 get_empty_book (void)
542     {
543 root 1.5 title *t = new title;
544 root 1.3
545 root 1.5 t->name = NULL;
546     t->archname = NULL;
547     t->authour = NULL;
548     t->level = 0;
549     t->size = 0;
550     t->msg_index = 0;
551     t->next = NULL;
552     return t;
553 elmex 1.1 }
554    
555     /* get_titlelist() - returns pointer to the title list referanced by i */
556    
557     static titlelist *
558     get_titlelist (int i)
559     {
560 root 1.5 titlelist *tl = booklist;
561     int number = i;
562 elmex 1.1
563 root 1.5 if (number < 0)
564     return tl;
565 elmex 1.1
566 root 1.5 while (tl && number)
567     {
568     if (!tl->next)
569     tl->next = get_empty_booklist ();
570     tl = tl->next;
571     number--;
572     }
573 elmex 1.1
574 root 1.5 return tl;
575 elmex 1.1 }
576    
577     /* HANDMADE STRING FUNCTIONS.., perhaps these belong in another file
578     * (shstr.c ?), but the quantity BOOK_BUF will need to be defined. */
579    
580     /* nstrtok() - simple routine to return the number of list
581     * items in buf1 as separated by the value of buf2
582     */
583    
584 root 1.5 int
585 elmex 1.1 nstrtok (const char *buf1, const char *buf2)
586     {
587 root 1.5 char *tbuf, sbuf[12], buf[MAX_BUF];
588     int number = 0;
589 elmex 1.1
590 root 1.5 if (!buf1 || !buf2)
591     return 0;
592     sprintf (buf, "%s", buf1);
593     sprintf (sbuf, "%s", buf2);
594     tbuf = strtok (buf, sbuf);
595     while (tbuf)
596     {
597     number++;
598     tbuf = strtok (NULL, sbuf);
599     }
600     return number;
601 elmex 1.1 }
602    
603     /* strtoktolin() - takes a string in buf1 and separates it into
604     * a list of strings delimited by buf2. Then returns a comma
605     * separated string w/ decent punctuation.
606     */
607    
608 root 1.5 char *
609 elmex 1.1 strtoktolin (const char *buf1, const char *buf2)
610     {
611 root 1.5 int maxi, i = nstrtok (buf1, buf2);
612     char *tbuf, buf[MAX_BUF], sbuf[12];
613     static char rbuf[BOOK_BUF];
614    
615     maxi = i;
616     strcpy (buf, buf1);
617     strcpy (sbuf, buf2);
618     strcpy (rbuf, " ");
619     tbuf = strtok (buf, sbuf);
620     while (tbuf && i > 0)
621     {
622     strcat (rbuf, tbuf);
623     i--;
624     if (i == 1 && maxi > 1)
625     strcat (rbuf, " and ");
626     else if (i > 0 && maxi > 1)
627     strcat (rbuf, ", ");
628     else
629     strcat (rbuf, ".");
630     tbuf = strtok (NULL, sbuf);
631     }
632     return (char *) rbuf;
633 elmex 1.1 }
634    
635 root 1.5 int
636 elmex 1.1 book_overflow (const char *buf1, const char *buf2, int booksize)
637     {
638    
639 root 1.5 if (buf_overflow (buf1, buf2, BOOK_BUF - 2) /* 2 less so always room for trailing \n */
640     || buf_overflow (buf1, buf2, booksize))
641     return 1;
642     return 0;
643 elmex 1.1
644    
645     }
646    
647     /*****************************************************************************
648     *
649     * Start of initialization related functions.
650     *
651     ****************************************************************************/
652    
653     /* init_msgfile() - if not called before, initialize the info list
654     * reads the messages file into the list pointed to by first_msg
655     */
656    
657 root 1.5 static void
658 elmex 1.1 init_msgfile (void)
659     {
660 root 1.5 FILE *fp;
661     char buf[MAX_BUF], msgbuf[HUGE_BUF], fname[MAX_BUF], *cp;
662     int comp;
663     static int did_init_msgfile;
664    
665     if (did_init_msgfile)
666     return;
667     did_init_msgfile = 1;
668    
669     sprintf (fname, "%s/messages", settings.datadir);
670     LOG (llevDebug, "Reading messages from %s...", fname);
671    
672     if ((fp = open_and_uncompress (fname, 0, &comp)) != NULL)
673     {
674     linked_char *tmp = NULL;
675    
676     while (fgets (buf, MAX_BUF, fp) != NULL)
677     {
678     if (*buf == '#')
679     continue;
680     if ((cp = strchr (buf, '\n')) != NULL)
681     *cp = '\0';
682     cp = buf;
683     while (*cp == ' ') /* Skip blanks */
684     cp++;
685     if (!strncmp (cp, "ENDMSG", 6))
686     {
687     if (strlen (msgbuf) > BOOK_BUF)
688     {
689     LOG (llevDebug, "Warning: this string exceeded max book buf size:");
690     LOG (llevDebug, " %s", msgbuf);
691     }
692     tmp->name = msgbuf;
693     tmp->next = first_msg;
694     first_msg = tmp;
695     nrofmsg++;
696     continue;
697     }
698     else if (!strncmp (cp, "MSG", 3))
699     {
700     tmp = new linked_char;
701    
702     strcpy (msgbuf, " "); /* reset msgbuf for new message */
703     continue;
704     }
705     else if (!buf_overflow (msgbuf, cp, HUGE_BUF - 1))
706     {
707     strcat (msgbuf, cp);
708     strcat (msgbuf, "\n");
709 root 1.2 }
710 root 1.5 }
711     close_and_delete (fp, comp);
712     }
713 elmex 1.1
714     #ifdef BOOK_MSG_DEBUG
715 root 1.5 LOG (llevDebug, "\ninit_info_listfile() got %d messages.\n", nrofmsg);
716 elmex 1.1 #endif
717 root 1.5 LOG (llevDebug, "done.\n");
718 elmex 1.1 }
719    
720    
721     /* init_book_archive() - if not called before, initialize the info list
722     * This reads in the bookarch file into memory. bookarch is the file
723     * created and updated across multiple runs of the program.
724     */
725    
726 root 1.5 static void
727 elmex 1.1 init_book_archive (void)
728     {
729 root 1.5 FILE *fp;
730     int comp, nroftitle = 0;
731     char buf[MAX_BUF], fname[MAX_BUF], *cp;
732     title *book = NULL;
733     titlelist *bl = get_empty_booklist ();
734     static int did_init_barch;
735    
736     if (did_init_barch)
737     return;
738     did_init_barch = 1;
739    
740     if (!booklist)
741     booklist = bl;
742    
743     sprintf (fname, "%s/bookarch", settings.localdir);
744     LOG (llevDebug, " Reading bookarch from %s...\n", fname);
745    
746     if ((fp = open_and_uncompress (fname, 0, &comp)) != NULL)
747     {
748     int value, type = 0;
749     size_t i;
750    
751     while (fgets (buf, MAX_BUF, fp) != NULL)
752     {
753     if (*buf == '#')
754     continue;
755     if ((cp = strchr (buf, '\n')) != NULL)
756     *cp = '\0';
757     cp = buf;
758     while (*cp == ' ') /* Skip blanks */
759     cp++;
760     if (!strncmp (cp, "title", 4))
761     {
762     book = get_empty_book (); /* init new book entry */
763     book->name = strchr (cp, ' ') + 1;
764     type = -1;
765     nroftitle++;
766     continue;
767     }
768     if (!strncmp (cp, "authour", 4))
769     {
770     book->authour = strchr (cp, ' ') + 1;
771     }
772     if (!strncmp (cp, "arch", 4))
773     {
774     book->archname = strchr (cp, ' ') + 1;
775     }
776     else if (sscanf (cp, "level %d", &value))
777     {
778     book->level = (uint16) value;
779     }
780     else if (sscanf (cp, "type %d", &value))
781     {
782     type = (uint16) value;
783     }
784     else if (sscanf (cp, "size %d", &value))
785     {
786     book->size = (uint16) value;
787 root 1.2 }
788 root 1.5 else if (sscanf (cp, "index %d", &value))
789 root 1.2 {
790 root 1.5 book->msg_index = (uint16) value;
791     }
792     else if (!strncmp (cp, "end", 3))
793     { /* link it */
794     bl = get_titlelist (type);
795     book->next = bl->first_book;
796     bl->first_book = book;
797     bl->number++;
798 root 1.2 }
799 root 1.5 }
800     LOG (llevDebug, " book archives(used/avail): ");
801     for (bl = booklist, i = 0; bl != NULL && i < sizeof (max_titles) / sizeof (*max_titles); bl = bl->next, i++)
802     {
803     LOG (llevDebug, "(%d/%d)", bl->number, max_titles[i]);
804     }
805     LOG (llevDebug, "\n");
806     close_and_delete (fp, comp);
807     }
808 elmex 1.1
809     #ifdef BOOK_MSG_DEBUG
810 root 1.5 LOG (llevDebug, "\n init_book_archive() got %d titles.\n", nroftitle);
811 elmex 1.1 #endif
812 root 1.5 LOG (llevDebug, " done.\n");
813 elmex 1.1 }
814    
815     /* init_mon_info() - creates the linked list of pointers to
816     * monster archetype objects if not called previously
817     */
818    
819 root 1.5 static void
820 elmex 1.1 init_mon_info (void)
821     {
822 root 1.5 archetype *at;
823     static int did_init_mon_info = 0;
824 elmex 1.1
825 root 1.5 if (did_init_mon_info)
826     return;
827     did_init_mon_info = 1;
828    
829    
830     for (at = first_archetype; at != NULL; at = at->next)
831     {
832     if (QUERY_FLAG (&at->clone, FLAG_MONSTER) && (!QUERY_FLAG (&at->clone, FLAG_CHANGING) || QUERY_FLAG (&at->clone, FLAG_UNAGGRESSIVE)))
833     {
834     objectlink *mon = (objectlink *) malloc (sizeof (objectlink));
835    
836     mon->ob = &at->clone;
837     mon->id = nrofmon;
838     mon->next = first_mon_info;
839     first_mon_info = mon;
840     nrofmon++;
841     }
842     }
843     LOG (llevDebug, "init_mon_info() got %d monsters\n", nrofmon);
844 elmex 1.1 }
845    
846    
847     /* init_readable() - initialize linked lists utilized by
848     * message functions in tailor_readable_ob()
849     *
850     * This is the function called by the main routine to initialize
851     * all the readable information.
852     */
853    
854 root 1.5 void
855 elmex 1.1 init_readable (void)
856     {
857 root 1.5 static int did_this;
858 elmex 1.1
859 root 1.5 if (did_this)
860     return;
861     did_this = 1;
862    
863     LOG (llevDebug, "Initializing reading data...");
864     init_msgfile ();
865     init_book_archive ();
866     init_mon_info ();
867     LOG (llevDebug, " Done\n");
868 elmex 1.1
869     }
870    
871     /*****************************************************************************
872     *
873     * This is the start of the administrative functions when creating
874     * new books (ie, updating title and the like)
875     *
876     *****************************************************************************/
877    
878    
879     /* find_title() - Search the titlelist (based on msgtype) to see if
880     * book matches something already there. IF so, return that title.
881     */
882    
883 root 1.5 static title *
884 elmex 1.1 find_title (const object *book, int msgtype)
885     {
886 root 1.5 title *t = NULL;
887     titlelist *tl = get_titlelist (msgtype);
888     int length = strlen (book->msg);
889     int index = strtoint (book->msg);
890    
891     if (msgtype < 0)
892     return (title *) NULL;
893    
894     if (tl)
895     t = tl->first_book;
896     while (t)
897     if (t->size == length && t->msg_index == index)
898     break;
899     else
900     t = t->next;
901 elmex 1.1
902     #ifdef ARCHIVE_DEBUG
903 root 1.5 if (t)
904     LOG (llevDebug, "Found title match (list %d): %s %s (%d)\n", msgtype, t->name, t->authour, t->msg_index);
905 elmex 1.1 #endif
906    
907 root 1.5 return t;
908 elmex 1.1 }
909    
910     /* new_text_name() - Only for objects of type BOOK. SPELLBOOK stuff is
911     * handled directly in change_book_name(). Names are based on text
912     * msgtype
913     * this sets book book->name based on msgtype given. What name
914     * is given is based on various criteria
915     */
916    
917 root 1.5 static void
918 elmex 1.1 new_text_name (object *book, int msgtype)
919     {
920 root 1.5 int nbr;
921     char name[MAX_BUF];
922 elmex 1.1
923 root 1.5 if (book->type != BOOK)
924     return;
925 elmex 1.1
926 root 1.5 switch (msgtype)
927     {
928     case 1: /*monster */
929 root 1.2 nbr = sizeof (mon_book_name) / sizeof (char *);
930     strcpy (name, mon_book_name[RANDOM () % nbr]);
931     break;
932 root 1.5 case 2: /*artifact */
933 root 1.2 nbr = sizeof (art_book_name) / sizeof (char *);
934     strcpy (name, art_book_name[RANDOM () % nbr]);
935     break;
936 root 1.5 case 3: /*spellpath */
937 root 1.2 nbr = sizeof (path_book_name) / sizeof (char *);
938     strcpy (name, path_book_name[RANDOM () % nbr]);
939     break;
940 root 1.5 case 4: /*alchemy */
941 root 1.2 nbr = sizeof (formula_book_name) / sizeof (char *);
942     strcpy (name, formula_book_name[RANDOM () % nbr]);
943     break;
944 root 1.5 case 5: /*gods */
945 root 1.2 nbr = sizeof (gods_book_name) / sizeof (char *);
946     strcpy (name, gods_book_name[RANDOM () % nbr]);
947     break;
948 root 1.5 case 6: /*msg file */
949     default:
950 root 1.2 if (book->weight > 2000)
951 root 1.5 { /* based on weight */
952     nbr = sizeof (heavy_book_name) / sizeof (char *);
953     strcpy (name, heavy_book_name[RANDOM () % nbr]);
954 root 1.2 }
955     else if (book->weight < 2001)
956     {
957 root 1.5 nbr = sizeof (light_book_name) / sizeof (char *);
958     strcpy (name, light_book_name[RANDOM () % nbr]);
959 root 1.2 }
960     break;
961 root 1.5 }
962 root 1.3
963 root 1.5 book->name = name;
964 elmex 1.1 }
965    
966     /* add_book_author()
967     * A lot like new_text_name above, but instead chooses an author
968     * and sets op->title to that value
969     */
970    
971 root 1.5 static void
972 elmex 1.1 add_author (object *op, int msgtype)
973     {
974 root 1.5 char title[MAX_BUF], name[MAX_BUF];
975     int nbr = sizeof (book_author) / sizeof (char *);
976 elmex 1.1
977 root 1.5 if (msgtype < 0 || strlen (op->msg) < 5)
978     return;
979 elmex 1.1
980 root 1.5 switch (msgtype)
981     {
982     case 1: /* monster */
983 root 1.2 nbr = sizeof (mon_author) / sizeof (char *);
984     strcpy (name, mon_author[RANDOM () % nbr]);
985     break;
986 root 1.5 case 2: /* artifacts */
987 root 1.2 nbr = sizeof (art_author) / sizeof (char *);
988     strcpy (name, art_author[RANDOM () % nbr]);
989     break;
990 root 1.5 case 3: /* spellpath */
991 root 1.2 nbr = sizeof (path_author) / sizeof (char *);
992     strcpy (name, path_author[RANDOM () % nbr]);
993     break;
994 root 1.5 case 4: /* alchemy */
995 root 1.2 nbr = sizeof (formula_author) / sizeof (char *);
996     strcpy (name, formula_author[RANDOM () % nbr]);
997     break;
998 root 1.5 case 5: /* gods */
999 root 1.2 nbr = sizeof (gods_author) / sizeof (char *);
1000     strcpy (name, gods_author[RANDOM () % nbr]);
1001     break;
1002 root 1.5 case 6: /* msg file */
1003     default:
1004 root 1.2 strcpy (name, book_author[RANDOM () % nbr]);
1005 root 1.5 }
1006 elmex 1.1
1007 root 1.5 sprintf (title, "of %s", name);
1008     op->title = title;
1009 elmex 1.1 }
1010    
1011     /* unique_book() - check to see if the book title/msg is unique. We
1012     * go through the entire list of possibilities each time. If we find
1013     * a match, then unique_book returns true (because inst unique).
1014     */
1015    
1016 root 1.5 static int
1017 elmex 1.1 unique_book (const object *book, int msgtype)
1018     {
1019 root 1.5 title *test;
1020 elmex 1.1
1021 root 1.5 if (!booklist)
1022     return 1; /* No archival entries! Must be unique! */
1023 elmex 1.1
1024 root 1.5 /* Go through the booklist. If the author and name match, not unique so
1025     * return 0.
1026     */
1027     for (test = get_titlelist (msgtype)->first_book; test; test = test->next)
1028     {
1029     if (!strcmp (test->name, book->name) && !strcmp (book->title, test->authour))
1030     return 0;
1031 elmex 1.1 }
1032 root 1.5 return 1;
1033 elmex 1.1 }
1034    
1035     /* add_book_to_list() */
1036    
1037 root 1.5 static void
1038 elmex 1.1 add_book_to_list (const object *book, int msgtype)
1039     {
1040 root 1.5 titlelist *tl = get_titlelist (msgtype);
1041     title *t;
1042 elmex 1.1
1043 root 1.5 if (!tl)
1044     {
1045     LOG (llevError, "add_book_to_list can't get booklist!\n");
1046     return;
1047     }
1048 elmex 1.1
1049 root 1.5 t = get_empty_book ();
1050     t->name = book->name;
1051     t->authour = book->title;
1052     t->size = strlen (book->msg);
1053     t->msg_index = strtoint (book->msg);
1054     t->archname = book->arch->name;
1055     t->level = book->level;
1056    
1057     t->next = tl->first_book;
1058     tl->first_book = t;
1059     tl->number++;
1060 elmex 1.1
1061 root 1.5 /* We have stuff we need to write now */
1062     need_to_write_bookarchive = 1;
1063 elmex 1.1
1064     #ifdef ARCHIVE_DEBUG
1065 root 1.5 LOG (llevDebug, "Archiving new title: %s %s (%d)\n", book->name, book->title, msgtype);
1066 elmex 1.1 #endif
1067    
1068     }
1069    
1070    
1071     /* change_book() - give a new, fancier name to generated
1072     * objects of type BOOK and SPELLBOOK.
1073     * Aug 96 I changed this so we will attempt to create consistent
1074     * authour/title and message content for BOOKs. Also, we will
1075     * alter books that match archive entries to the archival
1076     * levels and architypes. -b.t.
1077     */
1078    
1079     #define MAX_TITLE_CHECK 20
1080    
1081 root 1.5 void
1082 elmex 1.1 change_book (object *book, int msgtype)
1083     {
1084 root 1.5 int nbr = sizeof (book_descrpt) / sizeof (char *);
1085 elmex 1.1
1086 root 1.5 switch (book->type)
1087     {
1088     case BOOK:
1089 root 1.2 {
1090 root 1.5 titlelist *tl = get_titlelist (msgtype);
1091     title *t = NULL;
1092     int tries = 0;
1093    
1094     /* look to see if our msg already been archived. If so, alter
1095     * the book to match the archival text. If we fail to match,
1096     * then we archive the new title/name/msg combo if there is
1097     * room on the titlelist.
1098     */
1099 root 1.2
1100 root 1.5 if ((strlen (book->msg) > 5) && (t = find_title (book, msgtype)))
1101     {
1102     object *tmpbook;
1103 root 1.2
1104 root 1.5 /* alter book properties */
1105     if ((tmpbook = get_archetype (t->archname)) != NULL)
1106     {
1107     tmpbook->msg = book->msg;
1108     copy_object (tmpbook, book);
1109     free_object (tmpbook);
1110     }
1111 root 1.2
1112 root 1.5 book->title = t->authour;
1113     book->name = t->name;
1114     book->level = t->level;
1115     }
1116     /* Don't have any default title, so lets make up a new one */
1117     else
1118     {
1119     int numb, maxnames = max_titles[msgtype];
1120     const char *old_title;
1121     const char *old_name;
1122    
1123     old_title = book->title;
1124     old_name = book->name;
1125    
1126     /* some pre-generated books have title already set (from
1127     * maps), also don't bother looking for unique title if
1128     * we already used up all the available names! */
1129 root 1.2
1130 root 1.5 if (!tl)
1131     {
1132     LOG (llevError, "change_book_name(): can't find title list\n");
1133     numb = 0;
1134     }
1135     else
1136     numb = tl->number;
1137 elmex 1.1
1138 root 1.5 if (numb == maxnames)
1139     {
1140 elmex 1.1 #ifdef ARCHIVE_DEBUG
1141 root 1.5 LOG (llevDebug, "titles for list %d full (%d possible).\n", msgtype, maxnames);
1142 elmex 1.1 #endif
1143 root 1.5 break;
1144     }
1145     /* shouldnt change map-maker books */
1146     else if (!book->title)
1147     do
1148     {
1149     /* random book name */
1150     new_text_name (book, msgtype);
1151     add_author (book, msgtype); /* random author */
1152     tries++;
1153     }
1154     while (!unique_book (book, msgtype) && tries < MAX_TITLE_CHECK);
1155    
1156     /* Now deal with 2 cases.
1157     * 1)If no space for a new title exists lets just restore
1158     * the old book properties. Remember, if the book had
1159     * matchd an older entry on the titlelist, we shouldnt
1160     * have called this routine in the first place!
1161     * 2) If we got a unique title, we need to add it to
1162     * the list.
1163     */
1164 elmex 1.1
1165 root 1.5 if (tries == MAX_TITLE_CHECK || numb == maxnames)
1166     { /* got to check maxnames again */
1167 elmex 1.1 #ifdef ARCHIVE_DEBUG
1168 root 1.5 LOG (llevDebug, "Failed to obtain unique title for %s %s (names:%d/%d)\n", book->name, book->title, numb, maxnames);
1169 elmex 1.1 #endif
1170 root 1.5 /* restore old book properties here */
1171     book->title = old_title;
1172    
1173     if (RANDOM () % 4)
1174     {
1175     /* Lets give the book a description to individualize it some */
1176     char new_name[MAX_BUF];
1177    
1178     snprintf (new_name, MAX_BUF, "%s %s", book_descrpt[RANDOM () % nbr], old_name);
1179 root 1.2
1180 root 1.5 book->name = new_name;
1181     }
1182     else
1183     {
1184     book->name = old_name;
1185 root 1.2 }
1186 root 1.5 }
1187     else if (book->title && strlen (book->msg) > 5)
1188     { /* archive if long msg texts */
1189     add_book_to_list (book, msgtype);
1190     }
1191     }
1192     break;
1193 root 1.2 }
1194 elmex 1.1
1195 root 1.5 default:
1196 root 1.2 LOG (llevError, "change_book_name() called w/ illegal obj type.\n");
1197     return;
1198 root 1.5 }
1199 elmex 1.1 }
1200    
1201     /*****************************************************************************
1202     *
1203     * This is the start of the area that generates the actual contents
1204     * of the book.
1205     *
1206     *****************************************************************************/
1207    
1208     /*****************************************************************************
1209     * Monster msg generation code.
1210     ****************************************************************************/
1211    
1212     /* get_random_mon() - returns a random monster slected from linked
1213     * list of all monsters in the current game. If level is non-zero,
1214     * then only monsters greater than that level will be returned.
1215     * Changed 971225 to be greater than equal to level passed. Also
1216     * made choosing by level more random.
1217     */
1218    
1219     object *
1220     get_random_mon (int level)
1221     {
1222 root 1.5 objectlink *mon = first_mon_info;
1223     int i = 0, monnr;
1224    
1225     /* safety check. Problem w/ init_mon_info list? */
1226     if (!nrofmon || !mon)
1227     return (object *) NULL;
1228    
1229     if (!level)
1230     {
1231     /* lets get a random monster from the mon_info linked list */
1232     monnr = RANDOM () % nrofmon;
1233 elmex 1.1
1234 root 1.5 for (mon = first_mon_info, i = 0; mon; mon = mon->next)
1235     if (i++ == monnr)
1236     break;
1237    
1238     if (!mon)
1239     {
1240     LOG (llevError, "get_random_mon: Didn't find a monster when we should have\n");
1241 root 1.2 return NULL;
1242 root 1.5 }
1243     return mon->ob;
1244     }
1245 elmex 1.1
1246 root 1.5 /* Case where we are searching by level. Redone 971225 to be clearer
1247     * and more random. Before, it looks like it took a random monster from
1248     * the list, and then returned the first monster after that which was
1249     * appropriate level. This wasn't very random because if you had a
1250     * bunch of low level monsters and then a high level one, if the random
1251     * determine took one of the low level ones, it would just forward to the
1252     * high level one and return that. Thus, monsters that immediatly followed
1253     * a bunch of low level monsters would be more heavily returned. It also
1254     * means some of the dragons would be poorly represented, since they
1255     * are a group of high level monsters all around each other.
1256     */
1257    
1258     /* First count number of monsters meeting level criteria */
1259     for (mon = first_mon_info, i = 0; mon; mon = mon->next)
1260     if (mon->ob->level >= level)
1261     i++;
1262    
1263     if (i == 0)
1264     {
1265     LOG (llevError, "get_random_mon() couldn't return monster for level %d\n", level);
1266     return NULL;
1267     }
1268    
1269     monnr = RANDOM () % i;
1270     for (mon = first_mon_info; mon; mon = mon->next)
1271     if (mon->ob->level >= level && monnr-- == 0)
1272     return mon->ob;
1273    
1274     if (!mon)
1275     {
1276     LOG (llevError, "get_random_mon(): didn't find a monster when we should have\n");
1277     return NULL;
1278     }
1279     return NULL; /* Should be unreached, by keeps warnings down */
1280 elmex 1.1 }
1281    
1282     /*
1283     * Returns a description of the monster. This really needs to be
1284     * redone, as describe_item really gives a pretty internal description.
1285     */
1286    
1287 root 1.5 char *
1288 elmex 1.1 mon_desc (const object *mon)
1289     {
1290 root 1.5 static char retbuf[HUGE_BUF];
1291 elmex 1.1
1292 root 1.5 sprintf (retbuf, " *** %s ***\n", &mon->name);
1293     strcat (retbuf, describe_item (mon, NULL));
1294 elmex 1.1
1295 root 1.5 return retbuf;
1296 elmex 1.1 }
1297    
1298    
1299     /* This function returns the next monsters after 'tmp'. If no match is
1300     * found, it returns NULL (changed 0.94.3 to do this, since the
1301     * calling function (mon_info_msg) seems to expect that.
1302     */
1303    
1304     object *
1305     get_next_mon (object *tmp)
1306     {
1307 root 1.5 objectlink *mon;
1308 elmex 1.1
1309 root 1.5 for (mon = first_mon_info; mon; mon = mon->next)
1310     if (mon->ob == tmp)
1311     break;
1312    
1313     /* didn't find a match */
1314     if (!mon)
1315     return NULL;
1316     if (mon->next)
1317     return mon->next->ob;
1318     else
1319     return first_mon_info->ob;
1320 elmex 1.1
1321     }
1322    
1323    
1324    
1325     /* mon_info_msg() - generate a message detailing the properties
1326     * of a randomly selected monster.
1327     */
1328    
1329 root 1.5 char *
1330 elmex 1.1 mon_info_msg (int level, int booksize)
1331     {
1332 root 1.5 static char retbuf[BOOK_BUF];
1333     char tmpbuf[HUGE_BUF];
1334     object *tmp;
1335    
1336     /*preamble */
1337     strcpy (retbuf, "This beastiary contains:");
1338    
1339     /* lets print info on as many monsters as will fit in our
1340     * document.
1341     * 8-96 Had to change this a bit, otherwise there would
1342     * have been an impossibly large number of combinations
1343     * of text! (and flood out the available number of titles
1344     * in the archive in a snap!) -b.t.
1345     */
1346     tmp = get_random_mon (level * 3);
1347     while (tmp)
1348     {
1349     /* monster description */
1350     sprintf (tmpbuf, "\n---\n%s", mon_desc (tmp));
1351    
1352     if (!book_overflow (retbuf, tmpbuf, booksize))
1353     strcat (retbuf, tmpbuf);
1354     else
1355     break;
1356 elmex 1.1
1357 root 1.5 /* Note that the value this returns is not based on level */
1358     tmp = get_next_mon (tmp);
1359 elmex 1.1 }
1360    
1361     #ifdef BOOK_MSG_DEBUG
1362 root 1.5 LOG (llevDebug, "\n mon_info_msg() created strng: %d\n", strlen (retbuf));
1363     fprintf (logfile, " MADE THIS:\n%s\n", retbuf);
1364 elmex 1.1 #endif
1365    
1366 root 1.5 return retbuf;
1367 elmex 1.1 }
1368    
1369    
1370     /*****************************************************************************
1371     * Artifact msg generation code.
1372     ****************************************************************************/
1373    
1374     /* artifact_msg() - generate a message detailing the properties
1375     * of 1-6 artifacts drawn sequentially from the artifact list.
1376     */
1377    
1378 root 1.5 char *
1379 elmex 1.1 artifact_msg (int level, int booksize)
1380     {
1381 root 1.5 artifactlist *al = NULL;
1382     artifact *art;
1383     int chance, i, type, index;
1384     int book_entries = level > 5 ? RANDOM () % 3 + RANDOM () % 3 + 2 : RANDOM () % level + 1;
1385     char *ch, name[MAX_BUF], buf[BOOK_BUF], sbuf[MAX_BUF];
1386     static char retbuf[BOOK_BUF];
1387     object *tmp = NULL;
1388    
1389     /* values greater than 5 create msg buffers that are too big! */
1390     if (book_entries > 5)
1391     book_entries = 5;
1392    
1393     /* lets determine what kind of artifact type randomly.
1394     * Right now legal artifacts only come from those listed
1395     * in art_name_array. Also, we check to be sure an artifactlist
1396     * for that type exists!
1397     */
1398     i = 0;
1399     do
1400     {
1401     index = RANDOM () % (sizeof (art_name_array) / sizeof (arttypename));
1402     type = art_name_array[index].type;
1403     al = find_artifactlist (type);
1404     i++;
1405     }
1406     while ((al == NULL) && (i < 10));
1407    
1408     if (i == 10) /* Unable to find a message */
1409     return ("None");
1410 elmex 1.1
1411 root 1.5 /* There is no reason to start on the artifact list at the begining. Lets
1412     * take our starting position randomly... */
1413     art = al->items;
1414     for (i = RANDOM () % level + RANDOM () % 2 + 1; i > 0; i--)
1415     {
1416     if (art == NULL)
1417     art = al->items; /* hmm, out of stuff, loop back around */
1418     art = art->next;
1419     }
1420    
1421     /* the base 'generic' name for our artifact */
1422     strcpy (name, art_name_array[index].name);
1423    
1424     /* Ok, lets print out the contents */
1425     sprintf (retbuf, "Herein %s detailed %s...\n", book_entries > 1 ? "are" : "is", book_entries > 1 ? "some artifacts" : "an artifact");
1426    
1427     /* artifact msg attributes loop. Lets keep adding entries to the 'book'
1428     * as long as we have space up to the allowed max # (book_entires)
1429     */
1430     while (book_entries > 0)
1431     {
1432    
1433     if (art == NULL)
1434     art = al->items;
1435 elmex 1.1
1436 root 1.5 /* separator of items */
1437     strcpy (buf, "--- \n");
1438    
1439     /* Name */
1440     if (art->allowed != NULL && strcmp (art->allowed->name, "All"))
1441     {
1442     linked_char *temp, *next = art->allowed;
1443    
1444     do
1445 root 1.2 {
1446 root 1.5 temp = next;
1447     next = next->next;
1448 root 1.2 }
1449 root 1.5 while (next && !RANDOM () % 2);
1450     sprintf (buf, "%s A %s of %s", buf, &temp->name, &art->item->name);
1451     }
1452     else /* default name is used */
1453     sprintf (buf, "%s The %s of %s", buf, name, &art->item->name);
1454 root 1.2
1455 root 1.5 /* chance of finding */
1456     chance = (int) (100 * ((float) art->chance / al->total_chance));
1457     if (chance >= 20)
1458     sprintf (sbuf, "an uncommon");
1459     else if (chance >= 10)
1460     sprintf (sbuf, "an unusual");
1461     else if (chance >= 5)
1462     sprintf (sbuf, "a rare");
1463     else
1464     sprintf (sbuf, "a very rare");
1465     sprintf (buf, "%s is %s\n", buf, sbuf);
1466    
1467     /* value of artifact */
1468     sprintf (buf, "%s item with a value that is %d times normal.\n", buf, art->item->value);
1469    
1470     /* include the message about the artifact, if exists, and book
1471     * level is kinda high */
1472     if (art->item->msg && (RANDOM () % 4 + 1) < level && !((strlen (art->item->msg) + strlen (buf)) > BOOK_BUF))
1473     strcat (buf, art->item->msg);
1474    
1475     /* properties of the artifact */
1476     tmp = get_object ();
1477     add_abilities (tmp, art->item);
1478     tmp->type = type;
1479     SET_FLAG (tmp, FLAG_IDENTIFIED);
1480     if ((ch = describe_item (tmp, NULL)) != NULL && strlen (ch) > 1)
1481     sprintf (buf, "%s Properties of this artifact include: \n %s \n", buf, ch);
1482     free_object (tmp);
1483     /* add the buf if it will fit */
1484     if (!book_overflow (retbuf, buf, booksize))
1485     strcat (retbuf, buf);
1486     else
1487     break;
1488 root 1.2
1489 root 1.5 art = art->next;
1490     book_entries--;
1491     }
1492 elmex 1.1
1493     #ifdef BOOK_MSG_DEBUG
1494 root 1.5 LOG (llevDebug, "artifact_msg() created strng: %d\n", strlen (retbuf));
1495     fprintf (logfile, " MADE THIS:\n%s", retbuf);
1496 elmex 1.1 #endif
1497 root 1.5 return retbuf;
1498 elmex 1.1 }
1499    
1500     /*****************************************************************************
1501     * Spellpath message generation
1502     *****************************************************************************/
1503    
1504     /* spellpath_msg() - generate a message detailing the member
1505     * incantations/prayers (and some of their properties) belonging to
1506     * a given spellpath.
1507     */
1508    
1509 root 1.5 char *
1510 elmex 1.1 spellpath_msg (int level, int booksize)
1511     {
1512 root 1.5 static char retbuf[BOOK_BUF];
1513     char tmpbuf[BOOK_BUF];
1514     int path = RANDOM () % NRSPELLPATHS, prayers = RANDOM () % 2;
1515     int did_first_sp = 0;
1516     uint32 pnum = (path == -1) ? PATH_NULL : spellpathdef[path];
1517     archetype *at;
1518    
1519     /* Preamble */
1520     sprintf (retbuf, "Herein are detailed the names of %s\n", prayers ? "prayers" : "incantations");
1521    
1522     if (path == -1)
1523     strcat (retbuf, "having no known spell path.\n");
1524     else
1525     sprintf (retbuf, "%sbelonging to the path of %s:\n", retbuf, spellpathnames[path]);
1526    
1527     for (at = first_archetype; at != NULL; at = at->next)
1528     {
1529     /* Determine if this is an appropriate spell. Must
1530     * be of matching path, must be of appropriate type (prayer
1531     * or not), and must be within the valid level range.
1532     */
1533     if (at->clone.type == SPELL && at->clone.path_attuned & pnum &&
1534     ((at->clone.stats.grace && prayers) || (at->clone.stats.sp && !prayers)) && (at->clone.level < (level * 8)))
1535     {
1536     strcpy (tmpbuf, at->clone.name);
1537 elmex 1.1
1538 root 1.5 if (book_overflow (retbuf, tmpbuf, booksize))
1539     break;
1540     else
1541     {
1542     if (did_first_sp)
1543     strcat (retbuf, ",\n");
1544     did_first_sp = 1;
1545     strcat (retbuf, tmpbuf);
1546 root 1.2 }
1547 root 1.5 }
1548 elmex 1.1 }
1549 root 1.5 /* Geez, no spells were generated. */
1550     if (!did_first_sp)
1551     {
1552     if (RANDOM () % 4) /* usually, lets make a recursive call... */
1553     spellpath_msg (level, booksize);
1554     else /* give up, cause knowing no spells exist for path is info too. */
1555     strcat (retbuf, "\n - no known spells exist -\n");
1556 elmex 1.1 }
1557 root 1.5 else
1558     {
1559     strcat (retbuf, "\n");
1560 elmex 1.1 }
1561 root 1.5 return retbuf;
1562 elmex 1.1 }
1563    
1564    
1565    
1566     /* formula_msg() - generate a message detailing the properties
1567     * of a randomly selected alchemical formula.
1568     */
1569    
1570 root 1.5 void
1571     make_formula_book (object *book, int level)
1572     {
1573     char retbuf[BOOK_BUF], title[MAX_BUF];
1574     recipelist *fl;
1575     recipe *formula = NULL;
1576     int chance;
1577    
1578     /* the higher the book level, the more complex (ie number of
1579     * ingredients) the formula can be.
1580     */
1581     fl = get_formulalist (((RANDOM () % level) / 3) + 1);
1582    
1583     if (!fl)
1584     fl = get_formulalist (1); /* safety */
1585    
1586     if (fl->total_chance == 0)
1587     {
1588     book->msg = "<indecipherable text>\n";
1589     new_text_name (book, 4);
1590     add_author (book, 4);
1591     return;
1592     }
1593    
1594     /* get a random formula, weighted by its bookchance */
1595     chance = RANDOM () % fl->total_chance;
1596     for (formula = fl->items; formula != NULL; formula = formula->next)
1597     {
1598     chance -= formula->chance;
1599     if (chance <= 0)
1600     break;
1601     }
1602    
1603     if (!formula || formula->arch_names <= 0)
1604     {
1605     book->msg = "<indecipherable text>\n";
1606     new_text_name (book, 4);
1607     add_author (book, 4);
1608    
1609 elmex 1.1 }
1610 root 1.5 else
1611     {
1612     /* looks like a formula was found. Base the amount
1613     * of information on the booklevel and the spellevel
1614     * of the formula. */
1615    
1616     const char *op_name = formula->arch_name[RANDOM () % formula->arch_names];
1617     archetype *at;
1618    
1619     /* preamble */
1620     sprintf (retbuf, "Herein is described a project using %s: \n", formula->skill ? &formula->skill : "an unknown skill");
1621    
1622     if ((at = find_archetype (op_name)) != (archetype *) NULL)
1623     op_name = at->clone.name;
1624     else
1625     LOG (llevError, "formula_msg() can't find arch %s for formula.\n", op_name);
1626 elmex 1.1
1627 root 1.5 /* item name */
1628     if (strcmp (formula->title, "NONE"))
1629     {
1630     sprintf (retbuf, "%sThe %s of %s", retbuf, op_name, &formula->title);
1631     /* This results in things like pile of philo. sulfur.
1632     * while philo. sulfur may look better, without this,
1633     * you get things like 'the wise' because its missing the
1634     * water of section.
1635     */
1636     sprintf (title, "%s: %s of %s",
1637     formula_book_name[RANDOM () % (sizeof (formula_book_name) / sizeof (char *))], op_name, &formula->title);
1638 root 1.2 }
1639 root 1.5 else
1640 root 1.2 {
1641 root 1.5 sprintf (retbuf, "%sThe %s", retbuf, op_name);
1642     sprintf (title, "%s: %s", formula_book_name[RANDOM () % (sizeof (formula_book_name) / sizeof (char *))], op_name);
1643     if (at->clone.title)
1644     {
1645     strcat (retbuf, " ");
1646     strcat (retbuf, at->clone.title);
1647     strcat (title, " ");
1648     strcat (title, at->clone.title);
1649 root 1.2 }
1650     }
1651 root 1.5 /* Lets name the book something meaningful ! */
1652     book->name = title;
1653     book->title = NULL;
1654 root 1.2
1655 root 1.5 /* ingredients to make it */
1656     if (formula->ingred != NULL)
1657 root 1.2 {
1658 root 1.5 linked_char *next;
1659     archetype *at;
1660 root 1.2
1661 root 1.5 at = find_archetype (formula->cauldron);
1662 root 1.2
1663 root 1.5 sprintf (retbuf + strlen (retbuf),
1664     " may be made at %s using the following ingredients:\n", at ? query_name (&at->clone) : "an unknown place");
1665 root 1.2
1666 root 1.5 for (next = formula->ingred; next != NULL; next = next->next)
1667 root 1.2 {
1668 root 1.5 strcat (retbuf, next->name);
1669     strcat (retbuf, "\n");
1670 root 1.2 }
1671     }
1672 root 1.5 else
1673     LOG (llevError, "formula_msg() no ingredient list for object %s of %s\n", op_name, &formula->title);
1674     if (retbuf[strlen (retbuf) - 1] != '\n')
1675     strcat (retbuf, "\n");
1676 root 1.3
1677 root 1.5 book->msg = retbuf;
1678 elmex 1.1 }
1679     }
1680    
1681    
1682     /* msgfile_msg() - generate a message drawn randomly from a
1683     * file in lib/. Level currently has no effect on the message
1684     * which is returned.
1685     */
1686    
1687 root 1.5 char *
1688 elmex 1.1 msgfile_msg (int level, int booksize)
1689     {
1690 root 1.5 static char retbuf[BOOK_BUF];
1691     int i, msgnum;
1692     linked_char *msg = NULL;
1693    
1694     /* get a random message for the 'book' from linked list */
1695     if (nrofmsg > 1)
1696     {
1697     msg = first_msg;
1698     msgnum = RANDOM () % nrofmsg;
1699     for (i = 0; msg && i < nrofmsg && i != msgnum; i++)
1700     msg = msg->next;
1701     }
1702 elmex 1.1
1703 root 1.5 if (msg && !book_overflow (retbuf, msg->name, booksize))
1704     strcpy (retbuf, msg->name);
1705     else
1706     sprintf (retbuf, "\n <undecipherable text>");
1707 elmex 1.1
1708     #ifdef BOOK_MSG_DEBUG
1709 root 1.5 LOG (llevDebug, "\n info_list_msg() created strng: %d\n", strlen (retbuf));
1710     LOG (llevDebug, " MADE THIS:\n%s\n", retbuf);
1711 elmex 1.1 #endif
1712    
1713 root 1.5 return retbuf;
1714 elmex 1.1 }
1715    
1716    
1717     /* god_info_msg() - generate a message detailing the properties
1718     * of a random god. Used by the book hack. b.t.
1719     */
1720    
1721 root 1.5 const char *
1722 elmex 1.1 god_info_msg (int level, int booksize)
1723     {
1724 root 1.5 static char retbuf[BOOK_BUF];
1725     const char *name = NULL;
1726     char buf[BOOK_BUF];
1727     int i;
1728     size_t introlen;
1729     object *god = pntr_to_god_obj (get_rand_god ());
1730    
1731     if (!god)
1732     return (char *) NULL; /* oops, problems... */
1733     name = god->name;
1734    
1735     /* preamble.. */
1736     sprintf (retbuf, "This document contains knowledge concerning\n");
1737     sprintf (retbuf, "%sthe diety %s", retbuf, name);
1738    
1739     /* Always have as default information the god's descriptive terms. */
1740     if (nstrtok (god->msg, ",") > 0)
1741     {
1742     strcat (retbuf, ", known as");
1743     strcat (retbuf, strtoktolin (god->msg, ","));
1744     }
1745     else
1746     strcat (retbuf, "...");
1747    
1748     strcat (retbuf, "\n ---\n");
1749     introlen = strlen (retbuf); /* so we will know if no new info is added later */
1750 elmex 1.1
1751 root 1.5 /* Information about the god is random, and based on the level of the
1752     * 'book'. Probably there is a more intellegent way to implement
1753     * this ...
1754     */
1755    
1756     while (level > 0)
1757     {
1758     sprintf (buf, " ");
1759     if (level == 2 && RANDOM () % 2)
1760     { /* enemy god */
1761     const char *enemy = god->title;
1762 elmex 1.1
1763 root 1.5 if (enemy)
1764     sprintf (buf, "The gods %s and %s are enemies.\n ---\n", name, enemy);
1765     }
1766     if (level == 3 && RANDOM () % 2)
1767     { /* enemy race, what the god's holy word effects */
1768     const char *enemy = god->slaying;
1769    
1770     if (enemy && !(god->path_denied & PATH_TURNING))
1771     if ((i = nstrtok (enemy, ",")) > 0)
1772     {
1773     char tmpbuf[MAX_BUF];
1774    
1775     sprintf (buf, "The holy words of %s have the power to\n", name);
1776     strcat (buf, "slay creatures belonging to the ");
1777     if (i > 1)
1778     sprintf (tmpbuf, "following \n races:%s", strtoktolin (enemy, ","));
1779     else
1780     sprintf (tmpbuf, "race of%s", strtoktolin (enemy, ","));
1781     sprintf (buf, "%s%s\n ---\n", buf, tmpbuf);
1782     }
1783     }
1784     if (level == 4 && RANDOM () % 2)
1785     { /* Priest of god gets these protect,vulnerable... */
1786     char tmpbuf[MAX_BUF], *cp;
1787    
1788     cp = describe_resistance (god, 1);
1789    
1790     if (*cp)
1791     { /* This god does have protections */
1792     sprintf (tmpbuf, "%s has a potent aura which is extended\n", name);
1793     strcat (tmpbuf, "faithful priests. The effects of this aura include:\n");
1794     strcat (tmpbuf, cp);
1795     strcat (buf, tmpbuf);
1796     strcat (buf, "\n ---\n");
1797     }
1798     else
1799     sprintf (buf, " ");
1800     }
1801     if (level == 5 && RANDOM () % 2)
1802     { /* aligned race, summoning */
1803     const char *race = god->race; /* aligned race */
1804    
1805     if (race && !(god->path_denied & PATH_SUMMON))
1806     if ((i = nstrtok (race, ",")) > 0)
1807     {
1808     char tmpbuf[MAX_BUF];
1809    
1810     sprintf (buf, "Creatures sacred to %s include the \n", name);
1811     if (i > 1)
1812     sprintf (tmpbuf, "following \n races:%s", strtoktolin (race, ","));
1813     else
1814     sprintf (tmpbuf, "race of%s", strtoktolin (race, ","));
1815     sprintf (buf, "%s%s\n ---\n", buf, tmpbuf);
1816     }
1817     }
1818     if (level == 6 && RANDOM () % 2)
1819     { /* blessing,curse properties of the god */
1820     char tmpbuf[MAX_BUF], *cp;
1821    
1822     cp = describe_resistance (god, 1);
1823    
1824     if (*cp)
1825     { /* This god does have protections */
1826     sprintf (tmpbuf, "\nThe priests of %s are known to be able to \n", name);
1827     strcat (tmpbuf, "bestow a blessing which makes the recipient\n");
1828     strcat (tmpbuf, cp);
1829     strcat (buf, tmpbuf);
1830     strcat (buf, "\n ---\n");
1831 root 1.2 }
1832 root 1.5 else
1833     sprintf (buf, " ");
1834    
1835     }
1836     if (level == 8 && RANDOM () % 2)
1837     { /* immunity, holy possession */
1838     int has_effect = 0, tmpvar;
1839     char tmpbuf[MAX_BUF];
1840    
1841     sprintf (tmpbuf, "\n");
1842     sprintf (tmpbuf, "The priests of %s are known to make cast a mighty \n", name);
1843    
1844     strcat (tmpbuf, "prayer of possession which gives the recipient\n");
1845    
1846     for (tmpvar = 0; tmpvar < NROFATTACKS; tmpvar++)
1847     {
1848     if (god->resist[tmpvar] == 100)
1849     {
1850     has_effect = 1;
1851     sprintf (tmpbuf + strlen (tmpbuf), "Immunity to %s", attacktype_desc[tmpvar]);
1852 root 1.2 }
1853     }
1854 root 1.5 if (has_effect)
1855     {
1856     strcat (buf, tmpbuf);
1857     strcat (buf, "\n ---\n");
1858     }
1859     else
1860     sprintf (buf, " ");
1861     }
1862     if (level == 12 && RANDOM () % 2)
1863     { /* spell paths */
1864     int has_effect = 0, tmpvar;
1865     char tmpbuf[MAX_BUF];
1866    
1867     sprintf (tmpbuf, "\n");
1868     sprintf (tmpbuf, "It is rarely known fact that the priests of %s\n", name);
1869     strcat (tmpbuf, "are mystically transformed. Effects of this include:\n");
1870     if ((tmpvar = god->path_attuned))
1871     {
1872     has_effect = 1;
1873     DESCRIBE_PATH (tmpbuf, tmpvar, "Attuned");
1874 root 1.2 }
1875 root 1.5 if ((tmpvar = god->path_repelled))
1876     {
1877     has_effect = 1;
1878     DESCRIBE_PATH (tmpbuf, tmpvar, "Repelled");
1879 root 1.2 }
1880 root 1.5 if ((tmpvar = god->path_denied))
1881     {
1882     has_effect = 1;
1883     DESCRIBE_PATH (tmpbuf, tmpvar, "Denied");
1884 root 1.2 }
1885 root 1.5 if (has_effect)
1886     {
1887     strcat (buf, tmpbuf);
1888     strcat (buf, "\n ---\n");
1889 root 1.2 }
1890 root 1.5 else
1891     sprintf (buf, " ");
1892     }
1893 root 1.2
1894 root 1.5 /* check to be sure new buffer size dont exceed either
1895     * the maximum buffer size, or the 'natural' size of the
1896     * book...
1897     */
1898     if (book_overflow (retbuf, buf, booksize))
1899     break;
1900     else if (strlen (buf) > 1)
1901     strcat (retbuf, buf);
1902     level--;
1903     }
1904     if (strlen (retbuf) == introlen)
1905     { /* we got no information beyond the preamble! */
1906     strcat (retbuf, " [Unfortunately the rest of the information is\n");
1907     strcat (retbuf, " hopelessly garbled!]\n ---\n");
1908     }
1909 elmex 1.1 #ifdef BOOK_MSG_DEBUG
1910 root 1.5 LOG (llevDebug, "\n god_info_msg() created strng: %d\n", strlen (retbuf));
1911     fprintf (logfile, " MADE THIS:\n%s", retbuf);
1912 elmex 1.1 #endif
1913 root 1.5 return retbuf;
1914 elmex 1.1 }
1915    
1916     /* tailor_readable_ob()- The main routine. This chooses a random
1917     * message to put in given readable object (type==BOOK) which will
1918     * be referred hereafter as a 'book'. We use the book level to de-
1919     * termine the value of the information we will insert. Higher
1920     * values mean the book will (generally) have better/more info.
1921     * See individual cases as to how this will be utilized.
1922     * "Book" name/content length are based on the weight of the
1923     * document. If the value of msg_type is negative, we will randomly
1924     * choose the kind of message to generate.
1925     * -b.t. thomas@astro.psu.edu
1926     *
1927     * book is the object we are creating into.
1928     * If msg_type is a positive value, we use that to determine the
1929     * message type - otherwise a random value is used.
1930     *
1931     */
1932    
1933 root 1.5 void
1934 elmex 1.1 tailor_readable_ob (object *book, int msg_type)
1935     {
1936 root 1.5 char msgbuf[BOOK_BUF];
1937     int level = book->level ? (RANDOM () % book->level) + 1 : 1;
1938     int book_buf_size;
1939    
1940     /* safety */
1941     if (book->type != BOOK)
1942     return;
1943    
1944     if (level <= 0)
1945     return; /* if no level no point in doing any more... */
1946    
1947     /* Max text length this book can have. */
1948     book_buf_size = BOOKSIZE (book);
1949    
1950     /* &&& The message switch &&& */
1951     /* Below all of the possible types of messages in the "book"s.
1952     */
1953     /*
1954     * IF you add a new type of book msg, you will have to do several things.
1955     * 1) make sure there is an entry in the msg switch below!
1956     * 2) make sure there is an entry in max_titles[] array.
1957     * 3) make sure there are entries for your case in new_text_title()
1958     * and add_authour().
1959     * 4) you may want separate authour/book name arrays in read.h
1960     */
1961    
1962     msg_type = msg_type > 0 ? msg_type : (RANDOM () % 6);
1963     switch (msg_type)
1964     {
1965     case 1: /* monster attrib */
1966 root 1.2 strcpy (msgbuf, mon_info_msg (level, book_buf_size));
1967     break;
1968 root 1.5 case 2: /* artifact attrib */
1969 root 1.2 strcpy (msgbuf, artifact_msg (level, book_buf_size));
1970     break;
1971 root 1.5 case 3: /* grouping incantations/prayers by path */
1972 root 1.2 strcpy (msgbuf, spellpath_msg (level, book_buf_size));
1973     break;
1974 root 1.5 case 4: /* describe an alchemy formula */
1975     make_formula_book (book, level);
1976     /* make_formula_book already gives title */
1977     return;
1978     break;
1979     case 5: /* bits of information about a god */
1980 root 1.2 strcpy (msgbuf, god_info_msg (level, book_buf_size));
1981     break;
1982 root 1.5 case 0: /* use info list in lib/ */
1983     default:
1984 root 1.2 strcpy (msgbuf, msgfile_msg (level, book_buf_size));
1985     break;
1986 root 1.5 }
1987 elmex 1.1
1988 root 1.5 strcat (msgbuf, "\n"); /* safety -- we get ugly map saves/crashes w/o this */
1989     if (strlen (msgbuf) > 1)
1990     {
1991     book->msg = msgbuf;
1992     /* lets give the "book" a new name, which may be a compound word */
1993     change_book (book, msg_type);
1994     }
1995 elmex 1.1
1996     }
1997    
1998    
1999     /*****************************************************************************
2000     *
2001     * Cleanup routine for readble stuff.
2002     *
2003     *****************************************************************************/
2004    
2005 root 1.5 void
2006 elmex 1.1 free_all_readable (void)
2007     {
2008 root 1.5 titlelist *tlist, *tnext;
2009     title *title1, *titlenext;
2010     linked_char *lmsg, *nextmsg;
2011     objectlink *monlink, *nextmon;
2012    
2013     LOG (llevDebug, "freeing all book information\n");
2014    
2015     for (tlist = booklist; tlist != NULL; tlist = tnext)
2016     {
2017     tnext = tlist->next;
2018    
2019     for (title1 = tlist->first_book; title1; title1 = titlenext)
2020     {
2021     titlenext = title1->next;
2022     delete title1;
2023     }
2024    
2025     delete tlist;
2026     }
2027     for (lmsg = first_msg; lmsg; lmsg = nextmsg)
2028     {
2029     nextmsg = lmsg->next;
2030     delete lmsg;
2031     }
2032     for (monlink = first_mon_info; monlink; monlink = nextmon)
2033     {
2034     nextmon = monlink->next;
2035     free (monlink);
2036     }
2037 elmex 1.1 }
2038    
2039    
2040     /*****************************************************************************
2041     *
2042     * Writeback routine for updating the bookarchive.
2043     *
2044     ****************************************************************************/
2045    
2046     /* write_book_archive() - write out the updated book archive */
2047    
2048 root 1.5 void
2049 elmex 1.1 write_book_archive (void)
2050     {
2051 root 1.5 FILE *fp;
2052     int index = 0;
2053     char fname[MAX_BUF];
2054     title *book = NULL;
2055     titlelist *bl = get_titlelist (0);
2056    
2057     /* If nothing changed, don't write anything */
2058     if (!need_to_write_bookarchive)
2059     return;
2060     need_to_write_bookarchive = 0;
2061    
2062     sprintf (fname, "%s/bookarch", settings.localdir);
2063     LOG (llevDebug, "Updating book archive: %s...\n", fname);
2064    
2065     if ((fp = fopen (fname, "w")) == NULL)
2066     {
2067     LOG (llevDebug, "Can't open book archive file %s\n", fname);
2068     }
2069     else
2070     {
2071     while (bl)
2072     {
2073     for (book = bl->first_book; book; book = book->next)
2074     if (book && book->authour)
2075     {
2076     fprintf (fp, "title %s\n", &book->name);
2077     fprintf (fp, "authour %s\n", &book->authour);
2078     fprintf (fp, "arch %s\n", &book->archname);
2079     fprintf (fp, "level %d\n", book->level);
2080     fprintf (fp, "type %d\n", index);
2081     fprintf (fp, "size %d\n", book->size);
2082     fprintf (fp, "index %d\n", book->msg_index);
2083     fprintf (fp, "end\n");
2084     }
2085     bl = bl->next;
2086     index++;
2087     }
2088     fclose (fp);
2089     chmod (fname, SAVE_MODE);
2090     }
2091     }
2092     readable_message_type *
2093     get_readable_message_type (object *readable)
2094     {
2095     uint8 subtype = readable->subtype;
2096    
2097     if (subtype > last_readable_subtype)
2098     return &(readable_message_types[0]);
2099     return &(readable_message_types[subtype]);
2100 elmex 1.1 }