ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/readable.C
Revision: 1.44
Committed: Thu Oct 15 21:40:42 2009 UTC (14 years, 7 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.43: +2 -2 lines
Log Message:
cleanups

File Contents

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