ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/readable.C
Revision: 1.2
Committed: Tue Aug 29 08:01:35 2006 UTC (17 years, 8 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.1: +735 -735 lines
Log Message:
expand initial tabs to spaces

File Contents

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