Crossfire Server, Trunk  R20911
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 
38 /* Define this if you want to archive book titles by contents.
39  * This option should enforce UNIQUE combinations of titles,authors and
40  * msg contents during and *between *game sessions.
41  * Note: a slight degeneracy exists since books are archived based on an integer
42  * index value calculated from the message text (similar to alchemy formulae).
43  * Sometimes two widely different messages have the same index value (rare). In
44  * this case, it is possible to occasionally generate 2 books with same title and
45  * different message content. Not really a bug, but rather a feature. This action
46  * should keeps player on their toes ;).
47  * Also, note that there is *finite *space available for archiving message and titles.
48  * Once this space is used, books will stop being archived. Not a serious problem
49  * under the current regime, since there are generally fewer possible (random)
50  * messages than space available on the titlelists.
51  * One exception (for sure) are the monster messages. But no worries, you should
52  * see all of the monster info in some order (but not all possble combinations)
53  * before the monster titlelist space is run out. You can increase titlelist
54  * space by increasing the array sizes for the monster book_authours and book_names
55  * (see max_titles[] array and include/read.h). Since the unique_book algorthm is
56  * kinda stupid, this program *may *slow down program execution if defined (but I don't
57  * think its a significant problem, at least, I have no problems running this option
58  * on a Sparc 10! Also, once archive title lists are filled and/or all possible msg
59  * combinations have been generated, unique_book isnt called anymore. It takes 5-10
60  * sessions for this to happen).
61  * Final note: the game remembers book/title/msg combinations from reading the
62  * file lib/bookarch. If you REMOVE this file, you will lose your archive. So
63  * be sure to copy it over to the new lib directory when you change versions.
64  * -b.t.
65  */
66 
67 /* This flag is useful to see what kind of output messages are created */
68 /* #define BOOK_MSG_DEBUG */
69 
70 /* This flag is useful for debugging archiving action */
71 /* #define ARCHIVE_DEBUG */
72 
74 #define MAX_TITLE_CHECK 20
75 
77 #define MSGTYPE_LIB 0
78 
79 #define MSGTYPE_MONSTER 1
80 
81 #define MSGTYPE_ARTIFACT 2
82 
83 #define MSGTYPE_SPELLPATH 3
84 
85 #define MSGTYPE_ALCHEMY 4
86 
87 #define MSGTYPE_GODS 5
88 
89 #define MSGTYPE_MSGFILE 6
90 
96 #define arraysize(arrayname) (sizeof(arrayname)/sizeof(*(arrayname)))
97 
98 /* Moved these structures from struct.h to this file in 0.94.3 - they
99  * are not needed anyplace else, so why have them globally declared?
100  */
101 
106 typedef struct titlestruct {
107  const char *name;
108  const char *authour;
109  const char *archname;
110  unsigned int level;
111  size_t size;
112  int msg_index;
113  struct titlestruct *next;
114 } title;
115 
119 typedef struct titleliststruct {
120  int number;
123 } titlelist;
124 
126 typedef struct namebytype {
127  const char *name;
128  int type;
129 } arttypename;
130 
135  int chance;
141  unsigned int face;
143 };
144 
145 static void add_book(title *book, int type, const char *fname, int lineno);
146 
151 static titlelist *booklist = NULL;
152 
154 static objectlink *first_mon_info = NULL;
155 
156 static int nrofmon = 0,
164 static GeneralMessage *first_msg = NULL;
165 
169 static int msg_total_chance = 0;
170 
175  PATH_PROT,
176  PATH_FIRE,
177  PATH_FROST,
178  PATH_ELEC,
179  PATH_MISSILE,
180  PATH_SELF,
181  PATH_SUMMON,
182  PATH_ABJURE,
183  PATH_RESTORE,
185  PATH_MIND,
186  PATH_CREATE,
187  PATH_TELE,
188  PATH_INFO,
191  PATH_TURNING,
193  PATH_DEATH,
194  PATH_LIGHT
195 };
196 
198 static const char *const path_book_name[] = {
199  "codex",
200  "compendium",
201  "documentary",
202  "exposition",
203  "tables",
204  "treatise"
205 };
206 
208 static const char *const path_author[] = {
209  "aether",
210  "arcana",
211  "astral byways",
212  "connections",
213  "the Grey Council",
214  "deep pathways",
215  "knowledge",
216  "magic",
217  "mystic ways",
218  "pathways",
219  "power",
220  "spells",
221  "transforms",
222  "the mystic veil",
223  "unknown spells"
224 };
225 
232 static const arttypename art_name_array[] = {
233  { "Helmet", HELMET },
234  { "Amulet", AMULET },
235  { "Shield", SHIELD },
236  { "Bracers", BRACERS },
237  { "Boots", BOOTS },
238  { "Cloak", CLOAK },
239  { "Gloves", GLOVES },
240  { "Gridle", GIRDLE },
241  { "Ring", RING },
242  { "Horn", ROD },
243  { "Missile Weapon", BOW },
244  { "Missile", ARROW },
245  { "Hand Weapon", WEAPON },
246  { "Artifact", SKILL },
247  { "Food", FOOD },
248  { "Body Armour", ARMOUR }
249 };
250 
252 static const char *const art_book_name[] = {
253  "collection",
254  "file",
255  "files",
256  "guide",
257  "handbook",
258  "index",
259  "inventory",
260  "list",
261  "listing",
262  "record",
263  "record book"
264 };
265 
267 static const char *const art_author[] = {
268  "ancient things",
269  "artifacts",
270  "Havlor", /* ancient warrior scribe :) */
271  "items",
272  "lost artifacts",
273  "the ancients",
274  "treasures",
275  "useful things"
276 };
277 
281 static const char *const mon_book_name[] = {
282  "beastuary",
283  "catalog",
284  "compilation",
285  "collection",
286  "encyclopedia",
287  "guide",
288  "handbook",
289  "list",
290  "manual",
291  "notes",
292  "record",
293  "register",
294  "volume"
295 };
296 
298 static const char *const mon_author[] = {
299  "beasts",
300  "creatures",
301  "dezidens",
302  "dwellers",
303  "evil nature",
304  "life",
305  "monsters",
306  "nature",
307  "new life",
308  "residents",
309  "the spawn",
310  "the living",
311  "things"
312 };
313 
317 static const char *const gods_book_name[] = {
318  "devotional",
319  "devout notes",
320  "divine text",
321  "divine work",
322  "holy book",
323  "holy record",
324  "illuminated text",
325  "moral text",
326  "sacred guide",
327  "testament",
328  "transcript"
329 };
330 
332 static const char *const gods_author[] = {
333  "cults",
334  "joy",
335  "lasting curse",
336  "madness",
337  "religions",
338  "the dead",
339  "the gods",
340  "the heirophant",
341  "the poor priest",
342  "the priestess",
343  "pain",
344  "white"
345 };
346 
350 static const char *const formula_book_name[] = {
351  "cookbook",
352  "formulary",
353  "lab book",
354  "lab notes",
355  "recipe book",
356  "experiment record",
357  "work plan",
358  "design notes",
359  "research notes"
360 };
361 
363 static const char *const formula_author[] = {
364  "Albertus Magnus",
365  "alchemy",
366  "balms",
367  "creation",
368  "dusts",
369  "magical manufacture",
370  "making",
371  "philosophical items",
372  "potions",
373  "powders",
374  "the cauldron",
375  "the lamp black",
376  "transmutation",
377  "waters"
378 };
379 
385 static const char *const light_book_name[] = {
386  "calendar",
387  "datebook",
388  "diary",
389  "guidebook",
390  "handbook",
391  "ledger",
392  "notes",
393  "notebook",
394  "octavo",
395  "pamphlet",
396  "practicum",
397  "script",
398  "transcript"
399 };
400 
402 static const char *const heavy_book_name[] = {
403  "catalog",
404  "compendium",
405  "guide",
406  "manual",
407  "opus",
408  "tome",
409  "treatise",
410  "volume",
411  "work"
412 };
413 
415 static const char *const book_author[] = {
416  "Abdulah",
417  "Al'hezred",
418  "Alywn",
419  "Arundel",
420  "Arvind",
421  "Aerlingas",
422  "Bacon",
423  "Baliqendii",
424  "Bosworth",
425  "Beathis",
426  "Bertil",
427  "Cauchy",
428  "Chakrabarti",
429  "der Waalis",
430  "Dirk",
431  "Djwimii",
432  "Eisenstaadt",
433  "Fendris",
434  "Frank",
435  "Habbi",
436  "Harlod",
437  "Ichibod",
438  "Janus",
439  "June",
440  "Laplace",
441  "Magnuson",
442  "Nandii",
443  "Nitfeder",
444  "Norris",
445  "Parael",
446  "Penhew",
447  "Sophia",
448  "Skilly",
449  "Tahir",
450  "Thockmorton",
451  "Thomas",
452  "van Helsing",
453  "van Pelt",
454  "Voormis",
455  "Xavier",
456  "Xeno",
457  "Zardoz",
458  "Zagy"
459 };
460 
462 static const char *const book_descrpt[] = {
463  "ancient",
464  "cryptic",
465  "cryptical",
466  "dusty",
467  "hierarchical",
468  "grizzled",
469  "gold-gilt",
470  "great",
471  "lost",
472  "magnificent",
473  "musty",
474  "mythical",
475  "mystical",
476  "rustic",
477  "stained",
478  "silvered",
479  "transcendental",
480  "weathered"
481 };
482 
490  /*subtype 0 */ { 0, 0 },
491  /* book messages subtypes */
492  /*subtype 1 */ { MSG_TYPE_BOOK, MSG_TYPE_BOOK_CLASP_1 },
501  /*subtype 10 */ { MSG_TYPE_BOOK, MSG_TYPE_BOOK_SPELL_SORCERER },
503  /* card messages subtypes*/
512  /*subtype 20 */ { MSG_TYPE_CARD, MSG_TYPE_CARD_STRANGE_3 },
516  /* Paper messages subtypes */
523  /*subtype 30 */ { MSG_TYPE_PAPER, MSG_TYPE_PAPER_LETTER_NEW_2 },
531  /* road signs messages subtypes */
534  /*subtype 40 */ { MSG_TYPE_SIGN, MSG_TYPE_SIGN_DIR_RIGHT },
536  /* stones and monument messages */
550 };
551 
553 static const int last_readable_subtype = arraysize(readable_message_types);
554 
556 static const int max_titles[6] = {
558  arraysize(mon_book_name)*arraysize(mon_author), /* MSGTYPE_MONSTER */
559  arraysize(art_book_name)*arraysize(art_author), /* MSGTYPE_ARTIFACT */
560  arraysize(path_book_name)*arraysize(path_author), /* MSGTYPE_SPELLPATH */
561  arraysize(formula_book_name)*arraysize(formula_author), /* MSGTYPE_ALCHEMY */
562  arraysize(gods_book_name)*arraysize(gods_author), /* MSGTYPE_GODS */
563 };
564 
565 /******************************************************************************
566  *
567  * Start of misc. readable functions used by others functions in this file
568  *
569  *****************************************************************************/
570 
581  titlelist *bl = (titlelist *)malloc(sizeof(titlelist));
582 
583  if (bl == NULL)
585  bl->number = 0;
586  bl->first_book = NULL;
587  bl->next = NULL;
588  return bl;
589 }
590 
600 static title *get_empty_book(void) {
601  title *t = (title *)malloc(sizeof(title));
602 
603  if (t == NULL)
605  t->name = NULL;
606  t->archname = NULL;
607  t->authour = NULL;
608  t->level = 0;
609  t->size = 0;
610  t->msg_index = 0;
611  t->next = NULL;
612  return t;
613 }
614 
625 static titlelist *get_titlelist(int i) {
626  titlelist *tl;
627  int number;
628 
629  if (i < 0 || i >= (int)arraysize(max_titles)) {
630  LOG(llevInfo, "Warning: invalid book index %d, using 0 instead\n", i);
631  return booklist;
632  }
633 
634  for (tl = booklist, number = i; tl && number; tl = tl->next, number--) {
635  if (!tl->next)
636  tl->next = get_empty_booklist();
637  }
638 
639  return tl;
640 }
641 
642 /* HANDMADE STRING FUNCTIONS.., perhaps these belong in another file
643  * (shstr.c ?), but the quantity BOOK_BUF will need to be defined. */
644 
656 int nstrtok(const char *buf1, const char *buf2) {
657  char *tbuf, buf[MAX_BUF];
658  int number = 0;
659 
660  if (!buf1 || !buf2)
661  return 0;
662 
663  snprintf(buf, sizeof(buf), "%s", buf1);
664  for (tbuf = strtok(buf, buf2); tbuf; tbuf = strtok(NULL, buf2)) {
665  number++;
666  }
667  return number;
668 }
669 
686 char *strtoktolin(const char *buf1, const char *buf2, char *retbuf, size_t size) {
687  int maxi, i = nstrtok(buf1, buf2);
688  char *tbuf, buf[MAX_BUF];
689 
690  maxi = i;
691  snprintf(buf, sizeof(buf), "%s", buf1);
692  snprintf(retbuf, size, " ");
693  for (tbuf = strtok(buf, buf2); tbuf && i > 0; tbuf = strtok(NULL, buf2)) {
694  snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "%s", tbuf);
695  i--;
696  if (i == 1 && maxi > 1)
697  snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), " and ");
698  else if (i > 0 && maxi > 1)
699  snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), ", ");
700  else
701  snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), ".");
702  }
703  return retbuf;
704 }
705 
716 int book_overflow(const char *buf1, const char *buf2, size_t booksize) {
717  if (buf_overflow(buf1, buf2, BOOK_BUF-2) /* 2 less so always room for trailing \n */
718  || buf_overflow(buf1, buf2, booksize))
719  return 1;
720  return 0;
721 }
722 
723 /*****************************************************************************
724  *
725  * Start of initialization related functions.
726  *
727  ****************************************************************************/
728 
734 static void init_msgfile(void) {
735  FILE *fp;
736  char buf[MAX_BUF], msgbuf[HUGE_BUF], fname[MAX_BUF], *cp;
737  int text = 0, nrofmsg = 0;
738  static int did_init_msgfile = 0;
739 
740  if (did_init_msgfile)
741  return;
742  did_init_msgfile = 1;
743 
744  snprintf(fname, sizeof(fname), "%s/messages", settings.datadir);
745  LOG(llevDebug, "Reading messages from %s...\n", fname);
746 
747  fp = fopen(fname, "r");
748  if (fp != NULL) {
749  GeneralMessage *tmp = NULL;
750  int lineno;
751  int error_lineno;
752 
753  error_lineno = 0;
754  for (lineno = 1; fgets(buf, MAX_BUF, fp) != NULL; lineno++) {
755  if (*buf == '#')
756  continue;
757  cp = strchr(buf, '\n');
758  if (cp != NULL) {
759  // Remove trailing whitespace
760  while (cp > buf && (cp[-1] == ' ' || cp[-1] == '\t'))
761  cp--;
762  /* If we make sure there is a newline here,
763  * we can avoid the auto-append of it and make long
764  * blocks of text not get split.
765  * But only do that if we are getting the message.
766  * Everywhere else we do not want the newline.
767  * Daniel Hawkins 2018-10-24
768  */
769  if (text)
770  {
771  *cp = '\n';
772  // to have found a newline means we have room for a null terminator, too
773  *(++cp)= '\0';
774  }
775  else
776  *cp = '\0';
777  }
778  if (tmp != NULL) {
779  if (text && strncmp(buf, "ENDMSG", 6) == 0) {
780  if (strlen(msgbuf) > BOOK_BUF) {
781  LOG(llevDebug, "Warning: this string exceeded max book buf size:\n");
782  LOG(llevDebug, " %s\n", msgbuf);
783  }
784  tmp->message = add_string(msgbuf);
785  tmp->next = first_msg;
786  first_msg = tmp;
787  nrofmsg++;
788  if (tmp->identifier != NULL && tmp->title == NULL) {
789  LOG(llevError, "Error: message can't have identifier without title, on line %d\n", error_lineno);
791  }
792  tmp = NULL;
793  text = 0;
794  } else if (text) {
795  if (!buf_overflow(msgbuf, buf, HUGE_BUF-1)) {
796  strcat(msgbuf, buf);
797  // If there is a newline in the text, it will be included in the output where it belongs
798  // We should avoid really long lines of text getting split up this way.
799  } else if (error_lineno != 0) {
800  LOG(llevInfo, "Warning: truncating book at %s, line %d\n", fname, error_lineno);
801  }
802  } else if (strcmp(buf, "TEXT") == 0) {
803  text = 1;
804  } else if (strncmp(buf, "CHANCE ", 7) == 0) {
805  tmp->chance = atoi(buf + 7);
806  msg_total_chance += tmp->chance;
807  } else if (strncmp(buf, "TITLE ", 6) == 0) {
808  tmp->title = add_string(buf + 6);
809  } else if (strncmp(buf, "QUEST ", 6) == 0) {
810  tmp->quest_code = add_string(buf + 6);
811  } else if (strncmp(buf, "FACE ", 5) == 0) {
812  unsigned int face = find_face(buf + 5, (unsigned int)-1);
813  if (face != (unsigned int)-1) {
814  tmp->face = face;
815  } else {
816  LOG(llevInfo, "Warning: unknown face %s for message %s, line %d\n", buf + 5, tmp->identifier, error_lineno);
817  }
818  } else if (error_lineno != 0) {
819  LOG(llevInfo, "Warning: unknown line %s, line %d\n", buf, error_lineno);
820  }
821  } else if (strncmp(buf, "MSG", 3) == 0) {
822  error_lineno = lineno;
823  tmp = (GeneralMessage *)calloc(1, sizeof(GeneralMessage));
824  tmp->face = -1;
825  strcpy(msgbuf, " "); /* reset msgbuf for new message */
826  if (buf[3] == ' ') {
827  int i = 4;
828  while (buf[i] == ' ' && buf[i] != '\0')
829  i++;
830  if (buf[i] != '\0') {
831  tmp->identifier = add_string(buf + i);
832  if (get_message_from_identifier(buf + i)) {
833  LOG(llevError, "Duplicated message identifier %s at line %d\n", buf + i, error_lineno);
835  }
836  }
837  }
838  } else {
839  LOG(llevInfo, "Warning: syntax error at %s, line %d\n", fname, lineno);
840  }
841  }
842  fclose(fp);
843 
844  if (tmp != NULL) {
845  LOG(llevError, "Invalid file %s", fname);
847  }
848  }
849 
850  LOG(llevDebug, "done messages, found %d for total chance %d.\n", nrofmsg, msg_total_chance);
851 }
852 
859 static void init_book_archive(void) {
860  FILE *fp;
861  int nroftitle = 0;
862  char buf[MAX_BUF], fname[MAX_BUF], *cp;
863  static int did_init_barch = 0;
864 
865  if (did_init_barch)
866  return;
867  did_init_barch = 1;
868 
869  if (!booklist)
870  booklist = get_empty_booklist();
871 
872  snprintf(fname, sizeof(fname), "%s/bookarch", settings.localdir);
873  LOG(llevDebug, " Reading bookarch from %s...\n", fname);
874 
875  fp = fopen(fname, "r");
876  if (fp != NULL) {
877  int type;
878  size_t i;
879  titlelist *bl;
880  int lineno;
881  title *book;
882  int skipping;
883 
884  skipping = 0;
885  book = NULL;
886  type = -1;
887  for (lineno = 1; fgets(buf, MAX_BUF, fp) != NULL; lineno++) {
888  int len;
889  int value;
890 
891  if (*buf == '#')
892  continue;
893  cp = strchr(buf, '\n');
894  if (cp != NULL) {
895  while (cp > buf && (cp[-1] == ' ' || cp[-1] == '\t'))
896  cp--;
897  *cp = '\0';
898  }
899  cp = buf;
900  if (strncmp(buf, "title ", 6) == 0) {
901  skipping = 0;
902  cp = buf+6;
903  while (*cp == ' ' || *cp == '\t')
904  cp++;
905  if (*cp == '\0') {
906  LOG(llevInfo, "Warning: missing book title at %s, line %d\n", fname, lineno);
907  book = NULL;
908  } else {
909  book = get_empty_book(); /* init new book entry */
910  book->name = add_string(cp);
911  type = -1;
912  nroftitle++;
913  }
914  } else if (book == NULL) {
915  if (!skipping) {
916  skipping = 1;
917  LOG(llevInfo, "Warning: expecting 'title' at %s, line %d\n", fname, lineno);
918  }
919  } else if (strncmp(buf, "authour ", 8) == 0) {
920  cp = buf+8;
921  while (*cp == ' ' || *cp == '\t')
922  cp++;
923  if (*cp == '\0') {
924  LOG(llevInfo, "Warning: missing book authour at %s, line %d\n", fname, lineno);
925  } else {
926  book->authour = add_string(cp);
927  }
928  } else if (strncmp(buf, "arch ", 5) == 0) {
929  cp = buf+5;
930  while (*cp == ' ' || *cp == '\t')
931  cp++;
932  if (*cp == '\0') {
933  LOG(llevInfo, "Warning: missing book arch at %s, line %d\n", fname, lineno);
934  } else {
935  book->archname = add_string(cp);
936  }
937  } else if (sscanf(buf, "level %d%n", &value, &len) == 1 && len == (int)strlen(buf)) {
938  book->level = value;
939  } else if (sscanf(buf, "type %d%n", &value, &len) == 1 && len == (int)strlen(buf)) {
940  type = value;
941  } else if (sscanf(buf, "size %d%n", &value, &len) == 1 && len == (int)strlen(buf)) {
942  book->size = value;
943  } else if (sscanf(buf, "index %d%n", &value, &len) == 1 && len == (int)strlen(buf)) {
944  book->msg_index = value;
945  } else if (strcmp(buf, "end") == 0) { /* link it */
946  add_book(book, type, fname, lineno);
947  book = NULL;
948  type = -1;
949  } else {
950  LOG(llevInfo, "Warning: syntax error at %s, line %d\n", fname, lineno);
951  }
952  }
953  if (book != NULL) {
954  LOG(llevInfo, "Warning: missing 'end' at %s, line %d\n", fname, lineno);
955  add_book(book, type, fname, lineno);
956  }
957  LOG(llevDebug, " book archives(used/avail):\n");
958  for (bl = booklist, i = 0; bl != NULL && i < arraysize(max_titles); bl = bl->next, i++) {
959  LOG(llevDebug, "(%d/%d)\n", bl->number, max_titles[i]);
960  }
961  fclose(fp);
962  }
963 
964 #ifdef BOOK_MSG_DEBUG
965  LOG(llevDebug, "\n init_book_archive() got %d titles.\n", nroftitle);
966 #endif
967  LOG(llevDebug, " done.\n");
968 }
969 
977 static void add_book(title *book, int type, const char *fname, int lineno) {
978  titlelist *bl;
979 
980  if (type == -1) {
981  LOG(llevInfo, "Warning: book with no type at %s, line %d; using type 0\n", fname, lineno);
982  type = 0;
983  }
984 
985  bl = get_titlelist(type);
986  book->next = bl->first_book;
987  bl->first_book = book;
988  bl->number++;
989 }
990 
995 static void init_mon_info(void) {
996  archetype *at;
997  static int did_init_mon_info = 0;
998 
999  if (did_init_mon_info)
1000  return;
1001  did_init_mon_info = 1;
1002 
1003  for (at = first_archetype; at != NULL; at = at->next) {
1004  if (QUERY_FLAG(&at->clone, FLAG_MONSTER)
1006  objectlink *mon = (objectlink *)malloc(sizeof(objectlink));
1007  if (!mon) {
1008  LOG(llevError, "init_mon_info: malloc failed!\n");
1009  abort();
1010  }
1011  mon->ob = &at->clone;
1012  mon->id = nrofmon;
1013  mon->next = first_mon_info;
1014  first_mon_info = mon;
1015  nrofmon++;
1016  }
1017  }
1018  LOG(llevDebug, "init_mon_info() got %d monsters\n", nrofmon);
1019 }
1020 
1027 void init_readable(void) {
1028  static int did_this = 0;
1029 
1030  if (did_this)
1031  return;
1032  did_this = 1;
1033 
1034  LOG(llevDebug, "Initializing reading data...\n");
1035  init_msgfile();
1037  init_mon_info();
1038  LOG(llevDebug, " done reading data\n");
1039 }
1040 
1041 /*****************************************************************************
1042  *
1043  * This is the start of the administrative functions when creating
1044  * new books (ie, updating title and the like)
1045  *
1046  *****************************************************************************/
1047 
1059 static title *find_title(const object *book, int msgtype) {
1060  title *t;
1061  titlelist *tl;
1062  size_t length;
1063  int index;
1064 
1065  if (msgtype < 0)
1066  return (title *)NULL;
1067 
1068  tl = get_titlelist(msgtype);
1069  if (!tl)
1070  return (title *)NULL;
1071 
1072  length = strlen(book->msg);
1073  index = strtoint(book->msg);
1074  for (t = tl->first_book; t; t = t->next)
1075  if (t->size == length && t->msg_index == index) {
1076 #ifdef ARCHIVE_DEBUG
1077  LOG(llevDebug, "Found title match (list %d): %s %s (%d)\n", msgtype, t->name, t->authour, t->msg_index);
1078 #endif
1079  return t;
1080  }
1081 
1082  return (title *)NULL;
1083 }
1084 
1098 static void new_text_name(object *book, int msgtype) {
1099  const char *name;
1100 
1101  if (book->type != BOOK)
1102  return;
1103 
1104  switch (msgtype) {
1105  case MSGTYPE_MONSTER:
1107  break;
1108 
1109  case MSGTYPE_ARTIFACT:
1111  break;
1112 
1113  case MSGTYPE_SPELLPATH:
1115  break;
1116 
1117  case MSGTYPE_ALCHEMY:
1119  break;
1120 
1121  case MSGTYPE_GODS:
1123  break;
1124 
1125  case MSGTYPE_MSGFILE:
1126  default:
1127  if (book->weight > 2000) { /* based on weight */
1129  } else {
1131  }
1132  break;
1133  }
1134  free_string(book->name);
1135  book->name = add_string(name);
1136 }
1137 
1147 static void add_author(object *op, int msgtype) {
1148  char title[MAX_BUF];
1149  const char *name;
1150 
1151  if (msgtype < 0 || strlen(op->msg) < 5)
1152  return;
1153 
1154  switch (msgtype) {
1155  case MSGTYPE_MONSTER:
1157  break;
1158 
1159  case MSGTYPE_ARTIFACT:
1161  break;
1162 
1163  case MSGTYPE_SPELLPATH:
1165  break;
1166 
1167  case MSGTYPE_ALCHEMY:
1169  break;
1170 
1171  case MSGTYPE_GODS:
1173  break;
1174 
1175  case MSGTYPE_MSGFILE:
1176  default:
1178  }
1179 
1180  snprintf(title, sizeof(title), "of %s", name);
1181  op->title = add_string(title);
1182 }
1183 
1195 static int unique_book(const object *book, int msgtype) {
1196  title *test;
1197 
1198  if (!booklist)
1199  return 1; /* No archival entries! Must be unique! */
1200 
1201  /* Go through the booklist. If the author and name match, not unique so
1202  * return 0.
1203  */
1204  for (test = get_titlelist(msgtype)->first_book; test; test = test->next) {
1205  if (!strcmp(test->name, book->name) && !strcmp(book->title, test->authour))
1206  return 0;
1207  }
1208  return 1;
1209 }
1210 
1219 static void add_book_to_list(const object *book, int msgtype) {
1220  titlelist *tl = get_titlelist(msgtype);
1221  title *t;
1222 
1223  if (!tl) {
1224  LOG(llevError, "add_book_to_list can't get booklist!\n");
1225  return;
1226  }
1227 
1228  t = get_empty_book();
1229  t->name = add_string(book->name);
1230  t->authour = add_string(book->title);
1231  t->size = strlen(book->msg);
1232  t->msg_index = strtoint(book->msg);
1233  t->archname = add_string(book->arch->name);
1234  t->level = book->level;
1235 
1236  t->next = tl->first_book;
1237  tl->first_book = t;
1238  tl->number++;
1239 
1240  /* We have stuff we need to write now */
1242 
1243 #ifdef ARCHIVE_DEBUG
1244  LOG(llevDebug, "Archiving new title: %s %s (%d)\n", book->name, book->title, msgtype);
1245 #endif
1246 }
1247 
1261 static void change_book(object *book, int msgtype) {
1262  titlelist *tl;
1263  title *t;
1264  int tries;
1265 
1266  if (book->type != BOOK) {
1267  LOG(llevError, "change_book_name() called w/ illegal obj type.\n");
1268  return;
1269  }
1270 
1271  tl = get_titlelist(msgtype);
1272  t = NULL;
1273  tries = 0;
1274 
1275  /* look to see if our msg already been archived. If so, alter
1276  * the book to match the archival text. If we fail to match,
1277  * then we archive the new title/name/msg combo if there is
1278  * room on the titlelist.
1279  */
1280 
1281  if (strlen(book->msg) > 5 && (t = find_title(book, msgtype))) {
1282  object *tmpbook;
1283  sstring marker = object_get_value(book, "knowledge_marker");
1284 
1285  /* alter book properties */
1286  tmpbook = create_archetype(t->archname);
1287  if (marker != NULL)
1288  /* need to copy the knowledge_marker */
1289  object_set_value(tmpbook, "knowledge_marker", marker, 1);
1290  object_set_msg(tmpbook, book->msg);
1291  object_copy(tmpbook, book);
1292  object_free_drop_inventory(tmpbook);
1293 
1294  book->title = add_string(t->authour);
1295  free_string(book->name);
1296  book->name = add_string(t->name);
1297  book->level = t->level;
1298  } else { /* Don't have any default title, so lets make up a new one */
1299  int numb, maxnames = max_titles[msgtype];
1300  const char *old_title;
1301  const char *old_name;
1302 
1303  old_title = book->title ? add_string(book->title) : NULL;
1304  old_name = add_string(book->name);
1305 
1306  /* some pre-generated books have title already set (from
1307  * maps), also don't bother looking for unique title if
1308  * we already used up all the available names! */
1309 
1310  if (!tl) {
1311  LOG(llevError, "change_book_name(): can't find title list\n");
1312  numb = 0;
1313  } else
1314  numb = tl->number;
1315 
1316  if (numb == maxnames) {
1317 #ifdef ARCHIVE_DEBUG
1318  LOG(llevDebug, "titles for list %d full (%d possible).\n", msgtype, maxnames);
1319 #endif
1320  if (old_title != NULL)
1321  free_string(old_title);
1322  free_string(old_name);
1323  return;
1324  }
1325  /* shouldnt change map-maker books */
1326  if (!book->title)
1327  do {
1328  /* random book name */
1329  new_text_name(book, msgtype);
1330  add_author(book, msgtype); /* random author */
1331  tries++;
1332  } while (!unique_book(book, msgtype) && tries < MAX_TITLE_CHECK);
1333 
1334  /* Now deal with 2 cases.
1335  * 1) If no space for a new title exists lets just restore
1336  * the old book properties. Remember, if the book had
1337  * matchd an older entry on the titlelist, we shouldnt
1338  * have called this routine in the first place!
1339  * 2) If we got a unique title, we need to add it to
1340  * the list.
1341  */
1342 
1343  if (tries == MAX_TITLE_CHECK) {
1344 #ifdef ARCHIVE_DEBUG
1345  LOG(llevDebug, "Failed to obtain unique title for %s %s (names:%d/%d)\n", book->name, book->title, numb, maxnames);
1346 #endif
1347  /* restore old book properties here */
1348  free_string(book->name);
1349  free_string(book->title);
1350  book->title = old_title != NULL ? add_string(old_title) : NULL;
1351 
1352  if (RANDOM()%4) {
1353  /* Lets give the book a description to individualize it some */
1354  char new_name[MAX_BUF];
1355 
1356  snprintf(new_name, MAX_BUF, "%s %s", book_descrpt[RANDOM()%arraysize(book_descrpt)], old_name);
1357  book->name = add_string(new_name);
1358  } else {
1359  book->name = add_string(old_name);
1360  }
1361  } else if (book->title && strlen(book->msg) > 5) { /* archive if long msg texts */
1362  add_book_to_list(book, msgtype);
1363  }
1364 
1365  if (old_title != NULL)
1366  free_string(old_title);
1367  free_string(old_name);
1368  }
1369 }
1370 
1371 /*****************************************************************************
1372  *
1373  * This is the start of the area that generates the actual contents
1374  * of the book.
1375  *
1376  *****************************************************************************/
1377 
1378 /*****************************************************************************
1379  * Monster msg generation code.
1380  ****************************************************************************/
1381 
1393 object *get_random_mon(int level) {
1394  objectlink *mon;
1395  int i, monnr;
1396 
1397  /* safety check. Problem w/ init_mon_info list? */
1398  if (!nrofmon || !first_mon_info)
1399  return (object *)NULL;
1400 
1401  if (!level) {
1402  /* lets get a random monster from the mon_info linked list */
1403  monnr = RANDOM()%nrofmon;
1404 
1405  for (mon = first_mon_info, i = 0; mon; mon = mon->next, i++)
1406  if (i == monnr)
1407  break;
1408 
1409  if (!mon) {
1410  LOG(llevError, "get_random_mon: Didn't find a monster when we should have\n");
1411  return NULL;
1412  }
1413  return mon->ob;
1414  }
1415 
1416  /* Case where we are searching by level. Redone 971225 to be clearer
1417  * and more random. Before, it looks like it took a random monster from
1418  * the list, and then returned the first monster after that which was
1419  * appropriate level. This wasn't very random because if you had a
1420  * bunch of low level monsters and then a high level one, if the random
1421  * determine took one of the low level ones, it would just forward to the
1422  * high level one and return that. Thus, monsters that immediately followed
1423  * a bunch of low level monsters would be more heavily returned. It also
1424  * means some of the dragons would be poorly represented, since they
1425  * are a group of high level monsters all around each other.
1426  */
1427 
1428  /* First count number of monsters meeting level criteria */
1429  for (mon = first_mon_info, i = 0; mon; mon = mon->next)
1430  if (mon->ob->level >= level)
1431  i++;
1432 
1433  if (i == 0) {
1434  LOG(llevError, "get_random_mon() couldn't return monster for level %d\n", level);
1435  return NULL;
1436  }
1437 
1438  monnr = RANDOM()%i;
1439  for (mon = first_mon_info; mon; mon = mon->next)
1440  if (mon->ob->level >= level && monnr-- == 0)
1441  return mon->ob;
1442 
1443  LOG(llevError, "get_random_mon(): didn't find a monster when we should have\n");
1444  return NULL;
1445 }
1446 
1456 static StringBuffer *mon_desc(const object *mon) {
1457  StringBuffer *desc = stringbuffer_new();
1458  stringbuffer_append_printf(desc, "\n---\n *** %s ***\n", mon->name);
1459  describe_item(mon, NULL, 0, desc);
1460  return desc;
1461 }
1462 
1474 static object *get_next_mon(const object *tmp) {
1475  objectlink *mon;
1476 
1477  for (mon = first_mon_info; mon; mon = mon->next)
1478  if (mon->ob == tmp)
1479  break;
1480 
1481  /* didn't find a match */
1482  if (!mon)
1483  return NULL;
1484  if (mon->next)
1485  return mon->next->ob;
1486  else
1487  return first_mon_info->ob;
1488 }
1489 
1502 static StringBuffer *mon_info_msg(int level, size_t booksize, object *book) {
1503  object *tmp;
1504  StringBuffer *marker = stringbuffer_new(), *desc = stringbuffer_new(), *mon = NULL;
1505  int added = 0;
1506  sstring final;
1507 
1508  /*preamble */
1509  stringbuffer_append_string(desc, "This beastiary contains:");
1510  stringbuffer_append_string(marker, "monster");
1511 
1512  /* lets print info on as many monsters as will fit in our
1513  * document.
1514  * 8-96 Had to change this a bit, otherwise there would
1515  * have been an impossibly large number of combinations
1516  * of text! (and flood out the available number of titles
1517  * in the archive in a snap!) -b.t.
1518  */
1519  for (tmp = get_random_mon(level*3); tmp; tmp = get_next_mon(tmp)) {
1520  /* monster description */
1521  mon = mon_desc(tmp);
1522 
1523  if (stringbuffer_length(desc) + stringbuffer_length(mon) >= booksize)
1524  break;
1525  added++;
1526  stringbuffer_append_printf(marker, ":%s", tmp->arch->name);
1529  mon = NULL;
1530  }
1531 
1532  if (mon != NULL) {
1534  }
1535 
1536  final = stringbuffer_finish_shared(marker);
1537  if (added)
1538  object_set_value(book, "knowledge_marker", final, 1);
1539  free_string(final);
1540 
1541  return desc;
1542 }
1543 
1544 /*****************************************************************************
1545  * Artifact msg generation code.
1546  ****************************************************************************/
1547 
1557 static StringBuffer *artifact_describe(const artifact *art, const artifactlist *al, int message, int art_name, int separator) {
1558  object *tmp;
1559  int chance;
1560  StringBuffer *desc = stringbuffer_new(), *sbuf;
1561 
1562  if (separator)
1563  stringbuffer_append_string(desc, "---\n");
1564 
1565  /* Name */
1566  if (art->allowed != NULL) {
1567  archetype *arch;
1568  linked_char *temp = art->allowed;
1569  int inv = 0, w;
1570 
1571  assert(art->allowed_size > 0);
1572  if (art->allowed_size > 1)
1573  w = 1 + RANDOM() % art->allowed_size;
1574  else
1575  w = 1;
1576 
1577  while (w > 1) {
1578  assert(temp);
1579  temp = temp->next;
1580  w--;
1581  }
1582 
1583  if (temp->name[0] == '!')
1584  inv = 1;
1585 
1587  arch = try_find_archetype(temp->name + inv);
1588  if (!arch)
1589  arch = find_archetype_by_object_name(temp->name + inv);
1590 
1591  if (!arch)
1592  LOG(llevError, "artifact_msg: missing archetype %s for artifact %s (type %d)\n", temp->name + inv, art->item->name, art->item->type);
1593  else {
1594  if (inv)
1595  stringbuffer_append_printf(desc, " A %s (excepted %s) of %s", art_name_array[art_name].name, arch->clone.name_pl, art->item->name);
1596  else
1597  stringbuffer_append_printf(desc, " A %s of %s", arch->clone.name, art->item->name);
1598  }
1599  } else { /* default name is used */
1600  /* use the base 'generic' name for our artifact */
1601  stringbuffer_append_printf(desc, " The %s of %s", art_name_array[art_name].name, art->item->name);
1602  }
1603 
1604  /* chance of finding */
1605  stringbuffer_append_string(desc, " is ");
1606  chance = 100*((float)art->chance/al->total_chance);
1607  if (chance >= 20)
1608  stringbuffer_append_string(desc, "an uncommon");
1609  else if (chance >= 10)
1610  stringbuffer_append_string(desc, "an unusual");
1611  else if (chance >= 5)
1612  stringbuffer_append_string(desc, "a rare");
1613  else
1614  stringbuffer_append_string(desc, "a very rare");
1615 
1616  /* value of artifact */
1617  stringbuffer_append_printf(desc, " item with a value that is %d times normal.\n", art->item->value);
1618 
1619  /* include the message about the artifact, if exists, and book
1620  * level is kinda high */
1621  if (message && !(strlen(art->item->msg) > BOOK_BUF))
1622  stringbuffer_append_string(desc, art->item->msg);
1623 
1624  /* properties of the artifact */
1625  tmp = object_new();
1626  add_abilities(tmp, art->item);
1627  tmp->type = al->type;
1628  SET_FLAG(tmp, FLAG_IDENTIFIED);
1629  sbuf = describe_item(tmp, NULL, 0, NULL);
1630  if (stringbuffer_length(sbuf) > 1) {
1631  stringbuffer_append_string(desc, " Properties of this artifact include:\n ");
1633  stringbuffer_append_string(desc, "\n");
1634  }
1635  free(stringbuffer_finish(sbuf));
1637 
1638  return desc;
1639 }
1640 
1652 static StringBuffer *artifact_msg(unsigned int level, size_t booksize) {
1653  const artifactlist *al;
1654  const artifact *art;
1655  int i, type, index;
1656  int book_entries = level > 5 ? RANDOM()%3+RANDOM()%3+2 : RANDOM()%level+1;
1657  StringBuffer *desc, *message = stringbuffer_new();
1658 
1659  /* values greater than 5 create msg buffers that are too big! */
1660  if (book_entries > 5)
1661  book_entries = 5;
1662 
1663  /* lets determine what kind of artifact type randomly.
1664  * Right now legal artifacts only come from those listed
1665  * in art_name_array. Also, we check to be sure an artifactlist
1666  * for that type exists!
1667  */
1668  i = 0;
1669  do {
1670  index = RANDOM()%arraysize(art_name_array);
1671  type = art_name_array[index].type;
1672  al = find_artifactlist(type);
1673  i++;
1674  } while (al == NULL && i < 10);
1675 
1676  if (i == 10) { /* Unable to find a message */
1677  stringbuffer_append_string(message, "None");
1678  return message;
1679  }
1680 
1681  /* There is no reason to start on the artifact list at the beginning. Lets
1682  * take our starting position randomly... */
1683  art = al->items;
1684  for (i = RANDOM()%level+RANDOM()%2+1; i > 0; i--) {
1685  if (art == NULL)
1686  art = al->items; /* hmm, out of stuff, loop back around */
1687  art = art->next;
1688  }
1689 
1690  /* Ok, lets print out the contents */
1691  stringbuffer_append_printf(message, "Herein %s detailed %s...\n", book_entries > 1 ? "are" : "is", book_entries > 1 ? "some artifacts" : "an artifact");
1692 
1693  i = 0;
1694  /* artifact msg attributes loop. Lets keep adding entries to the 'book'
1695  * as long as we have space up to the allowed max # (book_entires)
1696  */
1697  while (book_entries > 0) {
1698  int with_message;
1699  if (art == NULL)
1700  art = al->items;
1701  with_message = (art->item->msg && RANDOM()%4+1 < level) ? 1 : 0;
1702 
1703  desc = artifact_describe(art, al, with_message, index, i++);
1704 
1705  if (stringbuffer_length(message) + stringbuffer_length(desc) >= booksize) {
1706  stringbuffer_delete(desc);
1707  break;
1708  }
1709 
1710  stringbuffer_append_stringbuffer(message, desc);
1711  stringbuffer_delete(desc);
1712 
1713  art = art->next;
1714  book_entries--;
1715  }
1716 
1717  return message;
1718 }
1719 
1720 /*****************************************************************************
1721  * Spellpath message generation
1722  *****************************************************************************/
1723 
1737 static StringBuffer *spellpath_msg(int level, size_t booksize, StringBuffer *buf) {
1738  int path = RANDOM()%NRSPELLPATHS, prayers = RANDOM()%2;
1739  int did_first_sp = 0;
1740  uint32_t pnum = spellpathdef[path];
1741  archetype *at;
1742 
1743  if (buf == NULL) {
1744  buf = stringbuffer_new();
1745  /* Preamble */
1746  stringbuffer_append_printf(buf, "Herein are detailed the names of %s", prayers ? "prayers" : "incantations");
1747  stringbuffer_append_printf(buf, " belonging to the path of %s:\n ", spellpathnames[path]);
1748  }
1749 
1750  for (at = first_archetype; at != NULL; at = at->next) {
1751  /* Determine if this is an appropriate spell. Must
1752  * be of matching path, must be of appropriate type (prayer
1753  * or not), and must be within the valid level range.
1754  */
1755  if (at->clone.type == SPELL
1756  && at->clone.path_attuned&pnum
1757  && ((at->clone.stats.grace && prayers) || (at->clone.stats.sp && !prayers))
1758  && at->clone.level < level*8) {
1759  if (strlen(at->clone.name) + stringbuffer_length(buf) >= booksize)
1760  break;
1761 
1762  if (did_first_sp)
1763  stringbuffer_append_string(buf, ",\n ");
1764  did_first_sp = 1;
1766  }
1767  }
1768 
1769  /* Geez, no spells were generated. */
1770  if (!did_first_sp) {
1771  if (RANDOM()%4) { /* usually, lets make a recursive call... */
1772  return spellpath_msg(level, booksize, buf);
1773  }
1774  /* give up, cause knowing no spells exist for path is info too. need the header too. */
1775  stringbuffer_append_string(buf, "- no known spells exist -\n");
1776  }
1777  return buf;
1778 }
1779 
1788 static void make_formula_book(object *book, int level) {
1789  recipelist *fl;
1790  recipe *formula;
1791  int chance, count = 0;
1792  const char *op_name;
1793  archetype *at;
1794  StringBuffer *text, *title;
1795  char *final, km[MAX_BUF];
1796 
1797  /* the higher the book level, the more complex (ie number of
1798  * ingredients) the formula can be.
1799  */
1800  fl = get_formulalist((RANDOM()%level)/3+1);
1801  if (!fl)
1802  fl = get_formulalist(1); /* safety */
1803 
1804  if (fl->total_chance == 0) {
1805  object_set_msg(book, " <indecipherable text>\n");
1807  add_author(book, MSGTYPE_ALCHEMY);
1808  return;
1809  }
1810 
1811  /* get a random formula, weighted by its bookchance */
1812  chance = RANDOM()%fl->total_chance;
1813  for (formula = fl->items; formula != NULL; formula = formula->next) {
1814  chance -= formula->chance;
1815  if (chance <= 0 && formula->chance != 0 && !formula->is_combination)
1816  break;
1817  }
1818 
1819  if (!formula || formula->arch_names <= 0) {
1820  object_set_msg(book, " <indecipherable text>\n");
1822  add_author(book, MSGTYPE_ALCHEMY);
1823  return;
1824  }
1825 
1826  /* looks like a formula was found. Base the amount
1827  * of information on the booklevel and the spellevel
1828  * of the formula. */
1829 
1830  op_name = formula->arch_name[RANDOM()%formula->arch_names];
1831  at = find_archetype(op_name);
1832  if (at == (archetype *)NULL) {
1833  LOG(llevError, "formula_msg() can't find arch %s for formula.\n", op_name);
1834  object_set_msg(book, " <indecipherable text>\n");
1836  add_author(book, MSGTYPE_ALCHEMY);
1837  return;
1838  }
1839  op_name = at->clone.name;
1840 
1841  text = stringbuffer_new();
1842  title = stringbuffer_new();
1843 
1844  /* preamble */
1845  stringbuffer_append_printf(text, "Herein is described a project using %s:\n", formula->skill ? formula->skill : "an unknown skill");
1846 
1847  /* item name */
1848  if (strcmp(formula->title, "NONE")) {
1849  stringbuffer_append_printf(text, "The %s of %s", op_name, formula->title);
1850  /* This results in things like pile of philo. sulfur.
1851  * while philo. sulfur may look better, without this,
1852  * you get things like 'the wise' because its missing the
1853  * water of section.
1854  */
1855  stringbuffer_append_printf(title, "%s: %s of %s", formula_book_name[RANDOM()%arraysize(formula_book_name)], op_name, formula->title);
1856  } else {
1857  stringbuffer_append_printf(text, "The %s", op_name);
1859  if (at->clone.title) {
1860  stringbuffer_append_printf(text, " %s", at->clone.title);
1861  stringbuffer_append_printf(title, " %s", at->clone.title);
1862  }
1863  }
1864  /* Lets name the book something meaningful ! */
1865  if (book->name)
1866  free_string(book->name);
1867  book->name = stringbuffer_finish_shared(title);
1868  if (book->title) {
1869  free_string(book->title);
1870  book->title = NULL;
1871  }
1872 
1873  /* ingredients to make it */
1874  if (formula->ingred != NULL) {
1875  linked_char *next;
1876  archetype *at;
1877  char name[MAX_BUF];
1878 
1879  at = find_archetype(formula->cauldron);
1880  if (at)
1881  query_name(&at->clone, name, MAX_BUF);
1882  else
1883  snprintf(name, sizeof(name), "an unknown place");
1884 
1885  stringbuffer_append_printf(text, " may be made at %s using the following ingredients:\n", name);
1886 
1887  for (next = formula->ingred; next != NULL; next = next->next) {
1888  count++;
1889  stringbuffer_append_printf(text, "%s\n", next->name);
1890  }
1891  } else {
1892  LOG(llevError, "formula_msg() no ingredient list for object %s of %s\n", op_name, formula->title);
1893  stringbuffer_append_string(text, "\n");
1894  }
1895 
1896  final = stringbuffer_finish(text);
1897  object_set_msg(book, final);
1898  free(final);
1899 
1902  snprintf(km, sizeof(km), "alchemy:%d:%d:%s", count, formula->index, formula->title);
1903  object_set_value(book, "knowledge_marker", km, 1);
1904 }
1905 
1916 static StringBuffer *msgfile_msg(object *book, size_t booksize) {
1917  int weight;
1918  GeneralMessage *msg = NULL;
1919  StringBuffer *ret = stringbuffer_new();
1920 
1921  /* get a random message for the 'book' from linked list */
1922  if (msg_total_chance > 0) {
1923  assert(first_msg != NULL);
1924  msg = first_msg;
1925  weight = (RANDOM() % msg_total_chance);
1926  while (msg) {
1927  weight -= msg->chance;
1928  if (weight < 0)
1929  break;
1930  msg = msg->next;
1931  }
1932  /* if msg is NULL, then something is really wrong in the computation! */
1933  assert(msg != NULL);
1934  }
1935 
1936  if (msg && strlen(msg->message) <= booksize) {
1938  if (msg->identifier != NULL) {
1939  char km[HUGE_BUF];
1942  snprintf(km, sizeof(km), "message:%s", msg->identifier);
1943  object_set_value(book, "knowledge_marker", km, 1);
1944  }
1945  if (msg->quest_code) {
1946  /* add a 'apply' hook to launch the quest */
1947  object *event = object_create_arch(find_archetype("quest_advance_apply"));
1948  FREE_AND_COPY(event->name, msg->quest_code);
1949  object_insert_in_ob(event, book);
1950  }
1951  } else
1952  stringbuffer_append_string(ret, "\n <undecipherable text>");
1953 
1954  return ret;
1955 }
1956 
1971 static StringBuffer *god_info_msg(int level, size_t booksize, object *book) {
1972  int what = 0;
1973  const object *god = pntr_to_god_obj(get_rand_god());
1974  StringBuffer *desc = NULL;
1975 
1976  if (!god)
1977  return NULL; /* oops, problems... */
1978 
1979  if (booksize > BOOK_BUF) {
1980  LOG(llevError, "common/readable.c:god_info_msg() - passed in booksize (%lu) is larger than book buffer (%d)\n", (unsigned long)booksize, BOOK_BUF);
1981  booksize = BOOK_BUF;
1982  }
1983 
1984  if (level >= 2 && RANDOM()%2) {
1985  what |= GOD_ENEMY;
1986  }
1987  if (level >= 3 && RANDOM()%2) {
1988  what |= GOD_HOLYWORD;
1989  }
1990  if (level >= 4 && RANDOM()%2) {
1991  what |= GOD_RESISTANCES;
1992  }
1993  if (level >= 5 && RANDOM()%2) {
1994  what |= GOD_SACRED;
1995  }
1996  if (level >= 6 && RANDOM()%2) {
1997  what |= GOD_BLESSED;
1998  }
1999  if (level >= 8 && RANDOM()%2) {
2000  what |= GOD_IMMUNITIES;
2001  }
2002  if (level >= 12 && RANDOM()%2) {
2003  what |= GOD_PATHS;
2004  }
2005 
2006  desc = stringbuffer_new();
2007  what = describe_god(god, what, desc, booksize);
2008 
2009  /* check to be sure new buffer size dont exceed either
2010  * the maximum buffer size, or the 'natural' size of the
2011  * book... */
2012  if (stringbuffer_length(desc) > 1 && stringbuffer_length(desc) <= booksize) {
2013  char buf[BOOK_BUF];
2014  snprintf(buf, sizeof(buf), "god:%s:%d", god->name, what);
2015  object_set_value(book, "knowledge_marker", buf, 1);
2016  return desc;
2017  }
2018 
2019  stringbuffer_delete(desc);
2020  return NULL;
2021 }
2022 
2041 void tailor_readable_ob(object *book, int msg_type) {
2042  int level = book->level ? RANDOM()%book->level+1 : 1;
2043  size_t book_buf_size;
2044  StringBuffer *message = NULL;
2045 
2046  /* safety */
2047  if (book->type != BOOK)
2048  return;
2049 
2050  if (level <= 0)
2051  return; /* if no level no point in doing any more... */
2052 
2053  /* Max text length this book can have. */
2054  book_buf_size = BOOKSIZE(book);
2055  book_buf_size -= strlen("\n"); /* Keep enough for final \n. */
2056  assert(book_buf_size < BOOK_BUF);
2057 
2058  /* &&& The message switch &&& */
2059  /* Below all of the possible types of messages in the "book"s.
2060  */
2061  /*
2062  * IF you add a new type of book msg, you will have to do several things.
2063  * 1) make sure there is an entry in the msg switch below!
2064  * 2) make sure there is an entry in max_titles[] array.
2065  * 3) make sure there are entries for your case in new_text_title()
2066  * and add_authour().
2067  * 4) you may want separate authour/book name arrays in read.h
2068  */
2069 
2070  if (msg_type >= (int)arraysize(max_titles))
2071  msg_type = 0;
2072 
2073  msg_type = msg_type > 0 ? msg_type : RANDOM()%6;
2074  switch (msg_type) {
2075  case MSGTYPE_MONSTER:
2076  message = mon_info_msg(level, book_buf_size, book);
2077  break;
2078 
2079  case MSGTYPE_ARTIFACT:
2080  message = artifact_msg(level, book_buf_size);
2081  break;
2082 
2083  case MSGTYPE_SPELLPATH: /* grouping incantations/prayers by path */
2084  message = spellpath_msg(level, book_buf_size, NULL);
2085  break;
2086 
2087  case MSGTYPE_ALCHEMY: /* describe an alchemy formula */
2088  make_formula_book(book, level);
2089  /* make_formula_book already gives title */
2090  return;
2091  break;
2092 
2093  case MSGTYPE_GODS: /* bits of information about a god */
2094  message = god_info_msg(level, book_buf_size, book);
2095  break;
2096 
2097  case MSGTYPE_LIB: /* use info list in lib/ */
2098  default:
2099  message = msgfile_msg(book, book_buf_size);
2100  break;
2101  }
2102 
2103  if (message != NULL) {
2104  char *final;
2105  stringbuffer_append_string(message, "\n");
2106  final = stringbuffer_finish(message);
2107  object_set_msg(book, final);
2108  free(final);
2109  /* lets give the "book" a new name, which may be a compound word */
2110  change_book(book, msg_type);
2111  }
2112 }
2113 
2114 /*****************************************************************************
2115  *
2116  * Cleanup routine for readable stuff.
2117  *
2118  *****************************************************************************/
2119 
2123 void free_all_readable(void) {
2124  titlelist *tlist, *tnext;
2125  title *title1, *titlenext;
2126  GeneralMessage *lmsg, *nextmsg;
2127  objectlink *monlink, *nextmon;
2128 
2129  LOG(llevDebug, "freeing all book information\n");
2130 
2131  for (tlist = booklist; tlist != NULL; tlist = tnext) {
2132  tnext = tlist->next;
2133  for (title1 = tlist->first_book; title1; title1 = titlenext) {
2134  titlenext = title1->next;
2135  if (title1->name)
2136  free_string(title1->name);
2137  if (title1->authour)
2138  free_string(title1->authour);
2139  if (title1->archname)
2140  free_string(title1->archname);
2141  free(title1);
2142  }
2143  free(tlist);
2144  }
2145  for (lmsg = first_msg; lmsg; lmsg = nextmsg) {
2146  nextmsg = lmsg->next;
2147  if (lmsg->identifier)
2148  free_string(lmsg->identifier);
2149  if (lmsg->title)
2150  free_string(lmsg->title);
2151  if (lmsg->message)
2152  free_string(lmsg->message);
2153  if (lmsg->quest_code)
2154  free_string(lmsg->quest_code);
2155  free(lmsg);
2156  }
2157  for (monlink = first_mon_info; monlink; monlink = nextmon) {
2158  nextmon = monlink->next;
2159  free(monlink);
2160  }
2161 }
2162 
2163 /*****************************************************************************
2164  *
2165  * Writeback routine for updating the bookarchive.
2166  *
2167  ****************************************************************************/
2168 
2173  FILE *fp;
2174  OutputFile of;
2175  int index;
2176  char fname[MAX_BUF];
2177  title *book;
2178  titlelist *bl;
2179 
2180  /* If nothing changed, don't write anything */
2182  return;
2183 
2184  snprintf(fname, sizeof(fname), "%s/bookarch", settings.localdir);
2185  LOG(llevDebug, "Updating book archive: %s...\n", fname);
2186 
2187  fp = of_open(&of, fname);
2188  if (fp == NULL)
2189  return;
2190 
2191  for (bl = get_titlelist(0), index = 0; bl; bl = bl->next, index++) {
2192  for (book = bl->first_book; book; book = book->next)
2193  if (book && book->authour) {
2194  fprintf(fp, "title %s\n", book->name);
2195  fprintf(fp, "authour %s\n", book->authour);
2196  fprintf(fp, "arch %s\n", book->archname);
2197  fprintf(fp, "level %d\n", book->level);
2198  fprintf(fp, "type %d\n", index);
2199  /* C89 doesn't have %zu... */
2200  fprintf(fp, "size %lu\n", (unsigned long)book->size);
2201  fprintf(fp, "index %d\n", book->msg_index);
2202  fprintf(fp, "end\n");
2203  }
2204  }
2205  if (!of_close(&of))
2206  return;
2207 
2208  if (chmod(fname, SAVE_MODE) != 0) {
2209  LOG(llevError, "Could not set permissions on '%s'\n", fname);
2210  }
2211 
2213 }
2214 
2223  uint8_t subtype = readable->subtype;
2224 
2225  if (subtype > last_readable_subtype)
2226  return &readable_message_types[0];
2227  return &readable_message_types[subtype];
2228 }
2229 
2235 const GeneralMessage *get_message_from_identifier(const char *identifier) {
2236  GeneralMessage *msg = first_msg;
2237  while (msg && ((msg->identifier == 0) || (strcmp(msg->identifier, identifier) != 0)))
2238  msg = msg->next;
2239 
2240  return msg;
2241 }
2242 
2249  return message->title;
2250 }
2251 
2258  return message->message;
2259 }
2260 
2266 unsigned int get_message_face(const GeneralMessage *message) {
2267  return message->face;
2268 }
#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:402
archetype * find_archetype(const char *name)
Definition: arch.c:692
#define GOD_BLESSED
Definition: god.h:28
Definition: object.h:185
static const uint32_t spellpathdef[NRSPELLPATHS]
Definition: readable.c:174
#define MSG_TYPE_PAPER_LETTER_NEW_1
Definition: newclient.h:433
#define PATH_MISSILE
Definition: spells.h:17
#define MSGTYPE_ARTIFACT
Definition: readable.c:81
Definition: object.h:442
#define MSG_TYPE_CARD_ELEGANT_1
Definition: newclient.h:417
static void init_msgfile(void)
Definition: readable.c:734
#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:2248
static void make_formula_book(object *book, int level)
Definition: readable.c:1788
const artifactlist * find_artifactlist(int type)
Definition: artifact.c:647
static titlelist * get_titlelist(int i)
Definition: readable.c:625
static void add_book_to_list(const object *book, int msgtype)
Definition: readable.c:1219
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:109
#define MSG_TYPE_CARD_MONEY_1
Definition: newclient.h:423
object * get_random_mon(int level)
Definition: readable.c:1393
#define MSG_TYPE_CARD_MONEY_3
Definition: newclient.h:425
Definition: object.h:204
Definition: object.h:112
#define PATH_LIGHT
Definition: spells.h:32
Definition: object.h:117
sstring title
Definition: readable.c:138
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:74
static const arttypename art_name_array[]
Definition: readable.c:232
#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
unsigned int get_message_face(const GeneralMessage *message)
Definition: readable.c:2266
#define MSGTYPE_MONSTER
Definition: readable.c:79
const char * object_get_value(const object *op, const char *const key)
Definition: object.c:4249
#define MSG_TYPE_MONUMENT_WALL_2
Definition: newclient.h:461
#define MSG_TYPE_PAPER_NOTE_2
Definition: newclient.h:429
object clone
Definition: object.h:470
linked_char * ingred
Definition: recipe.h:22
static const int last_readable_subtype
Definition: readable.c:553
#define MSG_TYPE_SIGN_DIR_RIGHT
Definition: newclient.h:446
static const char *const path_book_name[]
Definition: readable.c:198
Definition: object.h:119
static void init_book_archive(void)
Definition: readable.c:859
#define MSG_TYPE_PAPER_ENVELOPE_1
Definition: newclient.h:435
Definition: object.h:136
#define MSG_TYPE_BOOK_SPELL_SORCERER
Definition: newclient.h:410
#define MSG_TYPE_PAPER_SCROLL_NEW_1
Definition: newclient.h:439
static int msg_total_chance
Definition: readable.c:169
#define PATH_RESTORE
Definition: spells.h:21
#define GOD_HOLYWORD
Definition: god.h:25
int msg_index
Definition: readable.c:112
uint8_t subtype
Definition: object.h:339
Definition: object.h:109
#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:107
#define MSG_TYPE_MONUMENT_STONE_1
Definition: newclient.h:451
Definition: object.h:223
#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:1059
Definition: object.h:139
#define MSG_TYPE_BOOK_SPELL_PRAYER
Definition: newclient.h:408
uint32_t path_attuned
Definition: object.h:343
#define MSGTYPE_ALCHEMY
Definition: readable.c:85
int16_t sp
Definition: living.h:41
static object * get_next_mon(const object *tmp)
Definition: readable.c:1474
sstring identifier
Definition: readable.c:137
#define MSG_TYPE_BOOK_SPELL_PYRO
Definition: newclient.h:409
Definition: object.h:212
#define MSG_TYPE_BOOK_ELEGANT_1
Definition: newclient.h:403
tag_t id
Definition: object.h:445
Definition: object.h:465
#define MSG_TYPE_PAPER_NOTE_3
Definition: newclient.h:430
const char *const spellpathnames[NRSPELLPATHS]
Definition: init.c:122
#define MSG_TYPE_CARD_STRANGE_1
Definition: newclient.h:420
struct titlestruct title
object * ob
Definition: object.h:443
unsigned int face
Definition: readable.c:141
#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:4378
int chance
Definition: recipe.h:14
void object_free_drop_inventory(object *ob)
Definition: object.c:1368
const char * title
Definition: object.h:317
#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:281
int nstrtok(const char *buf1, const char *buf2)
Definition: readable.c:656
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:113
Definition: object.h:118
const char * name
Definition: readable.c:127
struct titleliststruct titlelist
uint16_t total_chance
Definition: artifact.h:28
Definition: object.h:114
#define MSG_TYPE_CARD_SIMPLE_3
Definition: newclient.h:416
object * object_new(void)
Definition: object.c:1037
sstring title
Definition: recipe.h:11
const char * name_pl
Definition: object.h:315
object * create_archetype(const char *name)
Definition: arch.c:617
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:2690
int total_chance
Definition: recipe.h:38
static StringBuffer * artifact_msg(unsigned int level, size_t booksize)
Definition: readable.c:1652
int32_t weight
Definition: object.h:365
#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:1502
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:1557
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
GeneralMessage * next
Definition: readable.c:142
int strtoint(const char *buf)
Definition: recipe.c:622
godlink * get_rand_god(void)
Definition: holy.c:101
int of_close(OutputFile *of)
Definition: output_file.c:61
static int need_to_write_bookarchive
Definition: readable.c:157
#define PATH_DEATH
Definition: spells.h:31
const char * name
Definition: object.h:311
#define FLAG_CHANGING
Definition: define.h:263
linked_char * allowed
Definition: artifact.h:19
struct titleliststruct * next
Definition: readable.c:122
unsigned int level
Definition: readable.c:110
#define MSG_TYPE_CARD_STRANGE_3
Definition: newclient.h:422
archetype * find_archetype_by_object_name(const char *name)
Definition: arch.c:57
size_t stringbuffer_length(StringBuffer *sb)
Definition: stringbuffer.c:154
void tailor_readable_ob(object *book, int msg_type)
Definition: readable.c:2041
#define PATH_PROT
Definition: spells.h:13
const object * pntr_to_god_obj(godlink *godlnk)
Definition: holy.c:123
static const char *const book_author[]
Definition: readable.c:415
#define arraysize(arrayname)
Definition: readable.c:96
int describe_god(const object *god, int what, StringBuffer *buf, size_t maxlen)
Definition: holy.c:137
static const char *const gods_author[]
Definition: readable.c:332
archetype * try_find_archetype(const char *name)
Definition: arch.c:663
#define PATH_SELF
Definition: spells.h:18
static int nrofmon
Definition: readable.c:156
static titlelist * booklist
Definition: readable.c:151
sstring skill
Definition: recipe.h:26
static void add_book(title *book, int type, const char *fname, int lineno)
Definition: readable.c:977
Definition: object.h:214
#define QUERY_FLAG(xyz, p)
Definition: define.h:225
void init_readable(void)
Definition: readable.c:1027
#define MSG_TYPE_BOOK_SPELL_SUMMONER
Definition: newclient.h:411
#define MSG_TYPE_SIGN_BASIC
Definition: newclient.h:444
static void add_author(object *op, int msgtype)
Definition: readable.c:1147
int index
Definition: recipe.h:18
sstring quest_code
Definition: readable.c:140
#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:1971
object * object_create_arch(archetype *at)
Definition: arch.c:733
static GeneralMessage * first_msg
Definition: readable.c:164
#define MSG_TYPE_CARD
Definition: newclient.h:371
#define MSGTYPE_SPELLPATH
Definition: readable.c:83
static const char *const mon_author[]
Definition: readable.c:298
#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:1916
#define PATH_CREATE
Definition: spells.h:24
#define BOOKSIZE(xyz)
Definition: book.h:31
#define MSG_TYPE_PAPER_LETTER_NEW_2
Definition: newclient.h:434
static int unique_book(const object *book, int msgtype)
Definition: readable.c:1195
static const char *const gods_book_name[]
Definition: readable.c:317
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
const char * datadir
Definition: global.h:242
#define MSG_TYPE_BOOK_CLASP_1
Definition: newclient.h:401
Definition: object.h:135
const char * authour
Definition: readable.c:108
#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:981
#define PATH_FROST
Definition: spells.h:15
static const char *const art_author[]
Definition: readable.c:267
static titlelist * get_empty_booklist(void)
Definition: readable.c:580
#define RANDOM()
Definition: define.h:679
static const readable_message_type readable_message_types[]
Definition: readable.c:489
#define FREE_AND_COPY(sv, nv)
Definition: global.h:211
int16_t grace
Definition: living.h:43
#define MSG_TYPE_PAPER_SCROLL_NEW_2
Definition: newclient.h:440
Definition: object.h:143
#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:243
living stats
Definition: object.h:368
#define MSG_TYPE_CARD_MONEY_2
Definition: newclient.h:424
static const char *const formula_author[]
Definition: readable.c:363
struct archt * arch
Definition: object.h:412
int type
Definition: readable.c:128
int allowed_size
Definition: artifact.h:20
struct oblnk * next
Definition: object.h:444
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
uint8_t type
Definition: object.h:338
struct Settings settings
Definition: init.c:40
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:716
struct archt * next
Definition: object.h:467
#define MSGTYPE_MSGFILE
Definition: readable.c:89
#define SAVE_MODE
Definition: config.h:617
const GeneralMessage * get_message_from_identifier(const char *identifier)
Definition: readable.c:2235
#define MSG_TYPE_PAPER_LETTER_OLD_2
Definition: newclient.h:432
void free_all_readable(void)
Definition: readable.c:2123
struct recipestruct * next
Definition: recipe.h:24
static const int max_titles[6]
Definition: readable.c:556
static const char *const path_author[]
Definition: readable.c:208
void add_abilities(object *op, const object *change)
Definition: artifact.c:284
const char * msg
Definition: object.h:322
void write_book_archive(void)
Definition: readable.c:2172
sstring get_message_body(const GeneralMessage *message)
Definition: readable.c:2257
static const char *const light_book_name[]
Definition: readable.c:385
static StringBuffer * mon_desc(const object *mon)
Definition: readable.c:1456
#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:686
static objectlink * first_mon_info
Definition: readable.c:154
sstring add_string(const char *str)
Definition: shstr.c:124
struct titlestruct * first_book
Definition: readable.c:121
#define MSG_TYPE_BOOK_SPELL_EVOKER
Definition: newclient.h:407
void stringbuffer_append_printf(StringBuffer *sb, const char *format,...)
Definition: stringbuffer.c:104
#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:1261
#define FLAG_MONSTER
Definition: define.h:245
void object_copy(const object *src_ob, object *dest_ob)
Definition: object.c:838
static void init_mon_info(void)
Definition: readable.c:995
const readable_message_type * get_readable_message_type(object *readable)
Definition: readable.c:2222
Definition: object.h:213
#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:77
#define MSG_TYPE_BOOK
Definition: newclient.h:370
void object_set_msg(object *op, const char *msg)
Definition: object.c:4698
static const char *const formula_book_name[]
Definition: readable.c:350
void query_name(const object *op, char *buf, size_t size)
Definition: item.c:626
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:462
#define MSGTYPE_GODS
Definition: readable.c:87
static void new_text_name(object *book, int msgtype)
Definition: readable.c:1098
static const char *const art_book_name[]
Definition: readable.c:252
size_t size
Definition: readable.c:111
Definition: object.h:120
int16_t level
Definition: object.h:351
#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:1737
unsigned find_face(const char *name, unsigned error)
Definition: image.c:303
EXTERN archetype * first_archetype
Definition: global.h:122
int32_t value
Definition: object.h:350
sstring cauldron
Definition: recipe.h:27
const char * name
Definition: object.h:466
char * stringbuffer_finish(StringBuffer *sb)
Definition: stringbuffer.c:76
#define PATH_WOUNDING
Definition: spells.h:30
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: readable.c:139
#define PATH_TELE
Definition: spells.h:25
#define MAX_TITLE_CHECK
Definition: readable.c:74
#define PATH_SUMMON
Definition: spells.h:19
static title * get_empty_book(void)
Definition: readable.c:600