ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/readable.C
Revision: 1.45
Committed: Thu Oct 15 22:50:41 2009 UTC (14 years, 7 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.44: +50 -71 lines
Log Message:
improve some book messages

File Contents

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