ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/readable.C
Revision: 1.23
Committed: Thu May 17 21:32:08 2007 UTC (17 years ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.22: +1 -1 lines
Log Message:
- prepare common/ for head_ => head change
- add some copyrights for files where they were missing

File Contents

# Content
1 /*
2 * CrossFire, A Multiplayer game
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 {"Girdle", 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 assign (name, mon_book_name[rndm (nbr)]);
927 break;
928 case 2: /*artifact */
929 nbr = sizeof (art_book_name) / sizeof (char *);
930 assign (name, art_book_name[rndm (nbr)]);
931 break;
932 case 3: /*spellpath */
933 nbr = sizeof (path_book_name) / sizeof (char *);
934 assign (name, path_book_name[rndm (nbr)]);
935 break;
936 case 4: /*alchemy */
937 nbr = sizeof (formula_book_name) / sizeof (char *);
938 assign (name, formula_book_name[rndm (nbr)]);
939 break;
940 case 5: /*gods */
941 nbr = sizeof (gods_book_name) / sizeof (char *);
942 assign (name, gods_book_name[rndm (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 assign (name, heavy_book_name[rndm (nbr)]);
950 }
951 else if (book->weight < 2001)
952 {
953 nbr = sizeof (light_book_name) / sizeof (char *);
954 assign (name, light_book_name[rndm (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 assign (name, mon_author[rndm (nbr)]);
981 break;
982 case 2: /* artifacts */
983 nbr = sizeof (art_author) / sizeof (char *);
984 assign (name, art_author[rndm (nbr)]);
985 break;
986 case 3: /* spellpath */
987 nbr = sizeof (path_author) / sizeof (char *);
988 assign (name, path_author[rndm (nbr)]);
989 break;
990 case 4: /* alchemy */
991 nbr = sizeof (formula_author) / sizeof (char *);
992 assign (name, formula_author[rndm (nbr)]);
993 break;
994 case 5: /* gods */
995 nbr = sizeof (gods_author) / sizeof (char *);
996 assign (name, gods_author[rndm (nbr)]);
997 break;
998 case 6: /* msg file */
999 default:
1000 assign (name, book_author[rndm (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[rndm (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 const char *
1374 artifact_msg (int level, int booksize)
1375 {
1376 artifactlist *al = NULL;
1377 artifact *art;
1378 int chance, i, type, index;
1379 int book_entries = level > 5 ? RANDOM () % 3 + RANDOM () % 3 + 2 : RANDOM () % level + 1;
1380 const char *ch;
1381 char 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 assign (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 assign (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 assign (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 /* god_info_msg() - generate a message detailing the properties
1713 * of a random god. Used by the book hack. b.t.
1714 */
1715 const char *
1716 god_info_msg (int level, int booksize)
1717 {
1718 static char retbuf[BOOK_BUF];
1719 const char *name = NULL;
1720 char buf[BOOK_BUF];
1721 int i;
1722 size_t introlen;
1723 object *god = pntr_to_god_obj (get_rand_god ());
1724
1725 if (!god)
1726 return (char *) NULL; /* oops, problems... */
1727 name = god->name;
1728
1729 /* preamble.. */
1730 sprintf (retbuf, "This document contains knowledge concerning\n");
1731 sprintf (retbuf, "%sthe diety %s", retbuf, name);
1732
1733 /* Always have as default information the god's descriptive terms. */
1734 if (nstrtok (god->msg, ",") > 0)
1735 {
1736 strcat (retbuf, ", known as");
1737 strcat (retbuf, strtoktolin (god->msg, ","));
1738 }
1739 else
1740 strcat (retbuf, "...");
1741
1742 strcat (retbuf, "\n ---\n");
1743 introlen = strlen (retbuf); /* so we will know if no new info is added later */
1744
1745 /* Information about the god is random, and based on the level of the
1746 * 'book'. Probably there is a more intellegent way to implement
1747 * this ...
1748 */
1749
1750 while (level > 0)
1751 {
1752 sprintf (buf, " ");
1753 if (level == 2 && RANDOM () % 2)
1754 { /* enemy god */
1755 const char *enemy = god->title;
1756
1757 if (enemy)
1758 sprintf (buf, "The gods %s and %s are enemies.\n ---\n", name, enemy);
1759 }
1760
1761 if (level == 3 && RANDOM () % 2)
1762 { /* enemy race, what the god's holy word effects */
1763 const char *enemy = god->slaying;
1764
1765 if (enemy && !(god->path_denied & PATH_TURNING))
1766 if ((i = nstrtok (enemy, ",")) > 0)
1767 {
1768 char tmpbuf[MAX_BUF];
1769
1770 sprintf (buf, "The holy words of %s have the power to\n", name);
1771 strcat (buf, "slay creatures belonging to the ");
1772 if (i > 1)
1773 sprintf (tmpbuf, "following \n races:%s", strtoktolin (enemy, ","));
1774 else
1775 sprintf (tmpbuf, "race of%s", strtoktolin (enemy, ","));
1776 sprintf (buf, "%s%s\n ---\n", buf, tmpbuf);
1777 }
1778 }
1779
1780 if (level == 4 && RANDOM () % 2)
1781 { /* Priest of god gets these protect,vulnerable... */
1782 char tmpbuf[MAX_BUF];
1783
1784 if (const char *cp = describe_resistance (god, 1))
1785 { /* This god does have protections */
1786 sprintf (tmpbuf, "%s has a potent aura which is extended\n", name);
1787 strcat (tmpbuf, "faithful priests. The effects of this aura include:\n");
1788 strcat (tmpbuf, cp);
1789 strcat (buf, tmpbuf);
1790 strcat (buf, "\n ---\n");
1791 }
1792 else
1793 sprintf (buf, " ");
1794 }
1795
1796 if (level == 5 && RANDOM () % 2)
1797 { /* aligned race, summoning */
1798 const char *race = god->race; /* aligned race */
1799
1800 if (race && !(god->path_denied & PATH_SUMMON))
1801 if ((i = nstrtok (race, ",")) > 0)
1802 {
1803 char tmpbuf[MAX_BUF];
1804
1805 sprintf (buf, "Creatures sacred to %s include the \n", name);
1806 if (i > 1)
1807 sprintf (tmpbuf, "following \n races:%s", strtoktolin (race, ","));
1808 else
1809 sprintf (tmpbuf, "race of%s", strtoktolin (race, ","));
1810 sprintf (buf, "%s%s\n ---\n", buf, tmpbuf);
1811 }
1812 }
1813
1814 if (level == 6 && RANDOM () % 2)
1815 { /* blessing,curse properties of the god */
1816 char tmpbuf[MAX_BUF];
1817
1818 if (const char *cp = describe_resistance (god, 1))
1819 { /* This god does have protections */
1820 sprintf (tmpbuf, "\nThe priests of %s are known to be able to \n", name);
1821 strcat (tmpbuf, "bestow a blessing which makes the recipient\n");
1822 strcat (tmpbuf, cp);
1823 strcat (buf, tmpbuf);
1824 strcat (buf, "\n ---\n");
1825 }
1826 else
1827 sprintf (buf, " ");
1828
1829 }
1830
1831 if (level == 8 && RANDOM () % 2)
1832 { /* immunity, holy possession */
1833 int has_effect = 0, tmpvar;
1834 char tmpbuf[MAX_BUF];
1835
1836 sprintf (tmpbuf, "\n");
1837 sprintf (tmpbuf, "The priests of %s are known to make cast a mighty \n", name);
1838
1839 strcat (tmpbuf, "prayer of possession which gives the recipient\n");
1840
1841 for (tmpvar = 0; tmpvar < NROFATTACKS; tmpvar++)
1842 {
1843 if (god->resist[tmpvar] == 100)
1844 {
1845 has_effect = 1;
1846 sprintf (tmpbuf + strlen (tmpbuf), "Immunity to %s", attacktype_desc[tmpvar]);
1847 }
1848 }
1849
1850 if (has_effect)
1851 {
1852 strcat (buf, tmpbuf);
1853 strcat (buf, "\n ---\n");
1854 }
1855 else
1856 sprintf (buf, " ");
1857 }
1858
1859 if (level == 12 && RANDOM () % 2)
1860 { /* spell paths */
1861 int has_effect = 0, tmpvar;
1862 char tmpbuf[MAX_BUF];
1863
1864 sprintf (tmpbuf, "\n");
1865 sprintf (tmpbuf, "It is rarely known fact that the priests of %s\n", name);
1866 strcat (tmpbuf, "are mystically transformed. Effects of this include:\n");
1867
1868 if ((tmpvar = god->path_attuned))
1869 {
1870 has_effect = 1;
1871 DESCRIBE_PATH (tmpbuf, tmpvar, "Attuned");
1872 }
1873
1874 if ((tmpvar = god->path_repelled))
1875 {
1876 has_effect = 1;
1877 DESCRIBE_PATH (tmpbuf, tmpvar, "Repelled");
1878 }
1879
1880 if ((tmpvar = god->path_denied))
1881 {
1882 has_effect = 1;
1883 DESCRIBE_PATH (tmpbuf, tmpvar, "Denied");
1884 }
1885
1886 if (has_effect)
1887 {
1888 strcat (buf, tmpbuf);
1889 strcat (buf, "\n ---\n");
1890 }
1891 else
1892 sprintf (buf, " ");
1893 }
1894
1895 /* check to be sure new buffer size dont exceed either
1896 * the maximum buffer size, or the 'natural' size of the
1897 * book...
1898 */
1899 if (book_overflow (retbuf, buf, booksize))
1900 break;
1901 else if (strlen (buf) > 1)
1902 strcat (retbuf, buf);
1903
1904 level--;
1905 }
1906
1907 if (strlen (retbuf) == introlen)
1908 { /* we got no information beyond the preamble! */
1909 strcat (retbuf, " [Unfortunately the rest of the information is\n");
1910 strcat (retbuf, " hopelessly garbled!]\n ---\n");
1911 }
1912 #ifdef BOOK_MSG_DEBUG
1913 LOG (llevDebug, "\n god_info_msg() created strng: %d\n", strlen (retbuf));
1914 fprintf (logfile, " MADE THIS:\n%s", retbuf);
1915 #endif
1916 return retbuf;
1917 }
1918
1919 /* tailor_readable_ob()- The main routine. This chooses a random
1920 * message to put in given readable object (type==BOOK) which will
1921 * be referred hereafter as a 'book'. We use the book level to de-
1922 * termine the value of the information we will insert. Higher
1923 * values mean the book will (generally) have better/more info.
1924 * See individual cases as to how this will be utilized.
1925 * "Book" name/content length are based on the weight of the
1926 * document. If the value of msg_type is negative, we will randomly
1927 * choose the kind of message to generate.
1928 * -b.t. thomas@astro.psu.edu
1929 *
1930 * book is the object we are creating into.
1931 * If msg_type is a positive value, we use that to determine the
1932 * message type - otherwise a random value is used.
1933 *
1934 */
1935
1936 void
1937 tailor_readable_ob (object *book, int msg_type)
1938 {
1939 char msgbuf[BOOK_BUF];
1940 int level = book->level ? (RANDOM () % book->level) + 1 : 1;
1941 int book_buf_size;
1942
1943 /* safety */
1944 if (book->type != BOOK)
1945 return;
1946
1947 if (level <= 0)
1948 return; /* if no level no point in doing any more... */
1949
1950 /* Max text length this book can have. */
1951 book_buf_size = BOOKSIZE (book);
1952
1953 /* &&& The message switch &&& */
1954 /* Below all of the possible types of messages in the "book"s.
1955 */
1956 /*
1957 * IF you add a new type of book msg, you will have to do several things.
1958 * 1) make sure there is an entry in the msg switch below!
1959 * 2) make sure there is an entry in max_titles[] array.
1960 * 3) make sure there are entries for your case in new_text_title()
1961 * and add_authour().
1962 * 4) you may want separate authour/book name arrays in read.h
1963 */
1964
1965 msg_type = msg_type > 0 ? msg_type : (RANDOM () % 6);
1966 switch (msg_type)
1967 {
1968 case 1: /* monster attrib */
1969 strcpy (msgbuf, mon_info_msg (level, book_buf_size));
1970 break;
1971 case 2: /* artifact attrib */
1972 strcpy (msgbuf, artifact_msg (level, book_buf_size));
1973 break;
1974 case 3: /* grouping incantations/prayers by path */
1975 strcpy (msgbuf, spellpath_msg (level, book_buf_size));
1976 break;
1977 case 4: /* describe an alchemy formula */
1978 make_formula_book (book, level);
1979 /* make_formula_book already gives title */
1980 return;
1981 break;
1982 case 5: /* bits of information about a god */
1983 strcpy (msgbuf, god_info_msg (level, book_buf_size));
1984 break;
1985 case 0: /* use info list in lib/ */
1986 default:
1987 strcpy (msgbuf, msgfile_msg (level, book_buf_size));
1988 break;
1989 }
1990
1991 strcat (msgbuf, "\n"); /* safety -- we get ugly map saves/crashes w/o this */
1992
1993 if (strlen (msgbuf) > 1)
1994 {
1995 book->msg = msgbuf;
1996 /* lets give the "book" a new name, which may be a compound word */
1997 change_book (book, msg_type);
1998 }
1999
2000 }
2001
2002
2003 /*****************************************************************************
2004 *
2005 * Cleanup routine for readble stuff.
2006 *
2007 *****************************************************************************/
2008
2009 void
2010 free_all_readable (void)
2011 {
2012 titlelist *tlist, *tnext;
2013 title *title1, *titlenext;
2014 linked_char *lmsg, *nextmsg;
2015 objectlink *monlink, *nextmon;
2016
2017 LOG (llevDebug, "freeing all book information\n");
2018
2019 for (tlist = booklist; tlist != NULL; tlist = tnext)
2020 {
2021 tnext = tlist->next;
2022
2023 for (title1 = tlist->first_book; title1; title1 = titlenext)
2024 {
2025 titlenext = title1->next;
2026 delete title1;
2027 }
2028
2029 delete tlist;
2030 }
2031
2032 for (lmsg = first_msg; lmsg; lmsg = nextmsg)
2033 {
2034 nextmsg = lmsg->next;
2035 delete lmsg;
2036 }
2037
2038 for (monlink = first_mon_info; monlink; monlink = nextmon)
2039 {
2040 nextmon = monlink->next;
2041 delete monlink;
2042 }
2043 }
2044
2045
2046 /*****************************************************************************
2047 *
2048 * Writeback routine for updating the bookarchive.
2049 *
2050 ****************************************************************************/
2051
2052 /* write_book_archive() - write out the updated book archive */
2053
2054 void
2055 write_book_archive (void)
2056 {
2057 FILE *fp;
2058 int index = 0;
2059 char fname[MAX_BUF];
2060 title *book = NULL;
2061 titlelist *bl = get_titlelist (0);
2062
2063 /* If nothing changed, don't write anything */
2064 if (!need_to_write_bookarchive)
2065 return;
2066 need_to_write_bookarchive = 0;
2067
2068 sprintf (fname, "%s/bookarch", settings.localdir);
2069 LOG (llevDebug, "Updating book archive: %s...\n", fname);
2070
2071 if ((fp = fopen (fname, "w")) == NULL)
2072 {
2073 LOG (llevDebug, "Can't open book archive file %s\n", fname);
2074 }
2075 else
2076 {
2077 while (bl)
2078 {
2079 for (book = bl->first_book; book; book = book->next)
2080 if (book && book->authour)
2081 {
2082 fprintf (fp, "title %s\n", &book->name);
2083 fprintf (fp, "authour %s\n", &book->authour);
2084 fprintf (fp, "arch %s\n", &book->archname);
2085 fprintf (fp, "level %d\n", book->level);
2086 fprintf (fp, "type %d\n", index);
2087 fprintf (fp, "size %d\n", book->size);
2088 fprintf (fp, "index %d\n", book->msg_index);
2089 fprintf (fp, "end\n");
2090 }
2091 bl = bl->next;
2092 index++;
2093 }
2094 fclose (fp);
2095 chmod (fname, SAVE_MODE);
2096 }
2097 }
2098 readable_message_type *
2099 get_readable_message_type (object *readable)
2100 {
2101 uint8 subtype = readable->subtype;
2102
2103 if (subtype > last_readable_subtype)
2104 return &(readable_message_types[0]);
2105 return &(readable_message_types[subtype]);
2106 }