ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/readable.C
Revision: 1.10
Committed: Fri Sep 29 21:55:54 2006 UTC (17 years, 7 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.9: +3 -0 lines
Log Message:
indent

File Contents

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