ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/readable.C
Revision: 1.39
Committed: Wed Dec 31 17:35:37 2008 UTC (15 years, 4 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.38: +4 -7 lines
Log Message:
refactoring of shstr classe,s new shstr_tmp, lots of minor rewriting

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