ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/readable.C
Revision: 1.6
Committed: Wed Sep 13 23:32:04 2006 UTC (17 years, 8 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.5: +13 -13 lines
Log Message:
*** empty log message ***

File Contents

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