ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/readable.C
Revision: 1.18
Committed: Mon Jan 15 21:06:18 2007 UTC (17 years, 4 months ago) by pippijn
Content type: text/plain
Branch: MAIN
Changes since 1.17: +22 -22 lines
Log Message:
comments

File Contents

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