ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/readable.C
Revision: 1.15
Committed: Wed Jan 3 00:08:17 2007 UTC (17 years, 4 months ago) by pippijn
Content type: text/plain
Branch: MAIN
Changes since 1.14: +2 -3 lines
Log Message:
1 date/line

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 pippijn 1.14 LOG (llevDebug, "Reading messages from %s...\n", fname);
663 root 1.5
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 pippijn 1.14 LOG (llevDebug, " %s\n", msgbuf);
683 root 1.5 }
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 pippijn 1.14 LOG (llevDebug, "init_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 pippijn 1.14 LOG (llevDebug, "book archives(used/avail): \n");
794 root 1.5 for (bl = booklist, i = 0; bl != NULL && i < sizeof (max_titles) / sizeof (*max_titles); bl = bl->next, i++)
795     {
796 pippijn 1.15 LOG (llevDebug, " (%d/%d)\n", bl->number, max_titles[i]);
797 root 1.5 }
798     close_and_delete (fp, comp);
799     }
800 elmex 1.1
801     #ifdef BOOK_MSG_DEBUG
802 pippijn 1.15 LOG (llevDebug, "init_book_archive() got %d titles.\n", nroftitle);
803 elmex 1.1 #endif
804 root 1.5 LOG (llevDebug, " done.\n");
805 elmex 1.1 }
806    
807     /* init_mon_info() - creates the linked list of pointers to
808     * monster archetype objects if not called previously
809     */
810    
811 root 1.5 static void
812 elmex 1.1 init_mon_info (void)
813     {
814 root 1.5 archetype *at;
815     static int did_init_mon_info = 0;
816 elmex 1.1
817 root 1.5 if (did_init_mon_info)
818     return;
819 root 1.11
820 root 1.5 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 root 1.11 objectlink *mon = new objectlink;
828 root 1.5
829     mon->ob = &at->clone;
830     mon->next = first_mon_info;
831     first_mon_info = mon;
832     nrofmon++;
833     }
834     }
835 root 1.11
836 root 1.5 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 root 1.11
855 root 1.5 did_this = 1;
856    
857 pippijn 1.14 LOG (llevDebug, "Initialising reading data...\n");
858 root 1.5 init_msgfile ();
859     init_book_archive ();
860     init_mon_info ();
861     LOG (llevDebug, " Done\n");
862 elmex 1.1
863     }
864    
865     /*****************************************************************************
866     *
867     * This is the start of the administrative functions when creating
868     * new books (ie, updating title and the like)
869     *
870     *****************************************************************************/
871    
872    
873     /* find_title() - Search the titlelist (based on msgtype) to see if
874     * book matches something already there. IF so, return that title.
875     */
876    
877 root 1.5 static title *
878 elmex 1.1 find_title (const object *book, int msgtype)
879     {
880 root 1.5 title *t = NULL;
881     titlelist *tl = get_titlelist (msgtype);
882     int length = strlen (book->msg);
883     int index = strtoint (book->msg);
884    
885     if (msgtype < 0)
886     return (title *) NULL;
887    
888     if (tl)
889     t = tl->first_book;
890 root 1.6
891 root 1.5 while (t)
892     if (t->size == length && t->msg_index == index)
893     break;
894     else
895     t = t->next;
896 elmex 1.1
897     #ifdef ARCHIVE_DEBUG
898 root 1.5 if (t)
899     LOG (llevDebug, "Found title match (list %d): %s %s (%d)\n", msgtype, t->name, t->authour, t->msg_index);
900 elmex 1.1 #endif
901    
902 root 1.5 return t;
903 elmex 1.1 }
904    
905     /* new_text_name() - Only for objects of type BOOK. SPELLBOOK stuff is
906     * handled directly in change_book_name(). Names are based on text
907     * msgtype
908     * this sets book book->name based on msgtype given. What name
909     * is given is based on various criteria
910     */
911    
912 root 1.5 static void
913 elmex 1.1 new_text_name (object *book, int msgtype)
914     {
915 root 1.5 int nbr;
916     char name[MAX_BUF];
917 elmex 1.1
918 root 1.5 if (book->type != BOOK)
919     return;
920 elmex 1.1
921 root 1.5 switch (msgtype)
922     {
923     case 1: /*monster */
924 root 1.2 nbr = sizeof (mon_book_name) / sizeof (char *);
925     strcpy (name, mon_book_name[RANDOM () % nbr]);
926     break;
927 root 1.5 case 2: /*artifact */
928 root 1.2 nbr = sizeof (art_book_name) / sizeof (char *);
929     strcpy (name, art_book_name[RANDOM () % nbr]);
930     break;
931 root 1.5 case 3: /*spellpath */
932 root 1.2 nbr = sizeof (path_book_name) / sizeof (char *);
933     strcpy (name, path_book_name[RANDOM () % nbr]);
934     break;
935 root 1.5 case 4: /*alchemy */
936 root 1.2 nbr = sizeof (formula_book_name) / sizeof (char *);
937     strcpy (name, formula_book_name[RANDOM () % nbr]);
938     break;
939 root 1.5 case 5: /*gods */
940 root 1.2 nbr = sizeof (gods_book_name) / sizeof (char *);
941     strcpy (name, gods_book_name[RANDOM () % nbr]);
942     break;
943 root 1.5 case 6: /*msg file */
944     default:
945 root 1.2 if (book->weight > 2000)
946 root 1.5 { /* based on weight */
947     nbr = sizeof (heavy_book_name) / sizeof (char *);
948     strcpy (name, heavy_book_name[RANDOM () % nbr]);
949 root 1.2 }
950     else if (book->weight < 2001)
951     {
952 root 1.5 nbr = sizeof (light_book_name) / sizeof (char *);
953     strcpy (name, light_book_name[RANDOM () % nbr]);
954 root 1.2 }
955     break;
956 root 1.5 }
957 root 1.3
958 root 1.5 book->name = name;
959 elmex 1.1 }
960    
961     /* add_book_author()
962     * A lot like new_text_name above, but instead chooses an author
963     * and sets op->title to that value
964     */
965    
966 root 1.5 static void
967 elmex 1.1 add_author (object *op, int msgtype)
968     {
969 root 1.5 char title[MAX_BUF], name[MAX_BUF];
970     int nbr = sizeof (book_author) / sizeof (char *);
971 elmex 1.1
972 root 1.5 if (msgtype < 0 || strlen (op->msg) < 5)
973     return;
974 elmex 1.1
975 root 1.5 switch (msgtype)
976     {
977     case 1: /* monster */
978 root 1.2 nbr = sizeof (mon_author) / sizeof (char *);
979     strcpy (name, mon_author[RANDOM () % nbr]);
980     break;
981 root 1.5 case 2: /* artifacts */
982 root 1.2 nbr = sizeof (art_author) / sizeof (char *);
983     strcpy (name, art_author[RANDOM () % nbr]);
984     break;
985 root 1.5 case 3: /* spellpath */
986 root 1.2 nbr = sizeof (path_author) / sizeof (char *);
987     strcpy (name, path_author[RANDOM () % nbr]);
988     break;
989 root 1.5 case 4: /* alchemy */
990 root 1.2 nbr = sizeof (formula_author) / sizeof (char *);
991     strcpy (name, formula_author[RANDOM () % nbr]);
992     break;
993 root 1.5 case 5: /* gods */
994 root 1.2 nbr = sizeof (gods_author) / sizeof (char *);
995     strcpy (name, gods_author[RANDOM () % nbr]);
996     break;
997 root 1.5 case 6: /* msg file */
998     default:
999 root 1.2 strcpy (name, book_author[RANDOM () % nbr]);
1000 root 1.5 }
1001 elmex 1.1
1002 root 1.5 sprintf (title, "of %s", name);
1003     op->title = title;
1004 elmex 1.1 }
1005    
1006     /* unique_book() - check to see if the book title/msg is unique. We
1007     * go through the entire list of possibilities each time. If we find
1008     * a match, then unique_book returns true (because inst unique).
1009     */
1010    
1011 root 1.5 static int
1012 elmex 1.1 unique_book (const object *book, int msgtype)
1013     {
1014 root 1.5 title *test;
1015 elmex 1.1
1016 root 1.5 if (!booklist)
1017     return 1; /* No archival entries! Must be unique! */
1018 elmex 1.1
1019 root 1.5 /* Go through the booklist. If the author and name match, not unique so
1020     * return 0.
1021     */
1022     for (test = get_titlelist (msgtype)->first_book; test; test = test->next)
1023     {
1024     if (!strcmp (test->name, book->name) && !strcmp (book->title, test->authour))
1025     return 0;
1026 elmex 1.1 }
1027 root 1.5 return 1;
1028 elmex 1.1 }
1029    
1030     /* add_book_to_list() */
1031    
1032 root 1.5 static void
1033 elmex 1.1 add_book_to_list (const object *book, int msgtype)
1034     {
1035 root 1.5 titlelist *tl = get_titlelist (msgtype);
1036     title *t;
1037 elmex 1.1
1038 root 1.5 if (!tl)
1039     {
1040     LOG (llevError, "add_book_to_list can't get booklist!\n");
1041     return;
1042     }
1043 elmex 1.1
1044 root 1.5 t = get_empty_book ();
1045     t->name = book->name;
1046     t->authour = book->title;
1047     t->size = strlen (book->msg);
1048     t->msg_index = strtoint (book->msg);
1049     t->archname = book->arch->name;
1050     t->level = book->level;
1051    
1052     t->next = tl->first_book;
1053     tl->first_book = t;
1054     tl->number++;
1055 elmex 1.1
1056 root 1.5 /* We have stuff we need to write now */
1057     need_to_write_bookarchive = 1;
1058 elmex 1.1
1059     #ifdef ARCHIVE_DEBUG
1060 root 1.5 LOG (llevDebug, "Archiving new title: %s %s (%d)\n", book->name, book->title, msgtype);
1061 elmex 1.1 #endif
1062    
1063     }
1064    
1065    
1066     /* change_book() - give a new, fancier name to generated
1067     * objects of type BOOK and SPELLBOOK.
1068     * Aug 96 I changed this so we will attempt to create consistent
1069     * authour/title and message content for BOOKs. Also, we will
1070     * alter books that match archive entries to the archival
1071     * levels and architypes. -b.t.
1072     */
1073    
1074     #define MAX_TITLE_CHECK 20
1075    
1076 root 1.5 void
1077 elmex 1.1 change_book (object *book, int msgtype)
1078     {
1079 root 1.5 int nbr = sizeof (book_descrpt) / sizeof (char *);
1080 elmex 1.1
1081 root 1.5 switch (book->type)
1082     {
1083     case BOOK:
1084 root 1.2 {
1085 root 1.5 titlelist *tl = get_titlelist (msgtype);
1086     title *t = NULL;
1087     int tries = 0;
1088    
1089     /* look to see if our msg already been archived. If so, alter
1090     * the book to match the archival text. If we fail to match,
1091     * then we archive the new title/name/msg combo if there is
1092     * room on the titlelist.
1093     */
1094 root 1.2
1095 root 1.5 if ((strlen (book->msg) > 5) && (t = find_title (book, msgtype)))
1096     {
1097     object *tmpbook;
1098 root 1.2
1099 root 1.5 /* alter book properties */
1100     if ((tmpbook = get_archetype (t->archname)) != NULL)
1101     {
1102     tmpbook->msg = book->msg;
1103 root 1.13 tmpbook->copy_to (book);
1104     tmpbook->destroy ();
1105 root 1.5 }
1106 root 1.2
1107 root 1.5 book->title = t->authour;
1108     book->name = t->name;
1109     book->level = t->level;
1110     }
1111     /* Don't have any default title, so lets make up a new one */
1112     else
1113     {
1114     int numb, maxnames = max_titles[msgtype];
1115     const char *old_title;
1116     const char *old_name;
1117    
1118     old_title = book->title;
1119     old_name = book->name;
1120    
1121     /* some pre-generated books have title already set (from
1122     * maps), also don't bother looking for unique title if
1123     * we already used up all the available names! */
1124 root 1.2
1125 root 1.5 if (!tl)
1126     {
1127     LOG (llevError, "change_book_name(): can't find title list\n");
1128     numb = 0;
1129     }
1130     else
1131     numb = tl->number;
1132 elmex 1.1
1133 root 1.5 if (numb == maxnames)
1134     {
1135 elmex 1.1 #ifdef ARCHIVE_DEBUG
1136 root 1.5 LOG (llevDebug, "titles for list %d full (%d possible).\n", msgtype, maxnames);
1137 elmex 1.1 #endif
1138 root 1.5 break;
1139     }
1140     /* shouldnt change map-maker books */
1141     else if (!book->title)
1142     do
1143     {
1144     /* random book name */
1145     new_text_name (book, msgtype);
1146     add_author (book, msgtype); /* random author */
1147     tries++;
1148     }
1149     while (!unique_book (book, msgtype) && tries < MAX_TITLE_CHECK);
1150    
1151     /* Now deal with 2 cases.
1152     * 1)If no space for a new title exists lets just restore
1153     * the old book properties. Remember, if the book had
1154     * matchd an older entry on the titlelist, we shouldnt
1155     * have called this routine in the first place!
1156     * 2) If we got a unique title, we need to add it to
1157     * the list.
1158     */
1159 elmex 1.1
1160 root 1.5 if (tries == MAX_TITLE_CHECK || numb == maxnames)
1161     { /* got to check maxnames again */
1162 elmex 1.1 #ifdef ARCHIVE_DEBUG
1163 root 1.5 LOG (llevDebug, "Failed to obtain unique title for %s %s (names:%d/%d)\n", book->name, book->title, numb, maxnames);
1164 elmex 1.1 #endif
1165 root 1.5 /* restore old book properties here */
1166     book->title = old_title;
1167    
1168     if (RANDOM () % 4)
1169     {
1170     /* Lets give the book a description to individualize it some */
1171     char new_name[MAX_BUF];
1172    
1173     snprintf (new_name, MAX_BUF, "%s %s", book_descrpt[RANDOM () % nbr], old_name);
1174 root 1.2
1175 root 1.5 book->name = new_name;
1176     }
1177     else
1178     {
1179     book->name = old_name;
1180 root 1.2 }
1181 root 1.5 }
1182     else if (book->title && strlen (book->msg) > 5)
1183     { /* archive if long msg texts */
1184     add_book_to_list (book, msgtype);
1185     }
1186     }
1187     break;
1188 root 1.2 }
1189 elmex 1.1
1190 root 1.5 default:
1191 root 1.2 LOG (llevError, "change_book_name() called w/ illegal obj type.\n");
1192     return;
1193 root 1.5 }
1194 elmex 1.1 }
1195    
1196     /*****************************************************************************
1197     *
1198     * This is the start of the area that generates the actual contents
1199     * of the book.
1200     *
1201     *****************************************************************************/
1202    
1203     /*****************************************************************************
1204     * Monster msg generation code.
1205     ****************************************************************************/
1206    
1207     /* get_random_mon() - returns a random monster slected from linked
1208     * list of all monsters in the current game. If level is non-zero,
1209     * then only monsters greater than that level will be returned.
1210     * Changed 971225 to be greater than equal to level passed. Also
1211     * made choosing by level more random.
1212     */
1213    
1214     object *
1215     get_random_mon (int level)
1216     {
1217 root 1.5 objectlink *mon = first_mon_info;
1218     int i = 0, monnr;
1219    
1220     /* safety check. Problem w/ init_mon_info list? */
1221     if (!nrofmon || !mon)
1222     return (object *) NULL;
1223    
1224     if (!level)
1225     {
1226     /* lets get a random monster from the mon_info linked list */
1227     monnr = RANDOM () % nrofmon;
1228 elmex 1.1
1229 root 1.5 for (mon = first_mon_info, i = 0; mon; mon = mon->next)
1230     if (i++ == monnr)
1231     break;
1232    
1233     if (!mon)
1234     {
1235     LOG (llevError, "get_random_mon: Didn't find a monster when we should have\n");
1236 root 1.2 return NULL;
1237 root 1.5 }
1238     return mon->ob;
1239     }
1240 elmex 1.1
1241 root 1.5 /* Case where we are searching by level. Redone 971225 to be clearer
1242     * and more random. Before, it looks like it took a random monster from
1243     * the list, and then returned the first monster after that which was
1244     * appropriate level. This wasn't very random because if you had a
1245     * bunch of low level monsters and then a high level one, if the random
1246     * determine took one of the low level ones, it would just forward to the
1247     * high level one and return that. Thus, monsters that immediatly followed
1248     * a bunch of low level monsters would be more heavily returned. It also
1249     * means some of the dragons would be poorly represented, since they
1250     * are a group of high level monsters all around each other.
1251     */
1252    
1253     /* First count number of monsters meeting level criteria */
1254     for (mon = first_mon_info, i = 0; mon; mon = mon->next)
1255     if (mon->ob->level >= level)
1256     i++;
1257    
1258     if (i == 0)
1259     {
1260     LOG (llevError, "get_random_mon() couldn't return monster for level %d\n", level);
1261     return NULL;
1262     }
1263    
1264     monnr = RANDOM () % i;
1265     for (mon = first_mon_info; mon; mon = mon->next)
1266     if (mon->ob->level >= level && monnr-- == 0)
1267     return mon->ob;
1268    
1269     if (!mon)
1270     {
1271     LOG (llevError, "get_random_mon(): didn't find a monster when we should have\n");
1272     return NULL;
1273     }
1274     return NULL; /* Should be unreached, by keeps warnings down */
1275 elmex 1.1 }
1276    
1277     /*
1278     * Returns a description of the monster. This really needs to be
1279     * redone, as describe_item really gives a pretty internal description.
1280     */
1281    
1282 root 1.5 char *
1283 elmex 1.1 mon_desc (const object *mon)
1284     {
1285 root 1.5 static char retbuf[HUGE_BUF];
1286 elmex 1.1
1287 root 1.5 sprintf (retbuf, " *** %s ***\n", &mon->name);
1288     strcat (retbuf, describe_item (mon, NULL));
1289 elmex 1.1
1290 root 1.5 return retbuf;
1291 elmex 1.1 }
1292    
1293    
1294     /* This function returns the next monsters after 'tmp'. If no match is
1295     * found, it returns NULL (changed 0.94.3 to do this, since the
1296     * calling function (mon_info_msg) seems to expect that.
1297     */
1298    
1299     object *
1300     get_next_mon (object *tmp)
1301     {
1302 root 1.5 objectlink *mon;
1303 elmex 1.1
1304 root 1.5 for (mon = first_mon_info; mon; mon = mon->next)
1305     if (mon->ob == tmp)
1306     break;
1307    
1308     /* didn't find a match */
1309     if (!mon)
1310     return NULL;
1311     if (mon->next)
1312     return mon->next->ob;
1313     else
1314     return first_mon_info->ob;
1315 elmex 1.1
1316     }
1317    
1318    
1319    
1320     /* mon_info_msg() - generate a message detailing the properties
1321     * of a randomly selected monster.
1322     */
1323    
1324 root 1.5 char *
1325 elmex 1.1 mon_info_msg (int level, int booksize)
1326     {
1327 root 1.5 static char retbuf[BOOK_BUF];
1328     char tmpbuf[HUGE_BUF];
1329     object *tmp;
1330    
1331     /*preamble */
1332     strcpy (retbuf, "This beastiary contains:");
1333    
1334     /* lets print info on as many monsters as will fit in our
1335     * document.
1336     * 8-96 Had to change this a bit, otherwise there would
1337     * have been an impossibly large number of combinations
1338     * of text! (and flood out the available number of titles
1339     * in the archive in a snap!) -b.t.
1340     */
1341     tmp = get_random_mon (level * 3);
1342     while (tmp)
1343     {
1344     /* monster description */
1345     sprintf (tmpbuf, "\n---\n%s", mon_desc (tmp));
1346    
1347     if (!book_overflow (retbuf, tmpbuf, booksize))
1348     strcat (retbuf, tmpbuf);
1349     else
1350     break;
1351 elmex 1.1
1352 root 1.5 /* Note that the value this returns is not based on level */
1353     tmp = get_next_mon (tmp);
1354 elmex 1.1 }
1355    
1356     #ifdef BOOK_MSG_DEBUG
1357 root 1.5 LOG (llevDebug, "\n mon_info_msg() created strng: %d\n", strlen (retbuf));
1358     fprintf (logfile, " MADE THIS:\n%s\n", retbuf);
1359 elmex 1.1 #endif
1360    
1361 root 1.5 return retbuf;
1362 elmex 1.1 }
1363    
1364    
1365     /*****************************************************************************
1366     * Artifact msg generation code.
1367     ****************************************************************************/
1368    
1369     /* artifact_msg() - generate a message detailing the properties
1370     * of 1-6 artifacts drawn sequentially from the artifact list.
1371     */
1372    
1373 root 1.5 char *
1374 elmex 1.1 artifact_msg (int level, int booksize)
1375     {
1376 root 1.5 artifactlist *al = NULL;
1377     artifact *art;
1378     int chance, i, type, index;
1379     int book_entries = level > 5 ? RANDOM () % 3 + RANDOM () % 3 + 2 : RANDOM () % level + 1;
1380     char *ch, name[MAX_BUF], buf[BOOK_BUF], sbuf[MAX_BUF];
1381     static char retbuf[BOOK_BUF];
1382     object *tmp = NULL;
1383    
1384     /* values greater than 5 create msg buffers that are too big! */
1385     if (book_entries > 5)
1386     book_entries = 5;
1387    
1388     /* lets determine what kind of artifact type randomly.
1389     * Right now legal artifacts only come from those listed
1390     * in art_name_array. Also, we check to be sure an artifactlist
1391     * for that type exists!
1392     */
1393     i = 0;
1394     do
1395     {
1396     index = RANDOM () % (sizeof (art_name_array) / sizeof (arttypename));
1397     type = art_name_array[index].type;
1398     al = find_artifactlist (type);
1399     i++;
1400     }
1401     while ((al == NULL) && (i < 10));
1402    
1403     if (i == 10) /* Unable to find a message */
1404     return ("None");
1405 elmex 1.1
1406 root 1.5 /* There is no reason to start on the artifact list at the begining. Lets
1407     * take our starting position randomly... */
1408     art = al->items;
1409     for (i = RANDOM () % level + RANDOM () % 2 + 1; i > 0; i--)
1410     {
1411     if (art == NULL)
1412     art = al->items; /* hmm, out of stuff, loop back around */
1413     art = art->next;
1414     }
1415    
1416     /* the base 'generic' name for our artifact */
1417     strcpy (name, art_name_array[index].name);
1418    
1419     /* Ok, lets print out the contents */
1420     sprintf (retbuf, "Herein %s detailed %s...\n", book_entries > 1 ? "are" : "is", book_entries > 1 ? "some artifacts" : "an artifact");
1421    
1422     /* artifact msg attributes loop. Lets keep adding entries to the 'book'
1423     * as long as we have space up to the allowed max # (book_entires)
1424     */
1425     while (book_entries > 0)
1426     {
1427    
1428     if (art == NULL)
1429     art = al->items;
1430 elmex 1.1
1431 root 1.5 /* separator of items */
1432     strcpy (buf, "--- \n");
1433    
1434     /* Name */
1435     if (art->allowed != NULL && strcmp (art->allowed->name, "All"))
1436     {
1437     linked_char *temp, *next = art->allowed;
1438    
1439     do
1440 root 1.2 {
1441 root 1.5 temp = next;
1442     next = next->next;
1443 root 1.2 }
1444 root 1.5 while (next && !RANDOM () % 2);
1445     sprintf (buf, "%s A %s of %s", buf, &temp->name, &art->item->name);
1446     }
1447     else /* default name is used */
1448     sprintf (buf, "%s The %s of %s", buf, name, &art->item->name);
1449 root 1.2
1450 root 1.5 /* chance of finding */
1451     chance = (int) (100 * ((float) art->chance / al->total_chance));
1452     if (chance >= 20)
1453     sprintf (sbuf, "an uncommon");
1454     else if (chance >= 10)
1455     sprintf (sbuf, "an unusual");
1456     else if (chance >= 5)
1457     sprintf (sbuf, "a rare");
1458     else
1459     sprintf (sbuf, "a very rare");
1460     sprintf (buf, "%s is %s\n", buf, sbuf);
1461    
1462     /* value of artifact */
1463     sprintf (buf, "%s item with a value that is %d times normal.\n", buf, art->item->value);
1464    
1465     /* include the message about the artifact, if exists, and book
1466     * level is kinda high */
1467     if (art->item->msg && (RANDOM () % 4 + 1) < level && !((strlen (art->item->msg) + strlen (buf)) > BOOK_BUF))
1468     strcat (buf, art->item->msg);
1469    
1470     /* properties of the artifact */
1471 root 1.13 tmp = object::create ();
1472 root 1.5 add_abilities (tmp, art->item);
1473     tmp->type = type;
1474     SET_FLAG (tmp, FLAG_IDENTIFIED);
1475     if ((ch = describe_item (tmp, NULL)) != NULL && strlen (ch) > 1)
1476     sprintf (buf, "%s Properties of this artifact include: \n %s \n", buf, ch);
1477 root 1.13 tmp->destroy ();
1478 root 1.5 /* add the buf if it will fit */
1479     if (!book_overflow (retbuf, buf, booksize))
1480     strcat (retbuf, buf);
1481     else
1482     break;
1483 root 1.2
1484 root 1.5 art = art->next;
1485     book_entries--;
1486     }
1487 elmex 1.1
1488     #ifdef BOOK_MSG_DEBUG
1489 root 1.5 LOG (llevDebug, "artifact_msg() created strng: %d\n", strlen (retbuf));
1490     fprintf (logfile, " MADE THIS:\n%s", retbuf);
1491 elmex 1.1 #endif
1492 root 1.5 return retbuf;
1493 elmex 1.1 }
1494    
1495     /*****************************************************************************
1496     * Spellpath message generation
1497     *****************************************************************************/
1498    
1499     /* spellpath_msg() - generate a message detailing the member
1500     * incantations/prayers (and some of their properties) belonging to
1501     * a given spellpath.
1502     */
1503    
1504 root 1.5 char *
1505 elmex 1.1 spellpath_msg (int level, int booksize)
1506     {
1507 root 1.5 static char retbuf[BOOK_BUF];
1508     char tmpbuf[BOOK_BUF];
1509     int path = RANDOM () % NRSPELLPATHS, prayers = RANDOM () % 2;
1510     int did_first_sp = 0;
1511     uint32 pnum = (path == -1) ? PATH_NULL : spellpathdef[path];
1512     archetype *at;
1513    
1514     /* Preamble */
1515     sprintf (retbuf, "Herein are detailed the names of %s\n", prayers ? "prayers" : "incantations");
1516    
1517     if (path == -1)
1518     strcat (retbuf, "having no known spell path.\n");
1519     else
1520     sprintf (retbuf, "%sbelonging to the path of %s:\n", retbuf, spellpathnames[path]);
1521    
1522     for (at = first_archetype; at != NULL; at = at->next)
1523     {
1524     /* Determine if this is an appropriate spell. Must
1525     * be of matching path, must be of appropriate type (prayer
1526     * or not), and must be within the valid level range.
1527     */
1528     if (at->clone.type == SPELL && at->clone.path_attuned & pnum &&
1529     ((at->clone.stats.grace && prayers) || (at->clone.stats.sp && !prayers)) && (at->clone.level < (level * 8)))
1530     {
1531     strcpy (tmpbuf, at->clone.name);
1532 elmex 1.1
1533 root 1.5 if (book_overflow (retbuf, tmpbuf, booksize))
1534     break;
1535     else
1536     {
1537     if (did_first_sp)
1538     strcat (retbuf, ",\n");
1539     did_first_sp = 1;
1540     strcat (retbuf, tmpbuf);
1541 root 1.2 }
1542 root 1.5 }
1543 elmex 1.1 }
1544 root 1.5 /* Geez, no spells were generated. */
1545     if (!did_first_sp)
1546     {
1547     if (RANDOM () % 4) /* usually, lets make a recursive call... */
1548     spellpath_msg (level, booksize);
1549     else /* give up, cause knowing no spells exist for path is info too. */
1550     strcat (retbuf, "\n - no known spells exist -\n");
1551 elmex 1.1 }
1552 root 1.5 else
1553     {
1554     strcat (retbuf, "\n");
1555 elmex 1.1 }
1556 root 1.5 return retbuf;
1557 elmex 1.1 }
1558    
1559    
1560    
1561     /* formula_msg() - generate a message detailing the properties
1562     * of a randomly selected alchemical formula.
1563     */
1564    
1565 root 1.5 void
1566     make_formula_book (object *book, int level)
1567     {
1568     char retbuf[BOOK_BUF], title[MAX_BUF];
1569     recipelist *fl;
1570     recipe *formula = NULL;
1571     int chance;
1572    
1573     /* the higher the book level, the more complex (ie number of
1574     * ingredients) the formula can be.
1575     */
1576     fl = get_formulalist (((RANDOM () % level) / 3) + 1);
1577    
1578     if (!fl)
1579     fl = get_formulalist (1); /* safety */
1580    
1581     if (fl->total_chance == 0)
1582     {
1583     book->msg = "<indecipherable text>\n";
1584     new_text_name (book, 4);
1585     add_author (book, 4);
1586     return;
1587     }
1588    
1589     /* get a random formula, weighted by its bookchance */
1590     chance = RANDOM () % fl->total_chance;
1591     for (formula = fl->items; formula != NULL; formula = formula->next)
1592     {
1593     chance -= formula->chance;
1594     if (chance <= 0)
1595     break;
1596     }
1597    
1598     if (!formula || formula->arch_names <= 0)
1599     {
1600     book->msg = "<indecipherable text>\n";
1601     new_text_name (book, 4);
1602     add_author (book, 4);
1603    
1604 elmex 1.1 }
1605 root 1.5 else
1606     {
1607     /* looks like a formula was found. Base the amount
1608     * of information on the booklevel and the spellevel
1609     * of the formula. */
1610    
1611     const char *op_name = formula->arch_name[RANDOM () % formula->arch_names];
1612     archetype *at;
1613    
1614     /* preamble */
1615     sprintf (retbuf, "Herein is described a project using %s: \n", formula->skill ? &formula->skill : "an unknown skill");
1616    
1617 root 1.8 if ((at = archetype::find (op_name)) != (archetype *) NULL)
1618 root 1.5 op_name = at->clone.name;
1619     else
1620     LOG (llevError, "formula_msg() can't find arch %s for formula.\n", op_name);
1621 elmex 1.1
1622 root 1.5 /* item name */
1623     if (strcmp (formula->title, "NONE"))
1624     {
1625     sprintf (retbuf, "%sThe %s of %s", retbuf, op_name, &formula->title);
1626     /* This results in things like pile of philo. sulfur.
1627     * while philo. sulfur may look better, without this,
1628     * you get things like 'the wise' because its missing the
1629     * water of section.
1630     */
1631     sprintf (title, "%s: %s of %s",
1632     formula_book_name[RANDOM () % (sizeof (formula_book_name) / sizeof (char *))], op_name, &formula->title);
1633 root 1.2 }
1634 root 1.5 else
1635 root 1.2 {
1636 root 1.5 sprintf (retbuf, "%sThe %s", retbuf, op_name);
1637     sprintf (title, "%s: %s", formula_book_name[RANDOM () % (sizeof (formula_book_name) / sizeof (char *))], op_name);
1638     if (at->clone.title)
1639     {
1640     strcat (retbuf, " ");
1641     strcat (retbuf, at->clone.title);
1642     strcat (title, " ");
1643     strcat (title, at->clone.title);
1644 root 1.2 }
1645     }
1646 root 1.5 /* Lets name the book something meaningful ! */
1647     book->name = title;
1648     book->title = NULL;
1649 root 1.2
1650 root 1.5 /* ingredients to make it */
1651     if (formula->ingred != NULL)
1652 root 1.2 {
1653 root 1.5 linked_char *next;
1654     archetype *at;
1655 root 1.2
1656 root 1.8 at = archetype::find (formula->cauldron);
1657 root 1.2
1658 root 1.5 sprintf (retbuf + strlen (retbuf),
1659     " may be made at %s using the following ingredients:\n", at ? query_name (&at->clone) : "an unknown place");
1660 root 1.2
1661 root 1.5 for (next = formula->ingred; next != NULL; next = next->next)
1662 root 1.2 {
1663 root 1.5 strcat (retbuf, next->name);
1664     strcat (retbuf, "\n");
1665 root 1.2 }
1666     }
1667 root 1.5 else
1668     LOG (llevError, "formula_msg() no ingredient list for object %s of %s\n", op_name, &formula->title);
1669     if (retbuf[strlen (retbuf) - 1] != '\n')
1670     strcat (retbuf, "\n");
1671 root 1.3
1672 root 1.5 book->msg = retbuf;
1673 elmex 1.1 }
1674     }
1675    
1676    
1677     /* msgfile_msg() - generate a message drawn randomly from a
1678     * file in lib/. Level currently has no effect on the message
1679     * which is returned.
1680     */
1681    
1682 root 1.5 char *
1683 elmex 1.1 msgfile_msg (int level, int booksize)
1684     {
1685 root 1.5 static char retbuf[BOOK_BUF];
1686     int i, msgnum;
1687     linked_char *msg = NULL;
1688    
1689     /* get a random message for the 'book' from linked list */
1690     if (nrofmsg > 1)
1691     {
1692     msg = first_msg;
1693     msgnum = RANDOM () % nrofmsg;
1694     for (i = 0; msg && i < nrofmsg && i != msgnum; i++)
1695     msg = msg->next;
1696     }
1697 elmex 1.1
1698 root 1.5 if (msg && !book_overflow (retbuf, msg->name, booksize))
1699     strcpy (retbuf, msg->name);
1700     else
1701     sprintf (retbuf, "\n <undecipherable text>");
1702 elmex 1.1
1703     #ifdef BOOK_MSG_DEBUG
1704 root 1.5 LOG (llevDebug, "\n info_list_msg() created strng: %d\n", strlen (retbuf));
1705     LOG (llevDebug, " MADE THIS:\n%s\n", retbuf);
1706 elmex 1.1 #endif
1707    
1708 root 1.5 return retbuf;
1709 elmex 1.1 }
1710    
1711    
1712     /* god_info_msg() - generate a message detailing the properties
1713     * of a random god. Used by the book hack. b.t.
1714     */
1715    
1716 root 1.5 const char *
1717 elmex 1.1 god_info_msg (int level, int booksize)
1718     {
1719 root 1.5 static char retbuf[BOOK_BUF];
1720     const char *name = NULL;
1721     char buf[BOOK_BUF];
1722     int i;
1723     size_t introlen;
1724     object *god = pntr_to_god_obj (get_rand_god ());
1725    
1726     if (!god)
1727     return (char *) NULL; /* oops, problems... */
1728     name = god->name;
1729    
1730     /* preamble.. */
1731     sprintf (retbuf, "This document contains knowledge concerning\n");
1732     sprintf (retbuf, "%sthe diety %s", retbuf, name);
1733    
1734     /* Always have as default information the god's descriptive terms. */
1735     if (nstrtok (god->msg, ",") > 0)
1736     {
1737     strcat (retbuf, ", known as");
1738     strcat (retbuf, strtoktolin (god->msg, ","));
1739     }
1740     else
1741     strcat (retbuf, "...");
1742    
1743     strcat (retbuf, "\n ---\n");
1744     introlen = strlen (retbuf); /* so we will know if no new info is added later */
1745 elmex 1.1
1746 root 1.5 /* Information about the god is random, and based on the level of the
1747     * 'book'. Probably there is a more intellegent way to implement
1748     * this ...
1749     */
1750    
1751     while (level > 0)
1752     {
1753     sprintf (buf, " ");
1754     if (level == 2 && RANDOM () % 2)
1755     { /* enemy god */
1756     const char *enemy = god->title;
1757 elmex 1.1
1758 root 1.5 if (enemy)
1759     sprintf (buf, "The gods %s and %s are enemies.\n ---\n", name, enemy);
1760     }
1761     if (level == 3 && RANDOM () % 2)
1762     { /* enemy race, what the god's holy word effects */
1763     const char *enemy = god->slaying;
1764    
1765     if (enemy && !(god->path_denied & PATH_TURNING))
1766     if ((i = nstrtok (enemy, ",")) > 0)
1767     {
1768     char tmpbuf[MAX_BUF];
1769    
1770     sprintf (buf, "The holy words of %s have the power to\n", name);
1771     strcat (buf, "slay creatures belonging to the ");
1772     if (i > 1)
1773     sprintf (tmpbuf, "following \n races:%s", strtoktolin (enemy, ","));
1774     else
1775     sprintf (tmpbuf, "race of%s", strtoktolin (enemy, ","));
1776     sprintf (buf, "%s%s\n ---\n", buf, tmpbuf);
1777     }
1778     }
1779     if (level == 4 && RANDOM () % 2)
1780     { /* Priest of god gets these protect,vulnerable... */
1781     char tmpbuf[MAX_BUF], *cp;
1782    
1783     cp = describe_resistance (god, 1);
1784    
1785     if (*cp)
1786     { /* This god does have protections */
1787     sprintf (tmpbuf, "%s has a potent aura which is extended\n", name);
1788     strcat (tmpbuf, "faithful priests. The effects of this aura include:\n");
1789     strcat (tmpbuf, cp);
1790     strcat (buf, tmpbuf);
1791     strcat (buf, "\n ---\n");
1792     }
1793     else
1794     sprintf (buf, " ");
1795     }
1796     if (level == 5 && RANDOM () % 2)
1797     { /* aligned race, summoning */
1798     const char *race = god->race; /* aligned race */
1799    
1800     if (race && !(god->path_denied & PATH_SUMMON))
1801     if ((i = nstrtok (race, ",")) > 0)
1802     {
1803     char tmpbuf[MAX_BUF];
1804    
1805     sprintf (buf, "Creatures sacred to %s include the \n", name);
1806     if (i > 1)
1807     sprintf (tmpbuf, "following \n races:%s", strtoktolin (race, ","));
1808     else
1809     sprintf (tmpbuf, "race of%s", strtoktolin (race, ","));
1810     sprintf (buf, "%s%s\n ---\n", buf, tmpbuf);
1811     }
1812     }
1813     if (level == 6 && RANDOM () % 2)
1814     { /* blessing,curse properties of the god */
1815     char tmpbuf[MAX_BUF], *cp;
1816    
1817     cp = describe_resistance (god, 1);
1818    
1819     if (*cp)
1820     { /* This god does have protections */
1821     sprintf (tmpbuf, "\nThe priests of %s are known to be able to \n", name);
1822     strcat (tmpbuf, "bestow a blessing which makes the recipient\n");
1823     strcat (tmpbuf, cp);
1824     strcat (buf, tmpbuf);
1825     strcat (buf, "\n ---\n");
1826 root 1.2 }
1827 root 1.5 else
1828     sprintf (buf, " ");
1829    
1830     }
1831     if (level == 8 && RANDOM () % 2)
1832     { /* immunity, holy possession */
1833     int has_effect = 0, tmpvar;
1834     char tmpbuf[MAX_BUF];
1835    
1836     sprintf (tmpbuf, "\n");
1837     sprintf (tmpbuf, "The priests of %s are known to make cast a mighty \n", name);
1838    
1839     strcat (tmpbuf, "prayer of possession which gives the recipient\n");
1840    
1841     for (tmpvar = 0; tmpvar < NROFATTACKS; tmpvar++)
1842     {
1843     if (god->resist[tmpvar] == 100)
1844     {
1845     has_effect = 1;
1846     sprintf (tmpbuf + strlen (tmpbuf), "Immunity to %s", attacktype_desc[tmpvar]);
1847 root 1.2 }
1848     }
1849 root 1.5 if (has_effect)
1850     {
1851     strcat (buf, tmpbuf);
1852     strcat (buf, "\n ---\n");
1853     }
1854     else
1855     sprintf (buf, " ");
1856     }
1857     if (level == 12 && RANDOM () % 2)
1858     { /* spell paths */
1859     int has_effect = 0, tmpvar;
1860     char tmpbuf[MAX_BUF];
1861    
1862     sprintf (tmpbuf, "\n");
1863     sprintf (tmpbuf, "It is rarely known fact that the priests of %s\n", name);
1864     strcat (tmpbuf, "are mystically transformed. Effects of this include:\n");
1865     if ((tmpvar = god->path_attuned))
1866     {
1867     has_effect = 1;
1868     DESCRIBE_PATH (tmpbuf, tmpvar, "Attuned");
1869 root 1.2 }
1870 root 1.5 if ((tmpvar = god->path_repelled))
1871     {
1872     has_effect = 1;
1873     DESCRIBE_PATH (tmpbuf, tmpvar, "Repelled");
1874 root 1.2 }
1875 root 1.5 if ((tmpvar = god->path_denied))
1876     {
1877     has_effect = 1;
1878     DESCRIBE_PATH (tmpbuf, tmpvar, "Denied");
1879 root 1.2 }
1880 root 1.5 if (has_effect)
1881     {
1882     strcat (buf, tmpbuf);
1883     strcat (buf, "\n ---\n");
1884 root 1.2 }
1885 root 1.5 else
1886     sprintf (buf, " ");
1887     }
1888 root 1.2
1889 root 1.5 /* check to be sure new buffer size dont exceed either
1890     * the maximum buffer size, or the 'natural' size of the
1891     * book...
1892     */
1893     if (book_overflow (retbuf, buf, booksize))
1894     break;
1895     else if (strlen (buf) > 1)
1896     strcat (retbuf, buf);
1897     level--;
1898     }
1899     if (strlen (retbuf) == introlen)
1900     { /* we got no information beyond the preamble! */
1901     strcat (retbuf, " [Unfortunately the rest of the information is\n");
1902     strcat (retbuf, " hopelessly garbled!]\n ---\n");
1903     }
1904 elmex 1.1 #ifdef BOOK_MSG_DEBUG
1905 root 1.5 LOG (llevDebug, "\n god_info_msg() created strng: %d\n", strlen (retbuf));
1906     fprintf (logfile, " MADE THIS:\n%s", retbuf);
1907 elmex 1.1 #endif
1908 root 1.5 return retbuf;
1909 elmex 1.1 }
1910    
1911     /* tailor_readable_ob()- The main routine. This chooses a random
1912     * message to put in given readable object (type==BOOK) which will
1913     * be referred hereafter as a 'book'. We use the book level to de-
1914     * termine the value of the information we will insert. Higher
1915     * values mean the book will (generally) have better/more info.
1916     * See individual cases as to how this will be utilized.
1917     * "Book" name/content length are based on the weight of the
1918     * document. If the value of msg_type is negative, we will randomly
1919     * choose the kind of message to generate.
1920     * -b.t. thomas@astro.psu.edu
1921     *
1922     * book is the object we are creating into.
1923     * If msg_type is a positive value, we use that to determine the
1924     * message type - otherwise a random value is used.
1925     *
1926     */
1927    
1928 root 1.5 void
1929 elmex 1.1 tailor_readable_ob (object *book, int msg_type)
1930     {
1931 root 1.5 char msgbuf[BOOK_BUF];
1932     int level = book->level ? (RANDOM () % book->level) + 1 : 1;
1933     int book_buf_size;
1934    
1935     /* safety */
1936     if (book->type != BOOK)
1937     return;
1938    
1939     if (level <= 0)
1940     return; /* if no level no point in doing any more... */
1941    
1942     /* Max text length this book can have. */
1943     book_buf_size = BOOKSIZE (book);
1944    
1945     /* &&& The message switch &&& */
1946     /* Below all of the possible types of messages in the "book"s.
1947     */
1948     /*
1949     * IF you add a new type of book msg, you will have to do several things.
1950     * 1) make sure there is an entry in the msg switch below!
1951     * 2) make sure there is an entry in max_titles[] array.
1952     * 3) make sure there are entries for your case in new_text_title()
1953     * and add_authour().
1954     * 4) you may want separate authour/book name arrays in read.h
1955     */
1956    
1957     msg_type = msg_type > 0 ? msg_type : (RANDOM () % 6);
1958     switch (msg_type)
1959     {
1960     case 1: /* monster attrib */
1961 root 1.2 strcpy (msgbuf, mon_info_msg (level, book_buf_size));
1962     break;
1963 root 1.5 case 2: /* artifact attrib */
1964 root 1.2 strcpy (msgbuf, artifact_msg (level, book_buf_size));
1965     break;
1966 root 1.5 case 3: /* grouping incantations/prayers by path */
1967 root 1.2 strcpy (msgbuf, spellpath_msg (level, book_buf_size));
1968     break;
1969 root 1.5 case 4: /* describe an alchemy formula */
1970     make_formula_book (book, level);
1971     /* make_formula_book already gives title */
1972     return;
1973     break;
1974     case 5: /* bits of information about a god */
1975 root 1.2 strcpy (msgbuf, god_info_msg (level, book_buf_size));
1976     break;
1977 root 1.5 case 0: /* use info list in lib/ */
1978     default:
1979 root 1.2 strcpy (msgbuf, msgfile_msg (level, book_buf_size));
1980     break;
1981 root 1.5 }
1982 elmex 1.1
1983 root 1.5 strcat (msgbuf, "\n"); /* safety -- we get ugly map saves/crashes w/o this */
1984 root 1.10
1985 root 1.5 if (strlen (msgbuf) > 1)
1986     {
1987     book->msg = msgbuf;
1988     /* lets give the "book" a new name, which may be a compound word */
1989     change_book (book, msg_type);
1990     }
1991 elmex 1.1
1992     }
1993    
1994    
1995     /*****************************************************************************
1996     *
1997     * Cleanup routine for readble stuff.
1998     *
1999     *****************************************************************************/
2000    
2001 root 1.5 void
2002 elmex 1.1 free_all_readable (void)
2003     {
2004 root 1.5 titlelist *tlist, *tnext;
2005     title *title1, *titlenext;
2006     linked_char *lmsg, *nextmsg;
2007     objectlink *monlink, *nextmon;
2008    
2009     LOG (llevDebug, "freeing all book information\n");
2010    
2011     for (tlist = booklist; tlist != NULL; tlist = tnext)
2012     {
2013     tnext = tlist->next;
2014    
2015     for (title1 = tlist->first_book; title1; title1 = titlenext)
2016     {
2017     titlenext = title1->next;
2018     delete title1;
2019     }
2020    
2021     delete tlist;
2022     }
2023 root 1.10
2024 root 1.5 for (lmsg = first_msg; lmsg; lmsg = nextmsg)
2025     {
2026     nextmsg = lmsg->next;
2027     delete lmsg;
2028     }
2029 root 1.10
2030 root 1.5 for (monlink = first_mon_info; monlink; monlink = nextmon)
2031     {
2032     nextmon = monlink->next;
2033 root 1.11 delete monlink;
2034 root 1.5 }
2035 elmex 1.1 }
2036    
2037    
2038     /*****************************************************************************
2039     *
2040     * Writeback routine for updating the bookarchive.
2041     *
2042     ****************************************************************************/
2043    
2044     /* write_book_archive() - write out the updated book archive */
2045    
2046 root 1.5 void
2047 elmex 1.1 write_book_archive (void)
2048     {
2049 root 1.5 FILE *fp;
2050     int index = 0;
2051     char fname[MAX_BUF];
2052     title *book = NULL;
2053     titlelist *bl = get_titlelist (0);
2054    
2055     /* If nothing changed, don't write anything */
2056     if (!need_to_write_bookarchive)
2057     return;
2058     need_to_write_bookarchive = 0;
2059    
2060     sprintf (fname, "%s/bookarch", settings.localdir);
2061     LOG (llevDebug, "Updating book archive: %s...\n", fname);
2062    
2063     if ((fp = fopen (fname, "w")) == NULL)
2064     {
2065     LOG (llevDebug, "Can't open book archive file %s\n", fname);
2066     }
2067     else
2068     {
2069     while (bl)
2070     {
2071     for (book = bl->first_book; book; book = book->next)
2072     if (book && book->authour)
2073     {
2074     fprintf (fp, "title %s\n", &book->name);
2075     fprintf (fp, "authour %s\n", &book->authour);
2076     fprintf (fp, "arch %s\n", &book->archname);
2077     fprintf (fp, "level %d\n", book->level);
2078     fprintf (fp, "type %d\n", index);
2079     fprintf (fp, "size %d\n", book->size);
2080     fprintf (fp, "index %d\n", book->msg_index);
2081     fprintf (fp, "end\n");
2082     }
2083     bl = bl->next;
2084     index++;
2085     }
2086     fclose (fp);
2087     chmod (fname, SAVE_MODE);
2088     }
2089     }
2090     readable_message_type *
2091     get_readable_message_type (object *readable)
2092     {
2093     uint8 subtype = readable->subtype;
2094    
2095     if (subtype > last_readable_subtype)
2096     return &(readable_message_types[0]);
2097     return &(readable_message_types[subtype]);
2098 elmex 1.1 }