ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/readable.C
Revision: 1.68
Committed: Sun Jan 29 02:47:04 2017 UTC (7 years, 3 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.67: +42 -42 lines
Log Message:
remove eol whitespace

File Contents

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