ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/readable.C
Revision: 1.64
Committed: Fri Jan 27 22:01:46 2012 UTC (12 years, 3 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.63: +0 -2 lines
Log Message:
Jonathan Neuschäfer [PATCH 1/9] common/item: remove redundant "type == ROD"
Jonathan Neuschäfer [PATCH 2/9] common/readable: remove fixed-size buffers in spellpath_msg
Jonathan Neuschäfer [PATCH 3/9] server/attack: remove unused struct att_msg_str
Jonathan Neuschäfer [PATCH 4/9] server/c_misc: remove unused typedef'd struct chars_names
Jonathan Neuschäfer [PATCH 5/9] server/attack: hit_map: return retflag, as promised
Jonathan Neuschäfer [PATCH 6/9] random_maps/wall: remove redundant check
Jonathan Neuschäfer [PATCH 7/9] server/spell_attack: fix function name in comment
Jonathan Neuschäfer [PATCH 9/9] socket/info: fix memory leak in error path

File Contents

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