ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/readable.C
Revision: 1.13
Committed: Tue Dec 12 21:39:56 2006 UTC (17 years, 5 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.12: +4 -4 lines
Log Message:
- more ooficiation
- removed now superfluous remove calls

File Contents

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