ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/readable.C
Revision: 1.69
Committed: Sat Nov 17 23:40:00 2018 UTC (5 years, 6 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.68: +1 -0 lines
Log Message:
copyright update 2018

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