ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/readable.C
Revision: 1.58
Committed: Thu Apr 15 21:49:15 2010 UTC (14 years, 2 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.57: +54 -70 lines
Log Message:
bookarch parser et al.

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