ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/readable.C
Revision: 1.54
Committed: Sun Nov 29 10:55:18 2009 UTC (14 years, 5 months ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: rel-2_92, rel-2_93
Changes since 1.53: +5 -5 lines
Log Message:
indent (remove useless use of void)

File Contents

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