ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/readable.C
Revision: 1.52
Committed: Fri Nov 6 13:31:47 2009 UTC (14 years, 6 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.51: +0 -57 lines
Log Message:
remove or document dead code

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