Crossfire Server, Trunk  R22047
readable.c
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2014 Mark Wedel and the Crossfire Development Team
5  * Copyright (c) 1992 Frank Tore Johansen
6  *
7  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
8  * welcome to redistribute it under certain conditions. For details, please
9  * see COPYING and LICENSE.
10  *
11  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
12  */
13 
23 /* laid down initial file - dec 1995. -b.t. thomas@astro.psu.edu */
24 
25 #include "global.h"
26 
27 #include <assert.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sys/stat.h>
32 
33 #include "book.h"
34 #include "living.h"
35 #include "output_file.h"
36 #include "spells.h"
37 #include "assets.h"
38 
39 /* Define this if you want to archive book titles by contents.
40  * This option should enforce UNIQUE combinations of titles,authors and
41  * msg contents during and *between *game sessions.
42  * Note: a slight degeneracy exists since books are archived based on an integer
43  * index value calculated from the message text (similar to alchemy formulae).
44  * Sometimes two widely different messages have the same index value (rare). In
45  * this case, it is possible to occasionally generate 2 books with same title and
46  * different message content. Not really a bug, but rather a feature. This action
47  * should keeps player on their toes ;).
48  * Also, note that there is *finite *space available for archiving message and titles.
49  * Once this space is used, books will stop being archived. Not a serious problem
50  * under the current regime, since there are generally fewer possible (random)
51  * messages than space available on the titlelists.
52  * One exception (for sure) are the monster messages. But no worries, you should
53  * see all of the monster info in some order (but not all possble combinations)
54  * before the monster titlelist space is run out. You can increase titlelist
55  * space by increasing the array sizes for the monster book_authours and book_names
56  * (see max_titles[] array and include/read.h). Since the unique_book algorthm is
57  * kinda stupid, this program *may *slow down program execution if defined (but I don't
58  * think its a significant problem, at least, I have no problems running this option
59  * on a Sparc 10! Also, once archive title lists are filled and/or all possible msg
60  * combinations have been generated, unique_book isnt called anymore. It takes 5-10
61  * sessions for this to happen).
62  * Final note: the game remembers book/title/msg combinations from reading the
63  * file lib/bookarch. If you REMOVE this file, you will lose your archive. So
64  * be sure to copy it over to the new lib directory when you change versions.
65  * -b.t.
66  */
67 
68 /* This flag is useful to see what kind of output messages are created */
69 /* #define BOOK_MSG_DEBUG */
70 
71 /* This flag is useful for debugging archiving action */
72 /* #define ARCHIVE_DEBUG */
73 
75 #define MAX_TITLE_CHECK 20
76 
78 #define MSGTYPE_LIB 0
79 
80 #define MSGTYPE_MONSTER 1
81 
82 #define MSGTYPE_ARTIFACT 2
83 
84 #define MSGTYPE_SPELLPATH 3
85 
86 #define MSGTYPE_ALCHEMY 4
87 
88 #define MSGTYPE_GODS 5
89 
90 #define MSGTYPE_MSGFILE 6
91 
97 #define arraysize(arrayname) (sizeof(arrayname)/sizeof(*(arrayname)))
98 
99 /* Moved these structures from struct.h to this file in 0.94.3 - they
100  * are not needed anyplace else, so why have them globally declared?
101  */
102 
107 typedef struct titlestruct {
108  const char *name;
109  const char *authour;
110  const char *archname;
111  unsigned int level;
112  size_t size;
113  int msg_index;
114  struct titlestruct *next;
115 } title;
116 
120 typedef struct titleliststruct {
121  int number;
124 } titlelist;
125 
127 typedef struct namebytype {
128  const char *name;
129  int type;
130 } arttypename;
131 
132 static void add_book(title *book, int type, const char *fname, int lineno);
133 
138 static titlelist *booklist = NULL;
139 
141 static objectlink *first_mon_info = NULL;
142 
143 static int nrofmon = 0,
150  PATH_PROT,
151  PATH_FIRE,
152  PATH_FROST,
153  PATH_ELEC,
154  PATH_MISSILE,
155  PATH_SELF,
156  PATH_SUMMON,
157  PATH_ABJURE,
158  PATH_RESTORE,
160  PATH_MIND,
161  PATH_CREATE,
162  PATH_TELE,
163  PATH_INFO,
166  PATH_TURNING,
168  PATH_DEATH,
169  PATH_LIGHT
170 };
171 
173 static const char *const path_book_name[] = {
174  "codex",
175  "compendium",
176  "documentary",
177  "exposition",
178  "tables",
179  "treatise"
180 };
181 
183 static const char *const path_author[] = {
184  "aether",
185  "arcana",
186  "astral byways",
187  "connections",
188  "the Grey Council",
189  "deep pathways",
190  "knowledge",
191  "magic",
192  "mystic ways",
193  "pathways",
194  "power",
195  "spells",
196  "transforms",
197  "the mystic veil",
198  "unknown spells"
199 };
200 
207 static const arttypename art_name_array[] = {
208  { "Helmet", HELMET },
209  { "Amulet", AMULET },
210  { "Shield", SHIELD },
211  { "Bracers", BRACERS },
212  { "Boots", BOOTS },
213  { "Cloak", CLOAK },
214  { "Gloves", GLOVES },
215  { "Gridle", GIRDLE },
216  { "Ring", RING },
217  { "Horn", ROD },
218  { "Missile Weapon", BOW },
219  { "Missile", ARROW },
220  { "Hand Weapon", WEAPON },
221  { "Artifact", SKILL },
222  { "Food", FOOD },
223  { "Body Armour", ARMOUR }
224 };
225 
227 static const char *const art_book_name[] = {
228  "collection",
229  "file",
230  "files",
231  "guide",
232  "handbook",
233  "index",
234  "inventory",
235  "list",
236  "listing",
237  "omnibus",
238  "record",
239  "record book"
240 };
241 
243 static const char *const art_author[] = {
244  "ancient things",
245  "artifacts",
246  "Havlor", /* ancient warrior scribe :) */
247  "items",
248  "lost artifacts",
249  "the ancients",
250  "treasures",
251  "useful things"
252 };
253 
257 static const char *const mon_book_name[] = {
258  "beastuary",
259  "catalog",
260  "compilation",
261  "collection",
262  "encyclopedia",
263  "guide",
264  "handbook",
265  "list",
266  "manual",
267  "notes",
268  "omnibus",
269  "record",
270  "register",
271  "volume"
272 };
273 
275 static const char *const mon_author[] = {
276  "beasts",
277  "creatures",
278  "dezidens",
279  "dwellers",
280  "evil nature",
281  "life",
282  "monsters",
283  "nature",
284  "new life",
285  "residents",
286  "the spawn",
287  "the living",
288  "things"
289 };
290 
294 static const char *const gods_book_name[] = {
295  "devotional",
296  "devout notes",
297  "divine text",
298  "divine work",
299  "holy book",
300  "holy record",
301  "illuminated text",
302  "moral text",
303  "sacred guide",
304  "testament",
305  "transcript"
306 };
307 
309 static const char *const gods_author[] = {
310  "cults",
311  "joy",
312  "lasting curse",
313  "madness",
314  "religions",
315  "the dead",
316  "the gods",
317  "the heirophant",
318  "the poor priest",
319  "the priestess",
320  "pain",
321  "white"
322 };
323 
327 static const char *const formula_book_name[] = {
328  "cookbook",
329  "formulary",
330  "lab book",
331  "lab notes",
332  "recipe book",
333  "experiment record",
334  "work plan",
335  "design notes",
336  "research notes",
337  "crafting manual"
338 };
339 
341 static const char *const formula_author[] = {
342  "Albertus Magnus",
343  "alchemy",
344  "balms",
345  "creation",
346  "dusts",
347  "magical manufacture",
348  "making",
349  "philosophical items",
350  "potions",
351  "powders",
352  "the cauldron",
353  "the lamp black",
354  "transmutation",
355  "waters"
356 };
357 
363 static const char *const light_book_name[] = {
364  "calendar",
365  "datebook",
366  "diary",
367  "essay",
368  "guidebook",
369  "handbook",
370  "ledger",
371  "notes",
372  "notebook",
373  "octavo",
374  "opuscule",
375  "pamphlet",
376  "practicum",
377  "script",
378  "transcript"
379 };
380 
382 static const char *const heavy_book_name[] = {
383  "catalog",
384  "compendium",
385  "dissertation",
386  "guide",
387  "manual",
388  "opus",
389  "tome",
390  "treatise",
391  "volume",
392  "work"
393 };
394 
396 static const char *const book_author[] = {
397  "Abdulah",
398  "Al'hezred",
399  "Alywn",
400  "Arundel",
401  "Arvind",
402  "Aerlingas",
403  "Bacon",
404  "Baliqendii",
405  "Bosworth",
406  "Beathis",
407  "Bertil",
408  "Cauchy",
409  "Chakrabarti",
410  "der Waalis",
411  "Dirk",
412  "Djwimii",
413  "Eisenstaadt",
414  "Fendris",
415  "Frank",
416  "Habbi",
417  "Harlod",
418  "Ichibod",
419  "Janus",
420  "June",
421  "Laplace",
422  "Magnuson",
423  "Nandii",
424  "Nitfeder",
425  "Norris",
426  "Parael",
427  "Penhew",
428  "Sophia",
429  "Skilly",
430  "Tahir",
431  "Thockmorton",
432  "Thomas",
433  "van Helsing",
434  "van Pelt",
435  "Voormis",
436  "Xavier",
437  "Xeno",
438  "Zardoz",
439  "Zagy"
440 };
441 
443 static const char *const book_descrpt[] = {
444  "ancient",
445  "cryptic",
446  "cryptical",
447  "dusty",
448  "hierarchical",
449  "grizzled",
450  "gold-gilt",
451  "great",
452  "lost",
453  "magnificent",
454  "musty",
455  "mythical",
456  "mystical",
457  "rustic",
458  "stained",
459  "silvered",
460  "transcendental",
461  "weathered"
462 };
463 
471  /*subtype 0 */ { 0, 0 },
472  /* book messages subtypes */
473  /*subtype 1 */ { MSG_TYPE_BOOK, MSG_TYPE_BOOK_CLASP_1 },
482  /*subtype 10 */ { MSG_TYPE_BOOK, MSG_TYPE_BOOK_SPELL_SORCERER },
484  /* card messages subtypes*/
493  /*subtype 20 */ { MSG_TYPE_CARD, MSG_TYPE_CARD_STRANGE_3 },
497  /* Paper messages subtypes */
504  /*subtype 30 */ { MSG_TYPE_PAPER, MSG_TYPE_PAPER_LETTER_NEW_2 },
512  /* road signs messages subtypes */
515  /*subtype 40 */ { MSG_TYPE_SIGN, MSG_TYPE_SIGN_DIR_RIGHT },
517  /* stones and monument messages */
531 };
532 
534 static const int last_readable_subtype = arraysize(readable_message_types);
535 
537 static const int max_titles[6] = {
539  arraysize(mon_book_name)*arraysize(mon_author), /* MSGTYPE_MONSTER */
540  arraysize(art_book_name)*arraysize(art_author), /* MSGTYPE_ARTIFACT */
541  arraysize(path_book_name)*arraysize(path_author), /* MSGTYPE_SPELLPATH */
542  arraysize(formula_book_name)*arraysize(formula_author), /* MSGTYPE_ALCHEMY */
543  arraysize(gods_book_name)*arraysize(gods_author), /* MSGTYPE_GODS */
544 };
545 
546 /******************************************************************************
547  *
548  * Start of misc. readable functions used by others functions in this file
549  *
550  *****************************************************************************/
551 
562  titlelist *bl = (titlelist *)malloc(sizeof(titlelist));
563 
564  if (bl == NULL)
566  bl->number = 0;
567  bl->first_book = NULL;
568  bl->next = NULL;
569  return bl;
570 }
571 
581 static title *get_empty_book(void) {
582  title *t = (title *)malloc(sizeof(title));
583 
584  if (t == NULL)
586  t->name = NULL;
587  t->archname = NULL;
588  t->authour = NULL;
589  t->level = 0;
590  t->size = 0;
591  t->msg_index = 0;
592  t->next = NULL;
593  return t;
594 }
595 
606 static titlelist *get_titlelist(int i) {
607  titlelist *tl;
608  int number;
609 
610  if (i < 0 || i >= (int)arraysize(max_titles)) {
611  LOG(llevInfo, "Warning: invalid book index %d, using 0 instead\n", i);
612  return booklist;
613  }
614 
615  for (tl = booklist, number = i; tl && number; tl = tl->next, number--) {
616  if (!tl->next)
617  tl->next = get_empty_booklist();
618  }
619 
620  return tl;
621 }
622 
623 /* HANDMADE STRING FUNCTIONS.., perhaps these belong in another file
624  * (shstr.c ?), but the quantity BOOK_BUF will need to be defined. */
625 
637 int nstrtok(const char *buf1, const char *buf2) {
638  char *tbuf, buf[MAX_BUF];
639  int number = 0;
640 
641  if (!buf1 || !buf2)
642  return 0;
643 
644  strlcpy(buf, buf1, sizeof(buf));
645  for (tbuf = strtok(buf, buf2); tbuf; tbuf = strtok(NULL, buf2)) {
646  number++;
647  }
648  return number;
649 }
650 
667 char *strtoktolin(const char *buf1, const char *buf2, char *retbuf, size_t size) {
668  int maxi, i = nstrtok(buf1, buf2);
669  char *tbuf, buf[MAX_BUF];
670 
671  maxi = i;
672  strlcpy(buf, buf1, sizeof(buf));
673  snprintf(retbuf, size, " ");
674  for (tbuf = strtok(buf, buf2); tbuf && i > 0; tbuf = strtok(NULL, buf2)) {
675  snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "%s", tbuf);
676  i--;
677  if (i == 1 && maxi > 1)
678  snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), " and ");
679  else if (i > 0 && maxi > 1)
680  snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), ", ");
681  else
682  snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), ".");
683  }
684  return retbuf;
685 }
686 
697 int book_overflow(const char *buf1, const char *buf2, size_t booksize) {
698  if (buf_overflow(buf1, buf2, BOOK_BUF-2) /* 2 less so always room for trailing \n */
699  || buf_overflow(buf1, buf2, booksize))
700  return 1;
701  return 0;
702 }
703 
704 /*****************************************************************************
705  *
706  * Start of initialization related functions.
707  *
708  ****************************************************************************/
709 
716 static void init_book_archive(void) {
717  FILE *fp;
718  int nroftitle = 0;
719  char buf[MAX_BUF], fname[MAX_BUF], *cp;
720  static int did_init_barch = 0;
721 
722  if (did_init_barch)
723  return;
724  did_init_barch = 1;
725 
726  if (!booklist)
727  booklist = get_empty_booklist();
728 
729  snprintf(fname, sizeof(fname), "%s/bookarch", settings.localdir);
730  LOG(llevDebug, " Reading bookarch from %s...\n", fname);
731 
732  fp = fopen(fname, "r");
733  if (fp != NULL) {
734  int type;
735  size_t i;
736  titlelist *bl;
737  int lineno;
738  title *book;
739  int skipping;
740 
741  skipping = 0;
742  book = NULL;
743  type = -1;
744  for (lineno = 1; fgets(buf, MAX_BUF, fp) != NULL; lineno++) {
745  int len;
746  int value;
747 
748  if (*buf == '#')
749  continue;
750  cp = strchr(buf, '\n');
751  if (cp != NULL) {
752  while (cp > buf && (cp[-1] == ' ' || cp[-1] == '\t'))
753  cp--;
754  *cp = '\0';
755  }
756  cp = buf;
757  if (strncmp(buf, "title ", 6) == 0) {
758  skipping = 0;
759  cp = buf+6;
760  while (*cp == ' ' || *cp == '\t')
761  cp++;
762  if (*cp == '\0') {
763  LOG(llevInfo, "Warning: missing book title at %s, line %d\n", fname, lineno);
764  book = NULL;
765  } else {
766  book = get_empty_book(); /* init new book entry */
767  book->name = add_string(cp);
768  type = -1;
769  nroftitle++;
770  }
771  } else if (book == NULL) {
772  if (!skipping) {
773  skipping = 1;
774  LOG(llevInfo, "Warning: expecting 'title' at %s, line %d\n", fname, lineno);
775  }
776  } else if (strncmp(buf, "authour ", 8) == 0) {
777  cp = buf+8;
778  while (*cp == ' ' || *cp == '\t')
779  cp++;
780  if (*cp == '\0') {
781  LOG(llevInfo, "Warning: missing book authour at %s, line %d\n", fname, lineno);
782  } else {
783  book->authour = add_string(cp);
784  }
785  } else if (strncmp(buf, "arch ", 5) == 0) {
786  cp = buf+5;
787  while (*cp == ' ' || *cp == '\t')
788  cp++;
789  if (*cp == '\0') {
790  LOG(llevInfo, "Warning: missing book arch at %s, line %d\n", fname, lineno);
791  } else {
792  book->archname = add_string(cp);
793  }
794  } else if (sscanf(buf, "level %d%n", &value, &len) == 1 && len == (int)strlen(buf)) {
795  book->level = value;
796  } else if (sscanf(buf, "type %d%n", &value, &len) == 1 && len == (int)strlen(buf)) {
797  type = value;
798  } else if (sscanf(buf, "size %d%n", &value, &len) == 1 && len == (int)strlen(buf)) {
799  book->size = value;
800  } else if (sscanf(buf, "index %d%n", &value, &len) == 1 && len == (int)strlen(buf)) {
801  book->msg_index = value;
802  } else if (strcmp(buf, "end") == 0) { /* link it */
803  add_book(book, type, fname, lineno);
804  book = NULL;
805  type = -1;
806  } else {
807  LOG(llevInfo, "Warning: syntax error at %s, line %d\n", fname, lineno);
808  }
809  }
810  if (book != NULL) {
811  LOG(llevInfo, "Warning: missing 'end' at %s, line %d\n", fname, lineno);
812  add_book(book, type, fname, lineno);
813  }
814  LOG(llevDebug, " book archives(used/avail):\n");
815  for (bl = booklist, i = 0; bl != NULL && i < arraysize(max_titles); bl = bl->next, i++) {
816  LOG(llevDebug, "(%d/%d)\n", bl->number, max_titles[i]);
817  }
818  fclose(fp);
819  }
820 
821 #ifdef BOOK_MSG_DEBUG
822  LOG(llevDebug, "\n init_book_archive() got %d titles.\n", nroftitle);
823 #endif
824  LOG(llevDebug, " done.\n");
825 }
826 
834 static void add_book(title *book, int type, const char *fname, int lineno) {
835  titlelist *bl;
836 
837  if (type == -1) {
838  LOG(llevInfo, "Warning: book with no type at %s, line %d; using type 0\n", fname, lineno);
839  type = 0;
840  }
841 
842  bl = get_titlelist(type);
843  book->next = bl->first_book;
844  bl->first_book = book;
845  bl->number++;
846 }
847 
848 static void do_monster(archetype *at) {
849  if (QUERY_FLAG(&at->clone, FLAG_MONSTER) && (!at->head)
850  && (!QUERY_FLAG(&at->clone, FLAG_CHANGING) || QUERY_FLAG(&at->clone, FLAG_UNAGGRESSIVE))) {
851  objectlink *mon = (objectlink *)malloc(sizeof(objectlink));
852  if (!mon) {
853  LOG(llevError, "init_mon_info: malloc failed!\n");
854  abort();
855  }
856  mon->ob = &at->clone;
857  mon->id = nrofmon;
858  mon->next = first_mon_info;
860  nrofmon++;
861  }
862 }
863 
868 static void init_mon_info(void) {
869  static int did_init_mon_info = 0;
870 
871  if (did_init_mon_info)
872  return;
873  did_init_mon_info = 1;
874 
876 
877  LOG(llevDebug, "init_mon_info() got %d monsters\n", nrofmon);
878 }
879 
886 void init_readable(void) {
887  static int did_this = 0;
888 
889  if (did_this)
890  return;
891  did_this = 1;
892 
893  LOG(llevDebug, "Initializing reading data...\n");
895  init_mon_info();
896  LOG(llevDebug, " done reading data\n");
897 }
898 
899 /*****************************************************************************
900  *
901  * This is the start of the administrative functions when creating
902  * new books (ie, updating title and the like)
903  *
904  *****************************************************************************/
905 
917 static title *find_title(const object *book, int msgtype) {
918  title *t;
919  titlelist *tl;
920  size_t length;
921  int index;
922 
923  if (msgtype < 0)
924  return (title *)NULL;
925 
926  tl = get_titlelist(msgtype);
927  if (!tl)
928  return (title *)NULL;
929 
930  length = strlen(book->msg);
931  index = strtoint(book->msg);
932  for (t = tl->first_book; t; t = t->next)
933  if (t->size == length && t->msg_index == index) {
934 #ifdef ARCHIVE_DEBUG
935  LOG(llevDebug, "Found title match (list %d): %s %s (%d)\n", msgtype, t->name, t->authour, t->msg_index);
936 #endif
937  return t;
938  }
939 
940  return (title *)NULL;
941 }
942 
956 static void new_text_name(object *book, int msgtype) {
957  const char *name;
958 
959  if (book->type != BOOK)
960  return;
961 
962  switch (msgtype) {
963  case MSGTYPE_MONSTER:
965  break;
966 
967  case MSGTYPE_ARTIFACT:
969  break;
970 
971  case MSGTYPE_SPELLPATH:
973  break;
974 
975  case MSGTYPE_ALCHEMY:
977  break;
978 
979  case MSGTYPE_GODS:
981  break;
982 
983  case MSGTYPE_MSGFILE:
984  default:
985  if (book->weight > 2000) { /* based on weight */
987  } else {
989  }
990  break;
991  }
992  free_string(book->name);
993  book->name = add_string(name);
994 }
995 
1005 static void add_author(object *op, int msgtype) {
1006  char title[MAX_BUF];
1007  const char *name;
1008 
1009  if (msgtype < 0 || strlen(op->msg) < 5)
1010  return;
1011 
1012  switch (msgtype) {
1013  case MSGTYPE_MONSTER:
1015  break;
1016 
1017  case MSGTYPE_ARTIFACT:
1019  break;
1020 
1021  case MSGTYPE_SPELLPATH:
1023  break;
1024 
1025  case MSGTYPE_ALCHEMY:
1027  break;
1028 
1029  case MSGTYPE_GODS:
1031  break;
1032 
1033  case MSGTYPE_MSGFILE:
1034  default:
1036  }
1037 
1038  snprintf(title, sizeof(title), "of %s", name);
1039  op->title = add_string(title);
1040 }
1041 
1053 static int unique_book(const object *book, int msgtype) {
1054  title *test;
1055 
1056  if (!booklist)
1057  return 1; /* No archival entries! Must be unique! */
1058 
1059  /* Go through the booklist. If the author and name match, not unique so
1060  * return 0.
1061  */
1062  for (test = get_titlelist(msgtype)->first_book; test; test = test->next) {
1063  if (!strcmp(test->name, book->name) && !strcmp(book->title, test->authour))
1064  return 0;
1065  }
1066  return 1;
1067 }
1068 
1077 static void add_book_to_list(const object *book, int msgtype) {
1078  titlelist *tl = get_titlelist(msgtype);
1079  title *t;
1080 
1081  if (!tl) {
1082  LOG(llevError, "add_book_to_list can't get booklist!\n");
1083  return;
1084  }
1085 
1086  t = get_empty_book();
1087  t->name = add_string(book->name);
1088  t->authour = add_string(book->title);
1089  t->size = strlen(book->msg);
1090  t->msg_index = strtoint(book->msg);
1091  t->archname = add_string(book->arch->name);
1092  t->level = book->level;
1093 
1094  t->next = tl->first_book;
1095  tl->first_book = t;
1096  tl->number++;
1097 
1098  /* We have stuff we need to write now */
1100 
1101 #ifdef ARCHIVE_DEBUG
1102  LOG(llevDebug, "Archiving new title: %s %s (%d)\n", book->name, book->title, msgtype);
1103 #endif
1104 }
1105 
1119 static void change_book(object *book, int msgtype) {
1120  titlelist *tl;
1121  title *t;
1122  int tries;
1123 
1124  if (book->type != BOOK) {
1125  LOG(llevError, "change_book_name() called w/ illegal obj type.\n");
1126  return;
1127  }
1128 
1129  tl = get_titlelist(msgtype);
1130  t = NULL;
1131  tries = 0;
1132 
1133  /* look to see if our msg already been archived. If so, alter
1134  * the book to match the archival text. If we fail to match,
1135  * then we archive the new title/name/msg combo if there is
1136  * room on the titlelist.
1137  */
1138 
1139  if (strlen(book->msg) > 5 && (t = find_title(book, msgtype))) {
1140  object *tmpbook;
1141  sstring marker = object_get_value(book, "knowledge_marker");
1142 
1143  /* alter book properties */
1144  tmpbook = create_archetype(t->archname);
1145  if (marker != NULL)
1146  /* need to copy the knowledge_marker */
1147  object_set_value(tmpbook, "knowledge_marker", marker, 1);
1148  object_set_msg(tmpbook, book->msg);
1149  object_copy(tmpbook, book);
1150  object_free_drop_inventory(tmpbook);
1151 
1152  book->title = add_string(t->authour);
1153  free_string(book->name);
1154  book->name = add_string(t->name);
1155  book->level = t->level;
1156  } else { /* Don't have any default title, so lets make up a new one */
1157  int numb, maxnames = max_titles[msgtype];
1158  const char *old_title;
1159  const char *old_name;
1160 
1161  old_title = book->title ? add_string(book->title) : NULL;
1162  old_name = add_string(book->name);
1163 
1164  /* some pre-generated books have title already set (from
1165  * maps), also don't bother looking for unique title if
1166  * we already used up all the available names! */
1167 
1168  if (!tl) {
1169  LOG(llevError, "change_book_name(): can't find title list\n");
1170  numb = 0;
1171  } else
1172  numb = tl->number;
1173 
1174  if (numb == maxnames) {
1175 #ifdef ARCHIVE_DEBUG
1176  LOG(llevDebug, "titles for list %d full (%d possible).\n", msgtype, maxnames);
1177 #endif
1178  if (old_title != NULL)
1179  free_string(old_title);
1180  free_string(old_name);
1181  return;
1182  }
1183  /* shouldnt change map-maker books */
1184  if (!book->title)
1185  do {
1186  /* random book name */
1187  new_text_name(book, msgtype);
1188  add_author(book, msgtype); /* random author */
1189  tries++;
1190  } while (!unique_book(book, msgtype) && tries < MAX_TITLE_CHECK);
1191 
1192  /* Now deal with 2 cases.
1193  * 1) If no space for a new title exists lets just restore
1194  * the old book properties. Remember, if the book had
1195  * matchd an older entry on the titlelist, we shouldnt
1196  * have called this routine in the first place!
1197  * 2) If we got a unique title, we need to add it to
1198  * the list.
1199  */
1200 
1201  if (tries == MAX_TITLE_CHECK) {
1202 #ifdef ARCHIVE_DEBUG
1203  LOG(llevDebug, "Failed to obtain unique title for %s %s (names:%d/%d)\n", book->name, book->title, numb, maxnames);
1204 #endif
1205  /* restore old book properties here */
1206  free_string(book->name);
1207  free_string(book->title);
1208  book->title = old_title != NULL ? add_string(old_title) : NULL;
1209 
1210  if (RANDOM()%4) {
1211  /* Lets give the book a description to individualize it some */
1212  char new_name[MAX_BUF];
1213 
1214  snprintf(new_name, MAX_BUF, "%s %s", book_descrpt[RANDOM()%arraysize(book_descrpt)], old_name);
1215  book->name = add_string(new_name);
1216  } else {
1217  book->name = add_string(old_name);
1218  }
1219  } else if (book->title && strlen(book->msg) > 5) { /* archive if long msg texts */
1220  add_book_to_list(book, msgtype);
1221  }
1222 
1223  if (old_title != NULL)
1224  free_string(old_title);
1225  free_string(old_name);
1226  }
1227 }
1228 
1229 /*****************************************************************************
1230  *
1231  * This is the start of the area that generates the actual contents
1232  * of the book.
1233  *
1234  *****************************************************************************/
1235 
1236 /*****************************************************************************
1237  * Monster msg generation code.
1238  ****************************************************************************/
1239 
1251 object *get_random_mon(int level) {
1252  objectlink *mon;
1253  int i, monnr;
1254 
1255  /* safety check. Problem w/ init_mon_info list? */
1256  if (!nrofmon || !first_mon_info)
1257  return (object *)NULL;
1258 
1259  if (!level) {
1260  /* lets get a random monster from the mon_info linked list */
1261  monnr = RANDOM()%nrofmon;
1262 
1263  for (mon = first_mon_info, i = 0; mon; mon = mon->next, i++)
1264  if (i == monnr)
1265  break;
1266 
1267  if (!mon) {
1268  LOG(llevError, "get_random_mon: Didn't find a monster when we should have\n");
1269  return NULL;
1270  }
1271  return mon->ob;
1272  }
1273 
1274  /* Case where we are searching by level. Redone 971225 to be clearer
1275  * and more random. Before, it looks like it took a random monster from
1276  * the list, and then returned the first monster after that which was
1277  * appropriate level. This wasn't very random because if you had a
1278  * bunch of low level monsters and then a high level one, if the random
1279  * determine took one of the low level ones, it would just forward to the
1280  * high level one and return that. Thus, monsters that immediately followed
1281  * a bunch of low level monsters would be more heavily returned. It also
1282  * means some of the dragons would be poorly represented, since they
1283  * are a group of high level monsters all around each other.
1284  */
1285 
1286  /* First count number of monsters meeting level criteria */
1287  for (mon = first_mon_info, i = 0; mon; mon = mon->next)
1288  if (mon->ob->level >= level)
1289  i++;
1290 
1291  if (i == 0) {
1292  LOG(llevError, "get_random_mon() couldn't return monster for level %d\n", level);
1293  return NULL;
1294  }
1295 
1296  monnr = RANDOM()%i;
1297  for (mon = first_mon_info; mon; mon = mon->next)
1298  if (mon->ob->level >= level && monnr-- == 0)
1299  return mon->ob;
1300 
1301  LOG(llevError, "get_random_mon(): didn't find a monster when we should have\n");
1302  return NULL;
1303 }
1304 
1314 static StringBuffer *mon_desc(const object *mon) {
1315  StringBuffer *desc = stringbuffer_new();
1316  stringbuffer_append_printf(desc, "\n---\n *** %s ***\n", mon->name);
1317  describe_item(mon, NULL, 0, desc);
1318  return desc;
1319 }
1320 
1332 static object *get_next_mon(const object *tmp) {
1333  objectlink *mon;
1334 
1335  for (mon = first_mon_info; mon; mon = mon->next)
1336  if (mon->ob == tmp)
1337  break;
1338 
1339  /* didn't find a match */
1340  if (!mon)
1341  return NULL;
1342  if (mon->next)
1343  return mon->next->ob;
1344  else
1345  return first_mon_info->ob;
1346 }
1347 
1360 static StringBuffer *mon_info_msg(int level, size_t booksize, object *book) {
1361  object *tmp;
1362  StringBuffer *marker = stringbuffer_new(), *desc = stringbuffer_new(), *mon = NULL;
1363  int added = 0;
1364  sstring final;
1365  const char *sep = ":";
1366 
1367  /*preamble */
1368  stringbuffer_append_string(desc, "This beastiary contains:");
1369  stringbuffer_append_string(marker, "monster");
1370 
1371  /* lets print info on as many monsters as will fit in our
1372  * document.
1373  * 8-96 Had to change this a bit, otherwise there would
1374  * have been an impossibly large number of combinations
1375  * of text! (and flood out the available number of titles
1376  * in the archive in a snap!) -b.t.
1377  */
1378  for (tmp = get_random_mon(level*3); tmp; tmp = get_next_mon(tmp)) {
1379  /* monster description */
1380  mon = mon_desc(tmp);
1381 
1382  if (stringbuffer_length(desc) + stringbuffer_length(mon) >= booksize)
1383  break;
1384  added++;
1385  stringbuffer_append_printf(marker, "%s%s", sep, tmp->arch->name);
1386  sep = "/";
1389  mon = NULL;
1390  }
1391 
1392  if (mon != NULL) {
1394  }
1395 
1396  final = stringbuffer_finish_shared(marker);
1397  if (added)
1398  object_set_value(book, "knowledge_marker", final, 1);
1399  free_string(final);
1400 
1401  return desc;
1402 }
1403 
1404 /*****************************************************************************
1405  * Artifact msg generation code.
1406  ****************************************************************************/
1407 
1417 static StringBuffer *artifact_describe(const artifact *art, const artifactlist *al, int message, int art_name, int separator) {
1418  object *tmp;
1419  int chance;
1420  StringBuffer *desc = stringbuffer_new(), *sbuf;
1421 
1422  if (separator)
1423  stringbuffer_append_string(desc, "---\n");
1424 
1425  /* Name */
1426  if (art->allowed != NULL) {
1427  archetype *arch;
1428  linked_char *temp = art->allowed;
1429  int inv = 0, w;
1430 
1431  assert(art->allowed_size > 0);
1432  if (art->allowed_size > 1)
1433  w = 1 + RANDOM() % art->allowed_size;
1434  else
1435  w = 1;
1436 
1437  while (w > 1) {
1438  assert(temp);
1439  temp = temp->next;
1440  w--;
1441  }
1442 
1443  if (temp->name[0] == '!')
1444  inv = 1;
1445 
1447  arch = try_find_archetype(temp->name + inv);
1448  if (!arch)
1449  arch = find_archetype_by_object_name(temp->name + inv);
1450 
1451  if (!arch)
1452  LOG(llevError, "artifact_msg: missing archetype %s for artifact %s (type %d)\n", temp->name + inv, art->item->name, art->item->type);
1453  else {
1454  if (inv)
1455  stringbuffer_append_printf(desc, " A %s (excepted %s) of %s", art_name_array[art_name].name, arch->clone.name_pl, art->item->name);
1456  else
1457  stringbuffer_append_printf(desc, " A %s of %s", arch->clone.name, art->item->name);
1458  }
1459  } else { /* default name is used */
1460  /* use the base 'generic' name for our artifact */
1461  stringbuffer_append_printf(desc, " The %s of %s", art_name_array[art_name].name, art->item->name);
1462  }
1463 
1464  /* chance of finding */
1465  stringbuffer_append_string(desc, " is ");
1466  chance = 100*((float)art->chance/al->total_chance);
1467  if (chance >= 20)
1468  stringbuffer_append_string(desc, "an uncommon");
1469  else if (chance >= 10)
1470  stringbuffer_append_string(desc, "an unusual");
1471  else if (chance >= 5)
1472  stringbuffer_append_string(desc, "a rare");
1473  else
1474  stringbuffer_append_string(desc, "a very rare");
1475 
1476  /* value of artifact */
1477  stringbuffer_append_printf(desc, " item with a value that is %d times normal.\n", art->item->value);
1478 
1479  /* include the message about the artifact, if exists, and book
1480  * level is kinda high */
1481  if (message && !(strlen(art->item->msg) > BOOK_BUF))
1482  stringbuffer_append_string(desc, art->item->msg);
1483 
1484  /* properties of the artifact */
1485  tmp = object_new();
1486  add_abilities(tmp, art->item);
1487  tmp->type = al->type;
1488  SET_FLAG(tmp, FLAG_IDENTIFIED);
1489  sbuf = describe_item(tmp, NULL, 0, NULL);
1490  if (stringbuffer_length(sbuf) > 1) {
1491  stringbuffer_append_string(desc, " Properties of this artifact include:\n ");
1493  stringbuffer_append_string(desc, "\n");
1494  }
1495  free(stringbuffer_finish(sbuf));
1497 
1498  return desc;
1499 }
1500 
1512 static StringBuffer *artifact_msg(unsigned int level, size_t booksize) {
1513  const artifactlist *al;
1514  const artifact *art;
1515  int i, type, index;
1516  int book_entries = level > 5 ? RANDOM()%3+RANDOM()%3+2 : RANDOM()%level+1;
1517  StringBuffer *desc, *message = stringbuffer_new();
1518 
1519  /* values greater than 5 create msg buffers that are too big! */
1520  if (book_entries > 5)
1521  book_entries = 5;
1522 
1523  /* lets determine what kind of artifact type randomly.
1524  * Right now legal artifacts only come from those listed
1525  * in art_name_array. Also, we check to be sure an artifactlist
1526  * for that type exists!
1527  */
1528  i = 0;
1529  do {
1530  index = RANDOM()%arraysize(art_name_array);
1531  type = art_name_array[index].type;
1532  al = find_artifactlist(type);
1533  i++;
1534  } while (al == NULL && i < 10);
1535 
1536  if (i == 10) { /* Unable to find a message */
1537  stringbuffer_append_string(message, "None");
1538  return message;
1539  }
1540 
1541  /* There is no reason to start on the artifact list at the beginning. Lets
1542  * take our starting position randomly... */
1543  art = al->items;
1544  for (i = RANDOM()%level+RANDOM()%2+1; i > 0; i--) {
1545  if (art == NULL)
1546  art = al->items; /* hmm, out of stuff, loop back around */
1547  art = art->next;
1548  }
1549 
1550  /* Ok, lets print out the contents */
1551  stringbuffer_append_printf(message, "Herein %s detailed %s...\n", book_entries > 1 ? "are" : "is", book_entries > 1 ? "some artifacts" : "an artifact");
1552 
1553  i = 0;
1554  /* artifact msg attributes loop. Lets keep adding entries to the 'book'
1555  * as long as we have space up to the allowed max # (book_entires)
1556  */
1557  while (book_entries > 0) {
1558  int with_message;
1559  if (art == NULL)
1560  art = al->items;
1561  with_message = (art->item->msg && RANDOM()%4+1 < level) ? 1 : 0;
1562 
1563  desc = artifact_describe(art, al, with_message, index, i++);
1564 
1565  if (stringbuffer_length(message) + stringbuffer_length(desc) >= booksize) {
1566  stringbuffer_delete(desc);
1567  break;
1568  }
1569 
1570  stringbuffer_append_stringbuffer(message, desc);
1571  stringbuffer_delete(desc);
1572 
1573  art = art->next;
1574  book_entries--;
1575  }
1576 
1577  return message;
1578 }
1579 
1580 /*****************************************************************************
1581  * Spellpath message generation
1582  *****************************************************************************/
1583 
1585 static struct {
1586  int prayers;
1589  int level;
1590  size_t booksize;
1592  int done;
1593 } sp_params;
1594 
1596 static void do_spellpath_msg(archetype *at) {
1597  /* Determine if this is an appropriate spell. Must
1598  * be of matching path, must be of appropriate type (prayer
1599  * or not), and must be within the valid level range.
1600  */
1601  if (at->clone.type == SPELL
1602  && at->clone.path_attuned & sp_params.pnum
1603  && ((at->clone.stats.grace && sp_params.prayers) || (at->clone.stats.sp && !sp_params.prayers))
1604  && at->clone.level < sp_params.level*8) {
1605  if (strlen(at->clone.name) + stringbuffer_length(sp_params.buf) >= sp_params.booksize) {
1606  sp_params.done = 1;
1607  return;
1608  }
1609 
1610  if (sp_params.did_first_sp)
1612  sp_params.did_first_sp = 1;
1613  stringbuffer_append_string(sp_params.buf,at->clone.name);
1614  }
1615 }
1616 
1631  int path = RANDOM()%NRSPELLPATHS;
1632  sp_params.prayers = RANDOM()%2;
1633  sp_params.did_first_sp = 0;
1634  sp_params.pnum = spellpathdef[path];
1635  sp_params.done = 0;
1636 
1637  if (buf == NULL) {
1638  buf = stringbuffer_new();
1639  /* Preamble */
1640  stringbuffer_append_printf(buf, "Herein are detailed the names of %s", sp_params.prayers ? "prayers" : "incantations");
1641  stringbuffer_append_printf(buf, " belonging to the path of %s:\n ", spellpathnames[path]);
1642  }
1643  sp_params.level = level;
1644  sp_params.booksize = booksize;
1645  sp_params.buf = buf;
1646 
1648 
1649  /* Geez, no spells were generated. */
1650  if (!sp_params.did_first_sp) {
1651  if (RANDOM()%4) { /* usually, lets make a recursive call... */
1652  // If we do a recursive call, we reset the spell path, so we will want to reset our text as well.
1653  stringbuffer_delete(buf);
1654  return spellpath_msg(level, booksize, NULL);
1655  }
1656  /* give up, cause knowing no spells exist for path is info too. need the header too. */
1657  stringbuffer_append_string(buf, "- no known spells exist -\n");
1658  }
1659  return buf;
1660 }
1661 
1670 static void make_formula_book(object *book, int level) {
1671  recipelist *fl;
1672  recipe *formula;
1673  int chance, count = 0;
1674  const char *op_name;
1675  archetype *at;
1676  StringBuffer *text, *title;
1677  char *final, km[MAX_BUF];
1678 
1679  /* the higher the book level, the more complex (ie number of
1680  * ingredients) the formula can be.
1681  */
1682  fl = get_formulalist((RANDOM()%level)/3+1);
1683  if (!fl)
1684  fl = get_formulalist(1); /* safety */
1685 
1686  if (fl->total_chance == 0) {
1687  object_set_msg(book, " <indecipherable text>\n");
1689  add_author(book, MSGTYPE_ALCHEMY);
1690  return;
1691  }
1692 
1693  /* get a random formula, weighted by its bookchance */
1694  chance = RANDOM()%fl->total_chance;
1695  for (formula = fl->items; formula != NULL; formula = formula->next) {
1696  chance -= formula->chance;
1697  if (chance <= 0 && formula->chance != 0 && !formula->is_combination)
1698  break;
1699  }
1700 
1701  if (!formula || formula->arch_names <= 0) {
1702  object_set_msg(book, " <indecipherable text>\n");
1704  add_author(book, MSGTYPE_ALCHEMY);
1705  return;
1706  }
1707 
1708  /* looks like a formula was found. Base the amount
1709  * of information on the booklevel and the spellevel
1710  * of the formula. */
1711 
1712  op_name = formula->arch_name[RANDOM()%formula->arch_names];
1713  at = find_archetype(op_name);
1714  if (at == (archetype *)NULL) {
1715  LOG(llevError, "formula_msg() can't find arch %s for formula.\n", op_name);
1716  object_set_msg(book, " <indecipherable text>\n");
1718  add_author(book, MSGTYPE_ALCHEMY);
1719  return;
1720  }
1721  op_name = at->clone.name;
1722 
1723  text = stringbuffer_new();
1724  title = stringbuffer_new();
1725 
1726  /* preamble */
1727  stringbuffer_append_printf(text, "Herein is described a project using %s:\n", formula->skill ? formula->skill : "an unknown skill");
1728 
1729  /* item name */
1730  if (strcmp(formula->title, "NONE")) {
1731  stringbuffer_append_printf(text, "The %s of %s", op_name, formula->title);
1732  /* This results in things like pile of philo. sulfur.
1733  * while philo. sulfur may look better, without this,
1734  * you get things like 'the wise' because its missing the
1735  * water of section.
1736  */
1737  stringbuffer_append_printf(title, "%s: %s of %s", formula_book_name[RANDOM()%arraysize(formula_book_name)], op_name, formula->title);
1738  } else {
1739  stringbuffer_append_printf(text, "The %s", op_name);
1741  if (at->clone.title) {
1742  stringbuffer_append_printf(text, " %s", at->clone.title);
1743  stringbuffer_append_printf(title, " %s", at->clone.title);
1744  }
1745  }
1746  /* Lets name the book something meaningful ! */
1747  if (book->name)
1748  free_string(book->name);
1749  book->name = stringbuffer_finish_shared(title);
1750  if (book->title) {
1751  free_string(book->title);
1752  book->title = NULL;
1753  }
1754 
1755  /* ingredients to make it */
1756  if (formula->ingred != NULL) {
1757  linked_char *next;
1758  archetype *at;
1759  char name[MAX_BUF];
1760 
1761  at = find_archetype(formula->cauldron);
1762  if (at)
1763  query_name(&at->clone, name, MAX_BUF);
1764  else
1765  snprintf(name, sizeof(name), "an unknown place");
1766 
1767  stringbuffer_append_printf(text, " may be made at %s using the following ingredients:\n", name);
1768 
1769  for (next = formula->ingred; next != NULL; next = next->next) {
1770  count++;
1771  stringbuffer_append_printf(text, "%s\n", next->name);
1772  }
1773  } else {
1774  LOG(llevError, "formula_msg() no ingredient list for object %s of %s\n", op_name, formula->title);
1775  stringbuffer_append_string(text, "\n");
1776  }
1777 
1778  final = stringbuffer_finish(text);
1779  object_set_msg(book, final);
1780  free(final);
1781 
1784  snprintf(km, sizeof(km), "alchemy:%d:%d:%s", count, formula->index, formula->title);
1785  object_set_value(book, "knowledge_marker", km, 1);
1786 }
1787 
1798 static StringBuffer *msgfile_msg(object *book, size_t booksize) {
1799  int weight;
1800  StringBuffer *ret = stringbuffer_new();
1801 
1803 
1804  if (msg && strlen(msg->message) <= booksize) {
1806  if (msg->identifier != NULL) {
1807  char km[HUGE_BUF];
1810  snprintf(km, sizeof(km), "message:%s", msg->identifier);
1811  object_set_value(book, "knowledge_marker", km, 1);
1812  }
1813  if (msg->quest_code) {
1814  /* add a 'apply' hook to launch the quest */
1815  archetype *at = find_archetype("quest_advance_apply");
1816  if (at != NULL) {
1817  object *event = object_create_arch(at);
1818  FREE_AND_COPY(event->name, msg->quest_code);
1819  object_insert_in_ob(event, book);
1820  }
1821  }
1822  } else
1823  stringbuffer_append_string(ret, "\n <undecipherable text>");
1824 
1825  return ret;
1826 }
1827 
1842 static StringBuffer *god_info_msg(int level, size_t booksize, object *book) {
1843  int what = 0;
1844  const object *god = pntr_to_god_obj(get_rand_god());
1845  StringBuffer *desc = NULL;
1846 
1847  if (!god)
1848  return NULL; /* oops, problems... */
1849 
1850  if (booksize > BOOK_BUF) {
1851  LOG(llevError, "common/readable.c:god_info_msg() - passed in booksize (%lu) is larger than book buffer (%d)\n", (unsigned long)booksize, BOOK_BUF);
1852  booksize = BOOK_BUF;
1853  }
1854 
1855  if (level >= 2 && RANDOM()%2) {
1856  what |= GOD_ENEMY;
1857  }
1858  if (level >= 3 && RANDOM()%2) {
1859  what |= GOD_HOLYWORD;
1860  }
1861  if (level >= 4 && RANDOM()%2) {
1862  what |= GOD_RESISTANCES;
1863  }
1864  if (level >= 5 && RANDOM()%2) {
1865  what |= GOD_SACRED;
1866  }
1867  if (level >= 6 && RANDOM()%2) {
1868  what |= GOD_BLESSED;
1869  }
1870  if (level >= 8 && RANDOM()%2) {
1871  what |= GOD_IMMUNITIES;
1872  }
1873  if (level >= 12 && RANDOM()%2) {
1874  what |= GOD_PATHS;
1875  }
1876 
1877  desc = stringbuffer_new();
1878  what = describe_god(god, what, desc, booksize);
1879 
1880  /* check to be sure new buffer size dont exceed either
1881  * the maximum buffer size, or the 'natural' size of the
1882  * book... */
1883  if (stringbuffer_length(desc) > 1 && stringbuffer_length(desc) <= booksize) {
1884  char buf[BOOK_BUF];
1885  snprintf(buf, sizeof(buf), "god:%s:%d", god->name, what);
1886  object_set_value(book, "knowledge_marker", buf, 1);
1887  return desc;
1888  }
1889 
1890  stringbuffer_delete(desc);
1891  return NULL;
1892 }
1893 
1912 void tailor_readable_ob(object *book, int msg_type) {
1913  int level = book->level ? RANDOM()%book->level+1 : 1;
1914  size_t book_buf_size;
1915  StringBuffer *message = NULL;
1916 
1917  /* safety */
1918  if (book->type != BOOK)
1919  return;
1920 
1921  if (level <= 0)
1922  return; /* if no level no point in doing any more... */
1923 
1924  /* Max text length this book can have. */
1925  book_buf_size = BOOKSIZE(book);
1926  book_buf_size -= strlen("\n"); /* Keep enough for final \n. */
1927  assert(book_buf_size < BOOK_BUF);
1928 
1929  /* &&& The message switch &&& */
1930  /* Below all of the possible types of messages in the "book"s.
1931  */
1932  /*
1933  * IF you add a new type of book msg, you will have to do several things.
1934  * 1) make sure there is an entry in the msg switch below!
1935  * 2) make sure there is an entry in max_titles[] array.
1936  * 3) make sure there are entries for your case in new_text_title()
1937  * and add_authour().
1938  * 4) you may want separate authour/book name arrays in read.h
1939  */
1940 
1941  if (msg_type >= (int)arraysize(max_titles))
1942  msg_type = 0;
1943 
1944  msg_type = msg_type > 0 ? msg_type : (int)(RANDOM()%6);
1945  switch (msg_type) {
1946  case MSGTYPE_MONSTER:
1947  message = mon_info_msg(level, book_buf_size, book);
1948  break;
1949 
1950  case MSGTYPE_ARTIFACT:
1951  message = artifact_msg(level, book_buf_size);
1952  break;
1953 
1954  case MSGTYPE_SPELLPATH: /* grouping incantations/prayers by path */
1955  message = spellpath_msg(level, book_buf_size, NULL);
1956  break;
1957 
1958  case MSGTYPE_ALCHEMY: /* describe an alchemy formula */
1959  make_formula_book(book, level);
1960  /* make_formula_book already gives title */
1961  return;
1962  break;
1963 
1964  case MSGTYPE_GODS: /* bits of information about a god */
1965  message = god_info_msg(level, book_buf_size, book);
1966  break;
1967 
1968  case MSGTYPE_LIB: /* use info list in lib/ */
1969  default:
1970  message = msgfile_msg(book, book_buf_size);
1971  break;
1972  }
1973 
1974  if (message != NULL) {
1975  char *final;
1976  stringbuffer_append_string(message, "\n");
1977  final = stringbuffer_finish(message);
1978  object_set_msg(book, final);
1979  free(final);
1980  /* lets give the "book" a new name, which may be a compound word */
1981  change_book(book, msg_type);
1982  }
1983 }
1984 
1985 /*****************************************************************************
1986  *
1987  * Cleanup routine for readable stuff.
1988  *
1989  *****************************************************************************/
1990 
1994 void free_all_readable(void) {
1995  titlelist *tlist, *tnext;
1996  title *title1, *titlenext;
1997  GeneralMessage *lmsg, *nextmsg;
1998  objectlink *monlink, *nextmon;
1999 
2000  LOG(llevDebug, "freeing all book information\n");
2001 
2002  for (tlist = booklist; tlist != NULL; tlist = tnext) {
2003  tnext = tlist->next;
2004  for (title1 = tlist->first_book; title1; title1 = titlenext) {
2005  titlenext = title1->next;
2006  if (title1->name)
2007  free_string(title1->name);
2008  if (title1->authour)
2009  free_string(title1->authour);
2010  if (title1->archname)
2011  free_string(title1->archname);
2012  free(title1);
2013  }
2014  free(tlist);
2015  }
2016  for (monlink = first_mon_info; monlink; monlink = nextmon) {
2017  nextmon = monlink->next;
2018  free(monlink);
2019  }
2020 }
2021 
2022 /*****************************************************************************
2023  *
2024  * Writeback routine for updating the bookarchive.
2025  *
2026  ****************************************************************************/
2027 
2032  FILE *fp;
2033  OutputFile of;
2034  int index;
2035  char fname[MAX_BUF];
2036  title *book;
2037  titlelist *bl;
2038 
2039  /* If nothing changed, don't write anything */
2041  return;
2042 
2043  snprintf(fname, sizeof(fname), "%s/bookarch", settings.localdir);
2044  LOG(llevDebug, "Updating book archive: %s...\n", fname);
2045 
2046  fp = of_open(&of, fname);
2047  if (fp == NULL)
2048  return;
2049 
2050  for (bl = get_titlelist(0), index = 0; bl; bl = bl->next, index++) {
2051  for (book = bl->first_book; book; book = book->next)
2052  if (book && book->authour) {
2053  fprintf(fp, "title %s\n", book->name);
2054  fprintf(fp, "authour %s\n", book->authour);
2055  fprintf(fp, "arch %s\n", book->archname);
2056  fprintf(fp, "level %d\n", book->level);
2057  fprintf(fp, "type %d\n", index);
2058  /* C89 doesn't have %zu... */
2059  fprintf(fp, "size %lu\n", (unsigned long)book->size);
2060  fprintf(fp, "index %d\n", book->msg_index);
2061  fprintf(fp, "end\n");
2062  }
2063  }
2064  if (!of_close(&of))
2065  return;
2066 
2067  if (chmod(fname, SAVE_MODE) != 0) {
2068  LOG(llevError, "Could not set permissions on '%s'\n", fname);
2069  }
2070 
2072 }
2073 
2082  uint8_t subtype = readable->subtype;
2083 
2084  if (subtype > last_readable_subtype)
2085  return &readable_message_types[0];
2086  return &readable_message_types[subtype];
2087 }
2088 
2095  return message->title;
2096 }
2097 
2104  return message->message;
2105 }
2106 
2112 const Face *get_message_face(const GeneralMessage *message) {
2113  return message->face;
2114 }
#define MSG_TYPE_PAPER_SCROLL_OLD_1
Definition: newclient.h:437
#define MSG_TYPE_SIGN_DIR_LEFT
Definition: newclient.h:445
static const char *const heavy_book_name[]
Definition: readable.c:382
#define GOD_BLESSED
Definition: god.h:28
static const uint32_t spellpathdef[NRSPELLPATHS]
Definition: readable.c:149
#define MSG_TYPE_PAPER_LETTER_NEW_1
Definition: newclient.h:433
#define PATH_MISSILE
Definition: spells.h:17
#define MSGTYPE_ARTIFACT
Definition: readable.c:82
#define MSG_TYPE_CARD_ELEGANT_1
Definition: newclient.h:417
#define PATH_MIND
Definition: spells.h:23
#define SET_FLAG(xyz, p)
Definition: define.h:223
sstring get_message_title(const GeneralMessage *message)
Definition: readable.c:2094
static void make_formula_book(object *book, int level)
Definition: readable.c:1670
const artifactlist * find_artifactlist(int type)
Definition: artifact.c:642
static titlelist * get_titlelist(int i)
Definition: readable.c:606
static void add_book_to_list(const object *book, int msgtype)
Definition: readable.c:1077
unsigned char uint8_t
Definition: win32.h:161
#define PATH_TURNING
Definition: spells.h:29
void stringbuffer_append_stringbuffer(StringBuffer *sb, const StringBuffer *sb2)
Definition: stringbuffer.c:131
const char * archname
Definition: readable.c:110
StringBuffer * buf
Definition: readable.c:1591
uint32_t pnum
Definition: readable.c:1588
#define MSG_TYPE_CARD_MONEY_1
Definition: newclient.h:423
object * get_random_mon(int level)
Definition: readable.c:1251
int prayers
Definition: readable.c:1586
#define MSG_TYPE_CARD_MONEY_3
Definition: newclient.h:425
#define PATH_LIGHT
Definition: spells.h:32
archetype * find_archetype(const char *name)
Definition: assets.cpp:253
sstring title
Definition: book.h:48
sstring stringbuffer_finish_shared(StringBuffer *sb)
Definition: stringbuffer.c:85
void fatal(enum fatal_error err)
Definition: utils.c:597
struct artifactstruct * items
Definition: artifact.h:30
object * mon
Definition: comet_perf.c:75
static const arttypename art_name_array[]
Definition: readable.c:207
void archetypes_for_each(arch_op op)
Definition: assets.cpp:300
Definition: face.h:14
#define MSG_TYPE_CARD_SIMPLE_2
Definition: newclient.h:415
StringBuffer * stringbuffer_new(void)
Definition: stringbuffer.c:57
void free_string(sstring str)
Definition: shstr.c:280
#define MSG_TYPE_MONUMENT_STATUE_3
Definition: newclient.h:456
#define HUGE_BUF
Definition: define.h:37
#define MSGTYPE_MONSTER
Definition: readable.c:80
const char * object_get_value(const object *op, const char *const key)
Definition: object.c:4136
#define MSG_TYPE_MONUMENT_WALL_2
Definition: newclient.h:461
#define MSG_TYPE_PAPER_NOTE_2
Definition: newclient.h:429
linked_char * ingred
Definition: recipe.h:22
static const int last_readable_subtype
Definition: readable.c:534
#define MSG_TYPE_SIGN_DIR_RIGHT
Definition: newclient.h:446
static const char *const path_book_name[]
Definition: readable.c:173
static void init_book_archive(void)
Definition: readable.c:716
#define MSG_TYPE_PAPER_ENVELOPE_1
Definition: newclient.h:435
#define MSG_TYPE_BOOK_SPELL_SORCERER
Definition: newclient.h:410
#define MSG_TYPE_PAPER_SCROLL_NEW_1
Definition: newclient.h:439
#define PATH_RESTORE
Definition: spells.h:21
#define GOD_HOLYWORD
Definition: god.h:25
int msg_index
Definition: readable.c:113
static struct @0 sp_params
#define MSG_TYPE_MONUMENT_GRAVESTONE_1
Definition: newclient.h:457
#define PATH_FIRE
Definition: spells.h:14
#define MSG_TYPE_BOOK_QUARTO_2
Definition: newclient.h:406
const char * name
Definition: readable.c:108
#define MSG_TYPE_MONUMENT_STONE_1
Definition: newclient.h:451
#define MSG_TYPE_MONUMENT_STONE_3
Definition: newclient.h:453
int is_combination
Definition: recipe.h:31
static title * find_title(const object *book, int msgtype)
Definition: readable.c:917
#define MSG_TYPE_BOOK_SPELL_PRAYER
Definition: newclient.h:408
#define MSGTYPE_ALCHEMY
Definition: readable.c:86
static object * get_next_mon(const object *tmp)
Definition: readable.c:1332
sstring identifier
Definition: book.h:47
#define MSG_TYPE_BOOK_SPELL_PYRO
Definition: newclient.h:409
#define MSG_TYPE_BOOK_ELEGANT_1
Definition: newclient.h:403
#define MSG_TYPE_PAPER_NOTE_3
Definition: newclient.h:430
const char *const spellpathnames[NRSPELLPATHS]
Definition: init.c:108
#define MSG_TYPE_CARD_STRANGE_1
Definition: newclient.h:420
archetype * try_find_archetype(const char *name)
Definition: assets.cpp:257
struct titlestruct title
#define GOD_RESISTANCES
Definition: god.h:26
int object_set_value(object *op, const char *key, const char *value, int add_key)
Definition: object.c:4259
int chance
Definition: recipe.h:14
void object_free_drop_inventory(object *ob)
Definition: object.c:1316
#define PATH_ELEC
Definition: spells.h:16
#define MSG_TYPE_CARD_ELEGANT_3
Definition: newclient.h:419
static const char *const mon_book_name[]
Definition: readable.c:257
int nstrtok(const char *buf1, const char *buf2)
Definition: readable.c:637
int buf_overflow(const char *buf1, const char *buf2, size_t bufsize)
Definition: shstr.c:398
#define MSG_TYPE_PAPER_SCROLL_OLD_2
Definition: newclient.h:438
struct titlestruct * next
Definition: readable.c:114
const char * name
Definition: readable.c:128
struct titleliststruct titlelist
uint16_t total_chance
Definition: artifact.h:28
static void do_monster(archetype *at)
Definition: readable.c:848
#define MSG_TYPE_CARD_SIMPLE_3
Definition: newclient.h:416
const Face * face
Definition: book.h:51
object * object_new(void)
Definition: object.c:1011
sstring title
Definition: recipe.h:11
void stringbuffer_append_string(StringBuffer *sb, const char *str)
Definition: stringbuffer.c:95
char ** arch_name
Definition: recipe.h:13
object * object_insert_in_ob(object *op, object *where)
Definition: object.c:2602
int total_chance
Definition: recipe.h:38
static StringBuffer * artifact_msg(unsigned int level, size_t booksize)
Definition: readable.c:1512
size_t booksize
Definition: readable.c:1590
object * create_archetype(const char *name)
Definition: arch.cpp:281
#define MSG_TYPE_BOOK_ELEGANT_2
Definition: newclient.h:404
#define FLAG_UNAGGRESSIVE
Definition: define.h:272
static StringBuffer * mon_info_msg(int level, size_t booksize, object *book)
Definition: readable.c:1360
struct namebytype arttypename
#define MSG_TYPE_PAPER_SCROLL_MAGIC
Definition: newclient.h:441
#define BOOK_BUF
Definition: book.h:16
#define snprintf
Definition: win32.h:46
#define MSG_TYPE_MONUMENT_WALL_3
Definition: newclient.h:462
#define GOD_IMMUNITIES
Definition: god.h:29
static StringBuffer * artifact_describe(const artifact *art, const artifactlist *al, int message, int art_name, int separator)
Definition: readable.c:1417
struct linked_char * next
Definition: global.h:88
#define FLAG_IDENTIFIED
Definition: define.h:261
#define PATH_TRANSMUTE
Definition: spells.h:27
#define MSG_TYPE_CARD_SIMPLE_1
Definition: newclient.h:414
int strtoint(const char *buf)
Definition: recipe.c:541
godlink * get_rand_god(void)
Definition: holy.c:102
int of_close(OutputFile *of)
Definition: output_file.c:61
static int need_to_write_bookarchive
Definition: readable.c:144
#define PATH_DEATH
Definition: spells.h:31
#define FLAG_CHANGING
Definition: define.h:263
linked_char * allowed
Definition: artifact.h:19
struct titleliststruct * next
Definition: readable.c:123
unsigned int level
Definition: readable.c:111
#define MSG_TYPE_CARD_STRANGE_3
Definition: newclient.h:422
size_t stringbuffer_length(StringBuffer *sb)
Definition: stringbuffer.c:162
void tailor_readable_ob(object *book, int msg_type)
Definition: readable.c:1912
#define PATH_PROT
Definition: spells.h:13
const object * pntr_to_god_obj(godlink *godlnk)
Definition: holy.c:124
static const char *const book_author[]
Definition: readable.c:396
GeneralMessage * get_random_message()
Definition: assets.cpp:318
#define arraysize(arrayname)
Definition: readable.c:97
int describe_god(const object *god, int what, StringBuffer *buf, size_t maxlen)
Definition: holy.c:138
static const char *const gods_author[]
Definition: readable.c:309
#define PATH_SELF
Definition: spells.h:18
int done
Definition: readable.c:1592
static int nrofmon
Definition: readable.c:143
static titlelist * booklist
Definition: readable.c:138
sstring skill
Definition: recipe.h:26
static void add_book(title *book, int type, const char *fname, int lineno)
Definition: readable.c:834
#define QUERY_FLAG(xyz, p)
Definition: define.h:225
static void do_spellpath_msg(archetype *at)
Definition: readable.c:1596
void init_readable(void)
Definition: readable.c:886
#define MSG_TYPE_BOOK_SPELL_SUMMONER
Definition: newclient.h:411
#define MSG_TYPE_SIGN_BASIC
Definition: newclient.h:444
const Face * get_message_face(const GeneralMessage *message)
Definition: readable.c:2112
static void add_author(object *op, int msgtype)
Definition: readable.c:1005
int index
Definition: recipe.h:18
sstring quest_code
Definition: book.h:50
#define MAX_BUF
Definition: define.h:35
#define NRSPELLPATHS
Definition: spells.h:40
static StringBuffer * god_info_msg(int level, size_t booksize, object *book)
Definition: readable.c:1842
#define MSG_TYPE_CARD
Definition: newclient.h:371
#define MSGTYPE_SPELLPATH
Definition: readable.c:84
static const char *const mon_author[]
Definition: readable.c:275
#define MSG_TYPE_CARD_STRANGE_2
Definition: newclient.h:421
#define MSG_TYPE_MONUMENT_STATUE_2
Definition: newclient.h:455
static StringBuffer * msgfile_msg(object *book, size_t booksize)
Definition: readable.c:1798
#define PATH_CREATE
Definition: spells.h:24
#define BOOKSIZE(xyz)
Definition: book.h:31
object * object_create_arch(archetype *at)
Definition: arch.cpp:301
#define MSG_TYPE_PAPER_LETTER_NEW_2
Definition: newclient.h:434
static int unique_book(const object *book, int msgtype)
Definition: readable.c:1053
static const char *const gods_book_name[]
Definition: readable.c:294
const char * sstring
Definition: global.h:40
#define MSG_TYPE_MONUMENT_STONE_2
Definition: newclient.h:452
#define MSG_TYPE_MONUMENT_WALL_1
Definition: newclient.h:460
unsigned int uint32_t
Definition: win32.h:162
#define MSG_TYPE_BOOK_CLASP_1
Definition: newclient.h:401
const char * authour
Definition: readable.c:109
#define MSG_TYPE_MONUMENT
Definition: newclient.h:374
const char * name
Definition: global.h:87
#define MSG_TYPE_MONUMENT_STATUE_1
Definition: newclient.h:454
#define PATH_INFO
Definition: spells.h:26
StringBuffer * describe_item(const object *op, const object *owner, int use_media_tags, StringBuffer *buf)
Definition: item.c:935
#define PATH_FROST
Definition: spells.h:15
static const char *const art_author[]
Definition: readable.c:243
static titlelist * get_empty_booklist(void)
Definition: readable.c:561
#define RANDOM()
Definition: define.h:681
static const readable_message_type readable_message_types[]
Definition: readable.c:470
#define FREE_AND_COPY(sv, nv)
Definition: global.h:204
#define MSG_TYPE_PAPER_SCROLL_NEW_2
Definition: newclient.h:440
#define GOD_ENEMY
Definition: god.h:24
#define MSG_TYPE_CARD_ELEGANT_2
Definition: newclient.h:418
#define PATH_TRANSFER
Definition: spells.h:28
const char * localdir
Definition: global.h:246
#define MSG_TYPE_CARD_MONEY_2
Definition: newclient.h:424
static const char *const formula_author[]
Definition: readable.c:341
int type
Definition: readable.c:129
int allowed_size
Definition: artifact.h:20
int did_first_sp
Definition: readable.c:1587
size_t arch_names
Definition: recipe.h:12
#define MSG_TYPE_SIGN_MAGIC_MOUTH
Definition: newclient.h:448
void stringbuffer_delete(StringBuffer *sb)
Definition: stringbuffer.c:71
#define MSG_TYPE_PAPER_ENVELOPE_2
Definition: newclient.h:436
#define GOD_PATHS
Definition: god.h:30
#define PATH_ABJURE
Definition: spells.h:20
struct Settings settings
Definition: init.c:39
FILE * of_open(OutputFile *of, const char *fname)
Definition: output_file.c:30
#define MSG_TYPE_PAPER
Definition: newclient.h:372
#define GOD_SACRED
Definition: god.h:27
int book_overflow(const char *buf1, const char *buf2, size_t booksize)
Definition: readable.c:697
#define MSGTYPE_MSGFILE
Definition: readable.c:90
#define SAVE_MODE
Definition: config.h:566
#define MSG_TYPE_PAPER_LETTER_OLD_2
Definition: newclient.h:432
void free_all_readable(void)
Definition: readable.c:1994
struct recipestruct * next
Definition: recipe.h:24
static const int max_titles[6]
Definition: readable.c:537
static const char *const path_author[]
Definition: readable.c:183
void add_abilities(object *op, const object *change)
Definition: artifact.c:288
void write_book_archive(void)
Definition: readable.c:2031
sstring get_message_body(const GeneralMessage *message)
Definition: readable.c:2103
static const char *const light_book_name[]
Definition: readable.c:363
static StringBuffer * mon_desc(const object *mon)
Definition: readable.c:1314
#define MSG_TYPE_PAPER_LETTER_OLD_1
Definition: newclient.h:431
char * strtoktolin(const char *buf1, const char *buf2, char *retbuf, size_t size)
Definition: readable.c:667
static objectlink * first_mon_info
Definition: readable.c:141
sstring add_string(const char *str)
Definition: shstr.c:124
struct titlestruct * first_book
Definition: readable.c:122
#define MSG_TYPE_BOOK_SPELL_EVOKER
Definition: newclient.h:407
void stringbuffer_append_printf(StringBuffer *sb, const char *format,...)
Definition: stringbuffer.c:104
archetype * find_archetype_by_object_name(const char *name)
Definition: arch.cpp:55
#define MSG_TYPE_SIGN_DIR_BOTH
Definition: newclient.h:447
#define MSG_TYPE_MONUMENT_GRAVESTONE_2
Definition: newclient.h:458
static void change_book(object *book, int msgtype)
Definition: readable.c:1119
#define FLAG_MONSTER
Definition: define.h:245
void object_copy(const object *src_ob, object *dest_ob)
Definition: object.c:824
static void init_mon_info(void)
Definition: readable.c:868
const readable_message_type * get_readable_message_type(object *readable)
Definition: readable.c:2081
#define MSG_TYPE_MONUMENT_GRAVESTONE_3
Definition: newclient.h:459
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.c:51
#define MSGTYPE_LIB
Definition: readable.c:78
#define MSG_TYPE_BOOK
Definition: newclient.h:370
void object_set_msg(object *op, const char *msg)
Definition: object.c:4570
static const char *const formula_book_name[]
Definition: readable.c:327
void query_name(const object *op, char *buf, size_t size)
Definition: item.c:583
object * item
Definition: artifact.h:15
#define PATH_DETONATE
Definition: spells.h:22
uint16_t chance
Definition: artifact.h:16
#define MSG_TYPE_BOOK_QUARTO_1
Definition: newclient.h:405
static const char *const book_descrpt[]
Definition: readable.c:443
#define MSGTYPE_GODS
Definition: readable.c:88
static void new_text_name(object *book, int msgtype)
Definition: readable.c:956
static const char *const art_book_name[]
Definition: readable.c:227
size_t size
Definition: readable.c:112
#define MSG_TYPE_PAPER_NOTE_1
Definition: newclient.h:428
#define MSG_TYPE_BOOK_CLASP_2
Definition: newclient.h:402
static StringBuffer * spellpath_msg(int level, size_t booksize, StringBuffer *buf)
Definition: readable.c:1630
sstring cauldron
Definition: recipe.h:27
char * stringbuffer_finish(StringBuffer *sb)
Definition: stringbuffer.c:76
#define PATH_WOUNDING
Definition: spells.h:30
size_t strlcpy(char *dst, const char *src, size_t size)
Definition: porting.c:370
struct recipestruct * items
Definition: recipe.h:40
recipelist * get_formulalist(int i)
Definition: recipe.c:96
struct artifactstruct * next
Definition: artifact.h:18
#define MSG_TYPE_SIGN
Definition: newclient.h:373
sstring message
Definition: book.h:49
#define PATH_TELE
Definition: spells.h:25
#define MAX_TITLE_CHECK
Definition: readable.c:75
#define PATH_SUMMON
Definition: spells.h:19
static title * get_empty_book(void)
Definition: readable.c:581