ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/readable.C
Revision: 1.49
Committed: Fri Oct 16 00:02:22 2009 UTC (14 years, 7 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.48: +12 -12 lines
Log Message:
avoid monster heads for info

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