ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/readable.C
Revision: 1.31
Committed: Tue Jul 10 06:05:55 2007 UTC (16 years, 10 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.30: +15 -1 lines
Log Message:
riddifed the world of safe_strcat

File Contents

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