Crossfire Server, Trunk  R21670
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  const Face *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  "omnibus",
263  "record",
264  "record book"
265 };
266 
268 static const char *const art_author[] = {
269  "ancient things",
270  "artifacts",
271  "Havlor", /* ancient warrior scribe :) */
272  "items",
273  "lost artifacts",
274  "the ancients",
275  "treasures",
276  "useful things"
277 };
278 
282 static const char *const mon_book_name[] = {
283  "beastuary",
284  "catalog",
285  "compilation",
286  "collection",
287  "encyclopedia",
288  "guide",
289  "handbook",
290  "list",
291  "manual",
292  "notes",
293  "omnibus",
294  "record",
295  "register",
296  "volume"
297 };
298 
300 static const char *const mon_author[] = {
301  "beasts",
302  "creatures",
303  "dezidens",
304  "dwellers",
305  "evil nature",
306  "life",
307  "monsters",
308  "nature",
309  "new life",
310  "residents",
311  "the spawn",
312  "the living",
313  "things"
314 };
315 
319 static const char *const gods_book_name[] = {
320  "devotional",
321  "devout notes",
322  "divine text",
323  "divine work",
324  "holy book",
325  "holy record",
326  "illuminated text",
327  "moral text",
328  "sacred guide",
329  "testament",
330  "transcript"
331 };
332 
334 static const char *const gods_author[] = {
335  "cults",
336  "joy",
337  "lasting curse",
338  "madness",
339  "religions",
340  "the dead",
341  "the gods",
342  "the heirophant",
343  "the poor priest",
344  "the priestess",
345  "pain",
346  "white"
347 };
348 
352 static const char *const formula_book_name[] = {
353  "cookbook",
354  "formulary",
355  "lab book",
356  "lab notes",
357  "recipe book",
358  "experiment record",
359  "work plan",
360  "design notes",
361  "research notes",
362  "crafting manual"
363 };
364 
366 static const char *const formula_author[] = {
367  "Albertus Magnus",
368  "alchemy",
369  "balms",
370  "creation",
371  "dusts",
372  "magical manufacture",
373  "making",
374  "philosophical items",
375  "potions",
376  "powders",
377  "the cauldron",
378  "the lamp black",
379  "transmutation",
380  "waters"
381 };
382 
388 static const char *const light_book_name[] = {
389  "calendar",
390  "datebook",
391  "diary",
392  "essay",
393  "guidebook",
394  "handbook",
395  "ledger",
396  "notes",
397  "notebook",
398  "octavo",
399  "opuscule",
400  "pamphlet",
401  "practicum",
402  "script",
403  "transcript"
404 };
405 
407 static const char *const heavy_book_name[] = {
408  "catalog",
409  "compendium",
410  "dissertation",
411  "guide",
412  "manual",
413  "opus",
414  "tome",
415  "treatise",
416  "volume",
417  "work"
418 };
419 
421 static const char *const book_author[] = {
422  "Abdulah",
423  "Al'hezred",
424  "Alywn",
425  "Arundel",
426  "Arvind",
427  "Aerlingas",
428  "Bacon",
429  "Baliqendii",
430  "Bosworth",
431  "Beathis",
432  "Bertil",
433  "Cauchy",
434  "Chakrabarti",
435  "der Waalis",
436  "Dirk",
437  "Djwimii",
438  "Eisenstaadt",
439  "Fendris",
440  "Frank",
441  "Habbi",
442  "Harlod",
443  "Ichibod",
444  "Janus",
445  "June",
446  "Laplace",
447  "Magnuson",
448  "Nandii",
449  "Nitfeder",
450  "Norris",
451  "Parael",
452  "Penhew",
453  "Sophia",
454  "Skilly",
455  "Tahir",
456  "Thockmorton",
457  "Thomas",
458  "van Helsing",
459  "van Pelt",
460  "Voormis",
461  "Xavier",
462  "Xeno",
463  "Zardoz",
464  "Zagy"
465 };
466 
468 static const char *const book_descrpt[] = {
469  "ancient",
470  "cryptic",
471  "cryptical",
472  "dusty",
473  "hierarchical",
474  "grizzled",
475  "gold-gilt",
476  "great",
477  "lost",
478  "magnificent",
479  "musty",
480  "mythical",
481  "mystical",
482  "rustic",
483  "stained",
484  "silvered",
485  "transcendental",
486  "weathered"
487 };
488 
496  /*subtype 0 */ { 0, 0 },
497  /* book messages subtypes */
498  /*subtype 1 */ { MSG_TYPE_BOOK, MSG_TYPE_BOOK_CLASP_1 },
507  /*subtype 10 */ { MSG_TYPE_BOOK, MSG_TYPE_BOOK_SPELL_SORCERER },
509  /* card messages subtypes*/
518  /*subtype 20 */ { MSG_TYPE_CARD, MSG_TYPE_CARD_STRANGE_3 },
522  /* Paper messages subtypes */
529  /*subtype 30 */ { MSG_TYPE_PAPER, MSG_TYPE_PAPER_LETTER_NEW_2 },
537  /* road signs messages subtypes */
540  /*subtype 40 */ { MSG_TYPE_SIGN, MSG_TYPE_SIGN_DIR_RIGHT },
542  /* stones and monument messages */
556 };
557 
559 static const int last_readable_subtype = arraysize(readable_message_types);
560 
562 static const int max_titles[6] = {
564  arraysize(mon_book_name)*arraysize(mon_author), /* MSGTYPE_MONSTER */
565  arraysize(art_book_name)*arraysize(art_author), /* MSGTYPE_ARTIFACT */
566  arraysize(path_book_name)*arraysize(path_author), /* MSGTYPE_SPELLPATH */
567  arraysize(formula_book_name)*arraysize(formula_author), /* MSGTYPE_ALCHEMY */
568  arraysize(gods_book_name)*arraysize(gods_author), /* MSGTYPE_GODS */
569 };
570 
571 /******************************************************************************
572  *
573  * Start of misc. readable functions used by others functions in this file
574  *
575  *****************************************************************************/
576 
587  titlelist *bl = (titlelist *)malloc(sizeof(titlelist));
588 
589  if (bl == NULL)
591  bl->number = 0;
592  bl->first_book = NULL;
593  bl->next = NULL;
594  return bl;
595 }
596 
606 static title *get_empty_book(void) {
607  title *t = (title *)malloc(sizeof(title));
608 
609  if (t == NULL)
611  t->name = NULL;
612  t->archname = NULL;
613  t->authour = NULL;
614  t->level = 0;
615  t->size = 0;
616  t->msg_index = 0;
617  t->next = NULL;
618  return t;
619 }
620 
631 static titlelist *get_titlelist(int i) {
632  titlelist *tl;
633  int number;
634 
635  if (i < 0 || i >= (int)arraysize(max_titles)) {
636  LOG(llevInfo, "Warning: invalid book index %d, using 0 instead\n", i);
637  return booklist;
638  }
639 
640  for (tl = booklist, number = i; tl && number; tl = tl->next, number--) {
641  if (!tl->next)
642  tl->next = get_empty_booklist();
643  }
644 
645  return tl;
646 }
647 
648 /* HANDMADE STRING FUNCTIONS.., perhaps these belong in another file
649  * (shstr.c ?), but the quantity BOOK_BUF will need to be defined. */
650 
662 int nstrtok(const char *buf1, const char *buf2) {
663  char *tbuf, buf[MAX_BUF];
664  int number = 0;
665 
666  if (!buf1 || !buf2)
667  return 0;
668 
669  strlcpy(buf, buf1, sizeof(buf));
670  for (tbuf = strtok(buf, buf2); tbuf; tbuf = strtok(NULL, buf2)) {
671  number++;
672  }
673  return number;
674 }
675 
692 char *strtoktolin(const char *buf1, const char *buf2, char *retbuf, size_t size) {
693  int maxi, i = nstrtok(buf1, buf2);
694  char *tbuf, buf[MAX_BUF];
695 
696  maxi = i;
697  strlcpy(buf, buf1, sizeof(buf));
698  snprintf(retbuf, size, " ");
699  for (tbuf = strtok(buf, buf2); tbuf && i > 0; tbuf = strtok(NULL, buf2)) {
700  snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "%s", tbuf);
701  i--;
702  if (i == 1 && maxi > 1)
703  snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), " and ");
704  else if (i > 0 && maxi > 1)
705  snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), ", ");
706  else
707  snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), ".");
708  }
709  return retbuf;
710 }
711 
722 int book_overflow(const char *buf1, const char *buf2, size_t booksize) {
723  if (buf_overflow(buf1, buf2, BOOK_BUF-2) /* 2 less so always room for trailing \n */
724  || buf_overflow(buf1, buf2, booksize))
725  return 1;
726  return 0;
727 }
728 
729 /*****************************************************************************
730  *
731  * Start of initialization related functions.
732  *
733  ****************************************************************************/
734 
740 static void init_msgfile(void) {
741  FILE *fp;
742  char buf[MAX_BUF], msgbuf[HUGE_BUF], fname[MAX_BUF], *cp;
743  int text = 0, nrofmsg = 0;
744  static int did_init_msgfile = 0;
745 
746  if (did_init_msgfile)
747  return;
748  did_init_msgfile = 1;
749 
750  snprintf(fname, sizeof(fname), "%s/messages", settings.datadir);
751  LOG(llevDebug, "Reading messages from %s...\n", fname);
752 
753  fp = fopen(fname, "r");
754  if (fp != NULL) {
755  GeneralMessage *tmp = NULL;
756  int lineno;
757  int error_lineno;
758 
759  error_lineno = 0;
760  for (lineno = 1; fgets(buf, MAX_BUF, fp) != NULL; lineno++) {
761  if (*buf == '#')
762  continue;
763  cp = strchr(buf, '\n');
764  if (cp != NULL) {
765  // Remove trailing whitespace
766  while (cp > buf && (cp[-1] == ' ' || cp[-1] == '\t'))
767  cp--;
768  /* If we make sure there is a newline here,
769  * we can avoid the auto-append of it and make long
770  * blocks of text not get split.
771  * But only do that if we are getting the message.
772  * Everywhere else we do not want the newline.
773  * Daniel Hawkins 2018-10-24
774  */
775  if (text)
776  {
777  *cp = '\n';
778  // to have found a newline means we have room for a null terminator, too
779  *(++cp)= '\0';
780  }
781  else
782  *cp = '\0';
783  }
784  if (tmp != NULL) {
785  if (text && strncmp(buf, "ENDMSG", 6) == 0) {
786  if (strlen(msgbuf) > BOOK_BUF) {
787  LOG(llevDebug, "Warning: this string exceeded max book buf size:\n");
788  LOG(llevDebug, " %s\n", msgbuf);
789  }
790  tmp->message = add_string(msgbuf);
791  tmp->next = first_msg;
792  first_msg = tmp;
793  nrofmsg++;
794  if (tmp->identifier != NULL && tmp->title == NULL) {
795  LOG(llevError, "Error: message can't have identifier without title, on line %d\n", error_lineno);
797  }
798  tmp = NULL;
799  text = 0;
800  } else if (text) {
801  if (!buf_overflow(msgbuf, buf, HUGE_BUF-1)) {
802  strcat(msgbuf, buf);
803  // If there is a newline in the text, it will be included in the output where it belongs
804  // We should avoid really long lines of text getting split up this way.
805  } else if (error_lineno != 0) {
806  LOG(llevInfo, "Warning: truncating book at %s, line %d\n", fname, error_lineno);
807  }
808  } else if (strcmp(buf, "TEXT") == 0) {
809  text = 1;
810  } else if (strncmp(buf, "CHANCE ", 7) == 0) {
811  tmp->chance = atoi(buf + 7);
812  msg_total_chance += tmp->chance;
813  } else if (strncmp(buf, "TITLE ", 6) == 0) {
814  tmp->title = add_string(buf + 6);
815  } else if (strncmp(buf, "QUEST ", 6) == 0) {
816  tmp->quest_code = add_string(buf + 6);
817  } else if (strncmp(buf, "FACE ", 5) == 0) {
818  const Face *face = find_face(buf + 5, NULL);
819  if (face != NULL) {
820  tmp->face = face;
821  } else {
822  LOG(llevInfo, "Warning: unknown face %s for message %s, line %d\n", buf + 5, tmp->identifier, error_lineno);
823  }
824  } else if (error_lineno != 0) {
825  LOG(llevInfo, "Warning: unknown line %s, line %d\n", buf, error_lineno);
826  }
827  } else if (strncmp(buf, "MSG", 3) == 0) {
828  error_lineno = lineno;
829  tmp = (GeneralMessage *)calloc(1, sizeof(GeneralMessage));
830  tmp->face = NULL;
831  strcpy(msgbuf, ""); /* reset msgbuf for new message */
832  if (buf[3] == ' ') {
833  int i = 4;
834  while (buf[i] == ' ' && buf[i] != '\0')
835  i++;
836  if (buf[i] != '\0') {
837  tmp->identifier = add_string(buf + i);
838  if (get_message_from_identifier(buf + i)) {
839  LOG(llevError, "Duplicated message identifier %s at line %d\n", buf + i, error_lineno);
841  }
842  }
843  }
844  } else {
845  LOG(llevInfo, "Warning: syntax error at %s, line %d\n", fname, lineno);
846  }
847  }
848  fclose(fp);
849 
850  if (tmp != NULL) {
851  LOG(llevError, "Invalid file %s", fname);
853  }
854  }
855 
856  LOG(llevDebug, "done messages, found %d for total chance %d.\n", nrofmsg, msg_total_chance);
857 }
858 
865 static void init_book_archive(void) {
866  FILE *fp;
867  int nroftitle = 0;
868  char buf[MAX_BUF], fname[MAX_BUF], *cp;
869  static int did_init_barch = 0;
870 
871  if (did_init_barch)
872  return;
873  did_init_barch = 1;
874 
875  if (!booklist)
876  booklist = get_empty_booklist();
877 
878  snprintf(fname, sizeof(fname), "%s/bookarch", settings.localdir);
879  LOG(llevDebug, " Reading bookarch from %s...\n", fname);
880 
881  fp = fopen(fname, "r");
882  if (fp != NULL) {
883  int type;
884  size_t i;
885  titlelist *bl;
886  int lineno;
887  title *book;
888  int skipping;
889 
890  skipping = 0;
891  book = NULL;
892  type = -1;
893  for (lineno = 1; fgets(buf, MAX_BUF, fp) != NULL; lineno++) {
894  int len;
895  int value;
896 
897  if (*buf == '#')
898  continue;
899  cp = strchr(buf, '\n');
900  if (cp != NULL) {
901  while (cp > buf && (cp[-1] == ' ' || cp[-1] == '\t'))
902  cp--;
903  *cp = '\0';
904  }
905  cp = buf;
906  if (strncmp(buf, "title ", 6) == 0) {
907  skipping = 0;
908  cp = buf+6;
909  while (*cp == ' ' || *cp == '\t')
910  cp++;
911  if (*cp == '\0') {
912  LOG(llevInfo, "Warning: missing book title at %s, line %d\n", fname, lineno);
913  book = NULL;
914  } else {
915  book = get_empty_book(); /* init new book entry */
916  book->name = add_string(cp);
917  type = -1;
918  nroftitle++;
919  }
920  } else if (book == NULL) {
921  if (!skipping) {
922  skipping = 1;
923  LOG(llevInfo, "Warning: expecting 'title' at %s, line %d\n", fname, lineno);
924  }
925  } else if (strncmp(buf, "authour ", 8) == 0) {
926  cp = buf+8;
927  while (*cp == ' ' || *cp == '\t')
928  cp++;
929  if (*cp == '\0') {
930  LOG(llevInfo, "Warning: missing book authour at %s, line %d\n", fname, lineno);
931  } else {
932  book->authour = add_string(cp);
933  }
934  } else if (strncmp(buf, "arch ", 5) == 0) {
935  cp = buf+5;
936  while (*cp == ' ' || *cp == '\t')
937  cp++;
938  if (*cp == '\0') {
939  LOG(llevInfo, "Warning: missing book arch at %s, line %d\n", fname, lineno);
940  } else {
941  book->archname = add_string(cp);
942  }
943  } else if (sscanf(buf, "level %d%n", &value, &len) == 1 && len == (int)strlen(buf)) {
944  book->level = value;
945  } else if (sscanf(buf, "type %d%n", &value, &len) == 1 && len == (int)strlen(buf)) {
946  type = value;
947  } else if (sscanf(buf, "size %d%n", &value, &len) == 1 && len == (int)strlen(buf)) {
948  book->size = value;
949  } else if (sscanf(buf, "index %d%n", &value, &len) == 1 && len == (int)strlen(buf)) {
950  book->msg_index = value;
951  } else if (strcmp(buf, "end") == 0) { /* link it */
952  add_book(book, type, fname, lineno);
953  book = NULL;
954  type = -1;
955  } else {
956  LOG(llevInfo, "Warning: syntax error at %s, line %d\n", fname, lineno);
957  }
958  }
959  if (book != NULL) {
960  LOG(llevInfo, "Warning: missing 'end' at %s, line %d\n", fname, lineno);
961  add_book(book, type, fname, lineno);
962  }
963  LOG(llevDebug, " book archives(used/avail):\n");
964  for (bl = booklist, i = 0; bl != NULL && i < arraysize(max_titles); bl = bl->next, i++) {
965  LOG(llevDebug, "(%d/%d)\n", bl->number, max_titles[i]);
966  }
967  fclose(fp);
968  }
969 
970 #ifdef BOOK_MSG_DEBUG
971  LOG(llevDebug, "\n init_book_archive() got %d titles.\n", nroftitle);
972 #endif
973  LOG(llevDebug, " done.\n");
974 }
975 
983 static void add_book(title *book, int type, const char *fname, int lineno) {
984  titlelist *bl;
985 
986  if (type == -1) {
987  LOG(llevInfo, "Warning: book with no type at %s, line %d; using type 0\n", fname, lineno);
988  type = 0;
989  }
990 
991  bl = get_titlelist(type);
992  book->next = bl->first_book;
993  bl->first_book = book;
994  bl->number++;
995 }
996 
1001 static void init_mon_info(void) {
1002  archetype *at;
1003  static int did_init_mon_info = 0;
1004 
1005  if (did_init_mon_info)
1006  return;
1007  did_init_mon_info = 1;
1008 
1009  for (at = first_archetype; at != NULL; at = at->next) {
1010  if (QUERY_FLAG(&at->clone, FLAG_MONSTER)
1012  objectlink *mon = (objectlink *)malloc(sizeof(objectlink));
1013  if (!mon) {
1014  LOG(llevError, "init_mon_info: malloc failed!\n");
1015  abort();
1016  }
1017  mon->ob = &at->clone;
1018  mon->id = nrofmon;
1019  mon->next = first_mon_info;
1020  first_mon_info = mon;
1021  nrofmon++;
1022  }
1023  }
1024  LOG(llevDebug, "init_mon_info() got %d monsters\n", nrofmon);
1025 }
1026 
1033 void init_readable(void) {
1034  static int did_this = 0;
1035 
1036  if (did_this)
1037  return;
1038  did_this = 1;
1039 
1040  LOG(llevDebug, "Initializing reading data...\n");
1041  init_msgfile();
1043  init_mon_info();
1044  LOG(llevDebug, " done reading data\n");
1045 }
1046 
1047 /*****************************************************************************
1048  *
1049  * This is the start of the administrative functions when creating
1050  * new books (ie, updating title and the like)
1051  *
1052  *****************************************************************************/
1053 
1065 static title *find_title(const object *book, int msgtype) {
1066  title *t;
1067  titlelist *tl;
1068  size_t length;
1069  int index;
1070 
1071  if (msgtype < 0)
1072  return (title *)NULL;
1073 
1074  tl = get_titlelist(msgtype);
1075  if (!tl)
1076  return (title *)NULL;
1077 
1078  length = strlen(book->msg);
1079  index = strtoint(book->msg);
1080  for (t = tl->first_book; t; t = t->next)
1081  if (t->size == length && t->msg_index == index) {
1082 #ifdef ARCHIVE_DEBUG
1083  LOG(llevDebug, "Found title match (list %d): %s %s (%d)\n", msgtype, t->name, t->authour, t->msg_index);
1084 #endif
1085  return t;
1086  }
1087 
1088  return (title *)NULL;
1089 }
1090 
1104 static void new_text_name(object *book, int msgtype) {
1105  const char *name;
1106 
1107  if (book->type != BOOK)
1108  return;
1109 
1110  switch (msgtype) {
1111  case MSGTYPE_MONSTER:
1113  break;
1114 
1115  case MSGTYPE_ARTIFACT:
1117  break;
1118 
1119  case MSGTYPE_SPELLPATH:
1121  break;
1122 
1123  case MSGTYPE_ALCHEMY:
1125  break;
1126 
1127  case MSGTYPE_GODS:
1129  break;
1130 
1131  case MSGTYPE_MSGFILE:
1132  default:
1133  if (book->weight > 2000) { /* based on weight */
1135  } else {
1137  }
1138  break;
1139  }
1140  free_string(book->name);
1141  book->name = add_string(name);
1142 }
1143 
1153 static void add_author(object *op, int msgtype) {
1154  char title[MAX_BUF];
1155  const char *name;
1156 
1157  if (msgtype < 0 || strlen(op->msg) < 5)
1158  return;
1159 
1160  switch (msgtype) {
1161  case MSGTYPE_MONSTER:
1163  break;
1164 
1165  case MSGTYPE_ARTIFACT:
1167  break;
1168 
1169  case MSGTYPE_SPELLPATH:
1171  break;
1172 
1173  case MSGTYPE_ALCHEMY:
1175  break;
1176 
1177  case MSGTYPE_GODS:
1179  break;
1180 
1181  case MSGTYPE_MSGFILE:
1182  default:
1184  }
1185 
1186  snprintf(title, sizeof(title), "of %s", name);
1187  op->title = add_string(title);
1188 }
1189 
1201 static int unique_book(const object *book, int msgtype) {
1202  title *test;
1203 
1204  if (!booklist)
1205  return 1; /* No archival entries! Must be unique! */
1206 
1207  /* Go through the booklist. If the author and name match, not unique so
1208  * return 0.
1209  */
1210  for (test = get_titlelist(msgtype)->first_book; test; test = test->next) {
1211  if (!strcmp(test->name, book->name) && !strcmp(book->title, test->authour))
1212  return 0;
1213  }
1214  return 1;
1215 }
1216 
1225 static void add_book_to_list(const object *book, int msgtype) {
1226  titlelist *tl = get_titlelist(msgtype);
1227  title *t;
1228 
1229  if (!tl) {
1230  LOG(llevError, "add_book_to_list can't get booklist!\n");
1231  return;
1232  }
1233 
1234  t = get_empty_book();
1235  t->name = add_string(book->name);
1236  t->authour = add_string(book->title);
1237  t->size = strlen(book->msg);
1238  t->msg_index = strtoint(book->msg);
1239  t->archname = add_string(book->arch->name);
1240  t->level = book->level;
1241 
1242  t->next = tl->first_book;
1243  tl->first_book = t;
1244  tl->number++;
1245 
1246  /* We have stuff we need to write now */
1248 
1249 #ifdef ARCHIVE_DEBUG
1250  LOG(llevDebug, "Archiving new title: %s %s (%d)\n", book->name, book->title, msgtype);
1251 #endif
1252 }
1253 
1267 static void change_book(object *book, int msgtype) {
1268  titlelist *tl;
1269  title *t;
1270  int tries;
1271 
1272  if (book->type != BOOK) {
1273  LOG(llevError, "change_book_name() called w/ illegal obj type.\n");
1274  return;
1275  }
1276 
1277  tl = get_titlelist(msgtype);
1278  t = NULL;
1279  tries = 0;
1280 
1281  /* look to see if our msg already been archived. If so, alter
1282  * the book to match the archival text. If we fail to match,
1283  * then we archive the new title/name/msg combo if there is
1284  * room on the titlelist.
1285  */
1286 
1287  if (strlen(book->msg) > 5 && (t = find_title(book, msgtype))) {
1288  object *tmpbook;
1289  sstring marker = object_get_value(book, "knowledge_marker");
1290 
1291  /* alter book properties */
1292  tmpbook = create_archetype(t->archname);
1293  if (marker != NULL)
1294  /* need to copy the knowledge_marker */
1295  object_set_value(tmpbook, "knowledge_marker", marker, 1);
1296  object_set_msg(tmpbook, book->msg);
1297  object_copy(tmpbook, book);
1298  object_free_drop_inventory(tmpbook);
1299 
1300  book->title = add_string(t->authour);
1301  free_string(book->name);
1302  book->name = add_string(t->name);
1303  book->level = t->level;
1304  } else { /* Don't have any default title, so lets make up a new one */
1305  int numb, maxnames = max_titles[msgtype];
1306  const char *old_title;
1307  const char *old_name;
1308 
1309  old_title = book->title ? add_string(book->title) : NULL;
1310  old_name = add_string(book->name);
1311 
1312  /* some pre-generated books have title already set (from
1313  * maps), also don't bother looking for unique title if
1314  * we already used up all the available names! */
1315 
1316  if (!tl) {
1317  LOG(llevError, "change_book_name(): can't find title list\n");
1318  numb = 0;
1319  } else
1320  numb = tl->number;
1321 
1322  if (numb == maxnames) {
1323 #ifdef ARCHIVE_DEBUG
1324  LOG(llevDebug, "titles for list %d full (%d possible).\n", msgtype, maxnames);
1325 #endif
1326  if (old_title != NULL)
1327  free_string(old_title);
1328  free_string(old_name);
1329  return;
1330  }
1331  /* shouldnt change map-maker books */
1332  if (!book->title)
1333  do {
1334  /* random book name */
1335  new_text_name(book, msgtype);
1336  add_author(book, msgtype); /* random author */
1337  tries++;
1338  } while (!unique_book(book, msgtype) && tries < MAX_TITLE_CHECK);
1339 
1340  /* Now deal with 2 cases.
1341  * 1) If no space for a new title exists lets just restore
1342  * the old book properties. Remember, if the book had
1343  * matchd an older entry on the titlelist, we shouldnt
1344  * have called this routine in the first place!
1345  * 2) If we got a unique title, we need to add it to
1346  * the list.
1347  */
1348 
1349  if (tries == MAX_TITLE_CHECK) {
1350 #ifdef ARCHIVE_DEBUG
1351  LOG(llevDebug, "Failed to obtain unique title for %s %s (names:%d/%d)\n", book->name, book->title, numb, maxnames);
1352 #endif
1353  /* restore old book properties here */
1354  free_string(book->name);
1355  free_string(book->title);
1356  book->title = old_title != NULL ? add_string(old_title) : NULL;
1357 
1358  if (RANDOM()%4) {
1359  /* Lets give the book a description to individualize it some */
1360  char new_name[MAX_BUF];
1361 
1362  snprintf(new_name, MAX_BUF, "%s %s", book_descrpt[RANDOM()%arraysize(book_descrpt)], old_name);
1363  book->name = add_string(new_name);
1364  } else {
1365  book->name = add_string(old_name);
1366  }
1367  } else if (book->title && strlen(book->msg) > 5) { /* archive if long msg texts */
1368  add_book_to_list(book, msgtype);
1369  }
1370 
1371  if (old_title != NULL)
1372  free_string(old_title);
1373  free_string(old_name);
1374  }
1375 }
1376 
1377 /*****************************************************************************
1378  *
1379  * This is the start of the area that generates the actual contents
1380  * of the book.
1381  *
1382  *****************************************************************************/
1383 
1384 /*****************************************************************************
1385  * Monster msg generation code.
1386  ****************************************************************************/
1387 
1399 object *get_random_mon(int level) {
1400  objectlink *mon;
1401  int i, monnr;
1402 
1403  /* safety check. Problem w/ init_mon_info list? */
1404  if (!nrofmon || !first_mon_info)
1405  return (object *)NULL;
1406 
1407  if (!level) {
1408  /* lets get a random monster from the mon_info linked list */
1409  monnr = RANDOM()%nrofmon;
1410 
1411  for (mon = first_mon_info, i = 0; mon; mon = mon->next, i++)
1412  if (i == monnr)
1413  break;
1414 
1415  if (!mon) {
1416  LOG(llevError, "get_random_mon: Didn't find a monster when we should have\n");
1417  return NULL;
1418  }
1419  return mon->ob;
1420  }
1421 
1422  /* Case where we are searching by level. Redone 971225 to be clearer
1423  * and more random. Before, it looks like it took a random monster from
1424  * the list, and then returned the first monster after that which was
1425  * appropriate level. This wasn't very random because if you had a
1426  * bunch of low level monsters and then a high level one, if the random
1427  * determine took one of the low level ones, it would just forward to the
1428  * high level one and return that. Thus, monsters that immediately followed
1429  * a bunch of low level monsters would be more heavily returned. It also
1430  * means some of the dragons would be poorly represented, since they
1431  * are a group of high level monsters all around each other.
1432  */
1433 
1434  /* First count number of monsters meeting level criteria */
1435  for (mon = first_mon_info, i = 0; mon; mon = mon->next)
1436  if (mon->ob->level >= level)
1437  i++;
1438 
1439  if (i == 0) {
1440  LOG(llevError, "get_random_mon() couldn't return monster for level %d\n", level);
1441  return NULL;
1442  }
1443 
1444  monnr = RANDOM()%i;
1445  for (mon = first_mon_info; mon; mon = mon->next)
1446  if (mon->ob->level >= level && monnr-- == 0)
1447  return mon->ob;
1448 
1449  LOG(llevError, "get_random_mon(): didn't find a monster when we should have\n");
1450  return NULL;
1451 }
1452 
1462 static StringBuffer *mon_desc(const object *mon) {
1463  StringBuffer *desc = stringbuffer_new();
1464  stringbuffer_append_printf(desc, "\n---\n *** %s ***\n", mon->name);
1465  describe_item(mon, NULL, 0, desc);
1466  return desc;
1467 }
1468 
1480 static object *get_next_mon(const object *tmp) {
1481  objectlink *mon;
1482 
1483  for (mon = first_mon_info; mon; mon = mon->next)
1484  if (mon->ob == tmp)
1485  break;
1486 
1487  /* didn't find a match */
1488  if (!mon)
1489  return NULL;
1490  if (mon->next)
1491  return mon->next->ob;
1492  else
1493  return first_mon_info->ob;
1494 }
1495 
1508 static StringBuffer *mon_info_msg(int level, size_t booksize, object *book) {
1509  object *tmp;
1510  StringBuffer *marker = stringbuffer_new(), *desc = stringbuffer_new(), *mon = NULL;
1511  int added = 0;
1512  sstring final;
1513  const char *sep = ":";
1514 
1515  /*preamble */
1516  stringbuffer_append_string(desc, "This beastiary contains:");
1517  stringbuffer_append_string(marker, "monster");
1518 
1519  /* lets print info on as many monsters as will fit in our
1520  * document.
1521  * 8-96 Had to change this a bit, otherwise there would
1522  * have been an impossibly large number of combinations
1523  * of text! (and flood out the available number of titles
1524  * in the archive in a snap!) -b.t.
1525  */
1526  for (tmp = get_random_mon(level*3); tmp; tmp = get_next_mon(tmp)) {
1527  /* monster description */
1528  mon = mon_desc(tmp);
1529 
1530  if (stringbuffer_length(desc) + stringbuffer_length(mon) >= booksize)
1531  break;
1532  added++;
1533  stringbuffer_append_printf(marker, "%s%s", sep, tmp->arch->name);
1534  sep = "/";
1537  mon = NULL;
1538  }
1539 
1540  if (mon != NULL) {
1542  }
1543 
1544  final = stringbuffer_finish_shared(marker);
1545  if (added)
1546  object_set_value(book, "knowledge_marker", final, 1);
1547  free_string(final);
1548 
1549  return desc;
1550 }
1551 
1552 /*****************************************************************************
1553  * Artifact msg generation code.
1554  ****************************************************************************/
1555 
1565 static StringBuffer *artifact_describe(const artifact *art, const artifactlist *al, int message, int art_name, int separator) {
1566  object *tmp;
1567  int chance;
1568  StringBuffer *desc = stringbuffer_new(), *sbuf;
1569 
1570  if (separator)
1571  stringbuffer_append_string(desc, "---\n");
1572 
1573  /* Name */
1574  if (art->allowed != NULL) {
1575  archetype *arch;
1576  linked_char *temp = art->allowed;
1577  int inv = 0, w;
1578 
1579  assert(art->allowed_size > 0);
1580  if (art->allowed_size > 1)
1581  w = 1 + RANDOM() % art->allowed_size;
1582  else
1583  w = 1;
1584 
1585  while (w > 1) {
1586  assert(temp);
1587  temp = temp->next;
1588  w--;
1589  }
1590 
1591  if (temp->name[0] == '!')
1592  inv = 1;
1593 
1595  arch = try_find_archetype(temp->name + inv);
1596  if (!arch)
1597  arch = find_archetype_by_object_name(temp->name + inv);
1598 
1599  if (!arch)
1600  LOG(llevError, "artifact_msg: missing archetype %s for artifact %s (type %d)\n", temp->name + inv, art->item->name, art->item->type);
1601  else {
1602  if (inv)
1603  stringbuffer_append_printf(desc, " A %s (excepted %s) of %s", art_name_array[art_name].name, arch->clone.name_pl, art->item->name);
1604  else
1605  stringbuffer_append_printf(desc, " A %s of %s", arch->clone.name, art->item->name);
1606  }
1607  } else { /* default name is used */
1608  /* use the base 'generic' name for our artifact */
1609  stringbuffer_append_printf(desc, " The %s of %s", art_name_array[art_name].name, art->item->name);
1610  }
1611 
1612  /* chance of finding */
1613  stringbuffer_append_string(desc, " is ");
1614  chance = 100*((float)art->chance/al->total_chance);
1615  if (chance >= 20)
1616  stringbuffer_append_string(desc, "an uncommon");
1617  else if (chance >= 10)
1618  stringbuffer_append_string(desc, "an unusual");
1619  else if (chance >= 5)
1620  stringbuffer_append_string(desc, "a rare");
1621  else
1622  stringbuffer_append_string(desc, "a very rare");
1623 
1624  /* value of artifact */
1625  stringbuffer_append_printf(desc, " item with a value that is %d times normal.\n", art->item->value);
1626 
1627  /* include the message about the artifact, if exists, and book
1628  * level is kinda high */
1629  if (message && !(strlen(art->item->msg) > BOOK_BUF))
1630  stringbuffer_append_string(desc, art->item->msg);
1631 
1632  /* properties of the artifact */
1633  tmp = object_new();
1634  add_abilities(tmp, art->item);
1635  tmp->type = al->type;
1636  SET_FLAG(tmp, FLAG_IDENTIFIED);
1637  sbuf = describe_item(tmp, NULL, 0, NULL);
1638  if (stringbuffer_length(sbuf) > 1) {
1639  stringbuffer_append_string(desc, " Properties of this artifact include:\n ");
1641  stringbuffer_append_string(desc, "\n");
1642  }
1643  free(stringbuffer_finish(sbuf));
1645 
1646  return desc;
1647 }
1648 
1660 static StringBuffer *artifact_msg(unsigned int level, size_t booksize) {
1661  const artifactlist *al;
1662  const artifact *art;
1663  int i, type, index;
1664  int book_entries = level > 5 ? RANDOM()%3+RANDOM()%3+2 : RANDOM()%level+1;
1665  StringBuffer *desc, *message = stringbuffer_new();
1666 
1667  /* values greater than 5 create msg buffers that are too big! */
1668  if (book_entries > 5)
1669  book_entries = 5;
1670 
1671  /* lets determine what kind of artifact type randomly.
1672  * Right now legal artifacts only come from those listed
1673  * in art_name_array. Also, we check to be sure an artifactlist
1674  * for that type exists!
1675  */
1676  i = 0;
1677  do {
1678  index = RANDOM()%arraysize(art_name_array);
1679  type = art_name_array[index].type;
1680  al = find_artifactlist(type);
1681  i++;
1682  } while (al == NULL && i < 10);
1683 
1684  if (i == 10) { /* Unable to find a message */
1685  stringbuffer_append_string(message, "None");
1686  return message;
1687  }
1688 
1689  /* There is no reason to start on the artifact list at the beginning. Lets
1690  * take our starting position randomly... */
1691  art = al->items;
1692  for (i = RANDOM()%level+RANDOM()%2+1; i > 0; i--) {
1693  if (art == NULL)
1694  art = al->items; /* hmm, out of stuff, loop back around */
1695  art = art->next;
1696  }
1697 
1698  /* Ok, lets print out the contents */
1699  stringbuffer_append_printf(message, "Herein %s detailed %s...\n", book_entries > 1 ? "are" : "is", book_entries > 1 ? "some artifacts" : "an artifact");
1700 
1701  i = 0;
1702  /* artifact msg attributes loop. Lets keep adding entries to the 'book'
1703  * as long as we have space up to the allowed max # (book_entires)
1704  */
1705  while (book_entries > 0) {
1706  int with_message;
1707  if (art == NULL)
1708  art = al->items;
1709  with_message = (art->item->msg && RANDOM()%4+1 < level) ? 1 : 0;
1710 
1711  desc = artifact_describe(art, al, with_message, index, i++);
1712 
1713  if (stringbuffer_length(message) + stringbuffer_length(desc) >= booksize) {
1714  stringbuffer_delete(desc);
1715  break;
1716  }
1717 
1718  stringbuffer_append_stringbuffer(message, desc);
1719  stringbuffer_delete(desc);
1720 
1721  art = art->next;
1722  book_entries--;
1723  }
1724 
1725  return message;
1726 }
1727 
1728 /*****************************************************************************
1729  * Spellpath message generation
1730  *****************************************************************************/
1731 
1745 static StringBuffer *spellpath_msg(int level, size_t booksize, StringBuffer *buf) {
1746  int path = RANDOM()%NRSPELLPATHS, prayers = RANDOM()%2;
1747  int did_first_sp = 0;
1748  uint32_t pnum = spellpathdef[path];
1749  archetype *at;
1750 
1751  if (buf == NULL) {
1752  buf = stringbuffer_new();
1753  /* Preamble */
1754  stringbuffer_append_printf(buf, "Herein are detailed the names of %s", prayers ? "prayers" : "incantations");
1755  stringbuffer_append_printf(buf, " belonging to the path of %s:\n ", spellpathnames[path]);
1756  }
1757 
1758  for (at = first_archetype; at != NULL; at = at->next) {
1759  /* Determine if this is an appropriate spell. Must
1760  * be of matching path, must be of appropriate type (prayer
1761  * or not), and must be within the valid level range.
1762  */
1763  if (at->clone.type == SPELL
1764  && at->clone.path_attuned&pnum
1765  && ((at->clone.stats.grace && prayers) || (at->clone.stats.sp && !prayers))
1766  && at->clone.level < level*8) {
1767  if (strlen(at->clone.name) + stringbuffer_length(buf) >= booksize)
1768  break;
1769 
1770  if (did_first_sp)
1771  stringbuffer_append_string(buf, ",\n ");
1772  did_first_sp = 1;
1774  }
1775  }
1776 
1777  /* Geez, no spells were generated. */
1778  if (!did_first_sp) {
1779  if (RANDOM()%4) { /* usually, lets make a recursive call... */
1780  return spellpath_msg(level, booksize, buf);
1781  }
1782  /* give up, cause knowing no spells exist for path is info too. need the header too. */
1783  stringbuffer_append_string(buf, "- no known spells exist -\n");
1784  }
1785  return buf;
1786 }
1787 
1796 static void make_formula_book(object *book, int level) {
1797  recipelist *fl;
1798  recipe *formula;
1799  int chance, count = 0;
1800  const char *op_name;
1801  archetype *at;
1802  StringBuffer *text, *title;
1803  char *final, km[MAX_BUF];
1804 
1805  /* the higher the book level, the more complex (ie number of
1806  * ingredients) the formula can be.
1807  */
1808  fl = get_formulalist((RANDOM()%level)/3+1);
1809  if (!fl)
1810  fl = get_formulalist(1); /* safety */
1811 
1812  if (fl->total_chance == 0) {
1813  object_set_msg(book, " <indecipherable text>\n");
1815  add_author(book, MSGTYPE_ALCHEMY);
1816  return;
1817  }
1818 
1819  /* get a random formula, weighted by its bookchance */
1820  chance = RANDOM()%fl->total_chance;
1821  for (formula = fl->items; formula != NULL; formula = formula->next) {
1822  chance -= formula->chance;
1823  if (chance <= 0 && formula->chance != 0 && !formula->is_combination)
1824  break;
1825  }
1826 
1827  if (!formula || formula->arch_names <= 0) {
1828  object_set_msg(book, " <indecipherable text>\n");
1830  add_author(book, MSGTYPE_ALCHEMY);
1831  return;
1832  }
1833 
1834  /* looks like a formula was found. Base the amount
1835  * of information on the booklevel and the spellevel
1836  * of the formula. */
1837 
1838  op_name = formula->arch_name[RANDOM()%formula->arch_names];
1839  at = find_archetype(op_name);
1840  if (at == (archetype *)NULL) {
1841  LOG(llevError, "formula_msg() can't find arch %s for formula.\n", op_name);
1842  object_set_msg(book, " <indecipherable text>\n");
1844  add_author(book, MSGTYPE_ALCHEMY);
1845  return;
1846  }
1847  op_name = at->clone.name;
1848 
1849  text = stringbuffer_new();
1850  title = stringbuffer_new();
1851 
1852  /* preamble */
1853  stringbuffer_append_printf(text, "Herein is described a project using %s:\n", formula->skill ? formula->skill : "an unknown skill");
1854 
1855  /* item name */
1856  if (strcmp(formula->title, "NONE")) {
1857  stringbuffer_append_printf(text, "The %s of %s", op_name, formula->title);
1858  /* This results in things like pile of philo. sulfur.
1859  * while philo. sulfur may look better, without this,
1860  * you get things like 'the wise' because its missing the
1861  * water of section.
1862  */
1863  stringbuffer_append_printf(title, "%s: %s of %s", formula_book_name[RANDOM()%arraysize(formula_book_name)], op_name, formula->title);
1864  } else {
1865  stringbuffer_append_printf(text, "The %s", op_name);
1867  if (at->clone.title) {
1868  stringbuffer_append_printf(text, " %s", at->clone.title);
1869  stringbuffer_append_printf(title, " %s", at->clone.title);
1870  }
1871  }
1872  /* Lets name the book something meaningful ! */
1873  if (book->name)
1874  free_string(book->name);
1875  book->name = stringbuffer_finish_shared(title);
1876  if (book->title) {
1877  free_string(book->title);
1878  book->title = NULL;
1879  }
1880 
1881  /* ingredients to make it */
1882  if (formula->ingred != NULL) {
1883  linked_char *next;
1884  archetype *at;
1885  char name[MAX_BUF];
1886 
1887  at = find_archetype(formula->cauldron);
1888  if (at)
1889  query_name(&at->clone, name, MAX_BUF);
1890  else
1891  snprintf(name, sizeof(name), "an unknown place");
1892 
1893  stringbuffer_append_printf(text, " may be made at %s using the following ingredients:\n", name);
1894 
1895  for (next = formula->ingred; next != NULL; next = next->next) {
1896  count++;
1897  stringbuffer_append_printf(text, "%s\n", next->name);
1898  }
1899  } else {
1900  LOG(llevError, "formula_msg() no ingredient list for object %s of %s\n", op_name, formula->title);
1901  stringbuffer_append_string(text, "\n");
1902  }
1903 
1904  final = stringbuffer_finish(text);
1905  object_set_msg(book, final);
1906  free(final);
1907 
1910  snprintf(km, sizeof(km), "alchemy:%d:%d:%s", count, formula->index, formula->title);
1911  object_set_value(book, "knowledge_marker", km, 1);
1912 }
1913 
1924 static StringBuffer *msgfile_msg(object *book, size_t booksize) {
1925  int weight;
1926  GeneralMessage *msg = NULL;
1927  StringBuffer *ret = stringbuffer_new();
1928 
1929  /* get a random message for the 'book' from linked list */
1930  if (msg_total_chance > 0) {
1931  assert(first_msg != NULL);
1932  msg = first_msg;
1933  weight = (RANDOM() % msg_total_chance);
1934  while (msg) {
1935  weight -= msg->chance;
1936  if (weight < 0)
1937  break;
1938  msg = msg->next;
1939  }
1940  /* if msg is NULL, then something is really wrong in the computation! */
1941  assert(msg != NULL);
1942  }
1943 
1944  if (msg && strlen(msg->message) <= booksize) {
1946  if (msg->identifier != NULL) {
1947  char km[HUGE_BUF];
1950  snprintf(km, sizeof(km), "message:%s", msg->identifier);
1951  object_set_value(book, "knowledge_marker", km, 1);
1952  }
1953  if (msg->quest_code) {
1954  /* add a 'apply' hook to launch the quest */
1955  archetype *at = find_archetype("quest_advance_apply");
1956  if (at != NULL) {
1957  object *event = object_create_arch(at);
1958  FREE_AND_COPY(event->name, msg->quest_code);
1959  object_insert_in_ob(event, book);
1960  }
1961  }
1962  } else
1963  stringbuffer_append_string(ret, "\n <undecipherable text>");
1964 
1965  return ret;
1966 }
1967 
1982 static StringBuffer *god_info_msg(int level, size_t booksize, object *book) {
1983  int what = 0;
1984  const object *god = pntr_to_god_obj(get_rand_god());
1985  StringBuffer *desc = NULL;
1986 
1987  if (!god)
1988  return NULL; /* oops, problems... */
1989 
1990  if (booksize > BOOK_BUF) {
1991  LOG(llevError, "common/readable.c:god_info_msg() - passed in booksize (%lu) is larger than book buffer (%d)\n", (unsigned long)booksize, BOOK_BUF);
1992  booksize = BOOK_BUF;
1993  }
1994 
1995  if (level >= 2 && RANDOM()%2) {
1996  what |= GOD_ENEMY;
1997  }
1998  if (level >= 3 && RANDOM()%2) {
1999  what |= GOD_HOLYWORD;
2000  }
2001  if (level >= 4 && RANDOM()%2) {
2002  what |= GOD_RESISTANCES;
2003  }
2004  if (level >= 5 && RANDOM()%2) {
2005  what |= GOD_SACRED;
2006  }
2007  if (level >= 6 && RANDOM()%2) {
2008  what |= GOD_BLESSED;
2009  }
2010  if (level >= 8 && RANDOM()%2) {
2011  what |= GOD_IMMUNITIES;
2012  }
2013  if (level >= 12 && RANDOM()%2) {
2014  what |= GOD_PATHS;
2015  }
2016 
2017  desc = stringbuffer_new();
2018  what = describe_god(god, what, desc, booksize);
2019 
2020  /* check to be sure new buffer size dont exceed either
2021  * the maximum buffer size, or the 'natural' size of the
2022  * book... */
2023  if (stringbuffer_length(desc) > 1 && stringbuffer_length(desc) <= booksize) {
2024  char buf[BOOK_BUF];
2025  snprintf(buf, sizeof(buf), "god:%s:%d", god->name, what);
2026  object_set_value(book, "knowledge_marker", buf, 1);
2027  return desc;
2028  }
2029 
2030  stringbuffer_delete(desc);
2031  return NULL;
2032 }
2033 
2052 void tailor_readable_ob(object *book, int msg_type) {
2053  int level = book->level ? RANDOM()%book->level+1 : 1;
2054  size_t book_buf_size;
2055  StringBuffer *message = NULL;
2056 
2057  /* safety */
2058  if (book->type != BOOK)
2059  return;
2060 
2061  if (level <= 0)
2062  return; /* if no level no point in doing any more... */
2063 
2064  /* Max text length this book can have. */
2065  book_buf_size = BOOKSIZE(book);
2066  book_buf_size -= strlen("\n"); /* Keep enough for final \n. */
2067  assert(book_buf_size < BOOK_BUF);
2068 
2069  /* &&& The message switch &&& */
2070  /* Below all of the possible types of messages in the "book"s.
2071  */
2072  /*
2073  * IF you add a new type of book msg, you will have to do several things.
2074  * 1) make sure there is an entry in the msg switch below!
2075  * 2) make sure there is an entry in max_titles[] array.
2076  * 3) make sure there are entries for your case in new_text_title()
2077  * and add_authour().
2078  * 4) you may want separate authour/book name arrays in read.h
2079  */
2080 
2081  if (msg_type >= (int)arraysize(max_titles))
2082  msg_type = 0;
2083 
2084  msg_type = msg_type > 0 ? msg_type : (int)(RANDOM()%6);
2085  switch (msg_type) {
2086  case MSGTYPE_MONSTER:
2087  message = mon_info_msg(level, book_buf_size, book);
2088  break;
2089 
2090  case MSGTYPE_ARTIFACT:
2091  message = artifact_msg(level, book_buf_size);
2092  break;
2093 
2094  case MSGTYPE_SPELLPATH: /* grouping incantations/prayers by path */
2095  message = spellpath_msg(level, book_buf_size, NULL);
2096  break;
2097 
2098  case MSGTYPE_ALCHEMY: /* describe an alchemy formula */
2099  make_formula_book(book, level);
2100  /* make_formula_book already gives title */
2101  return;
2102  break;
2103 
2104  case MSGTYPE_GODS: /* bits of information about a god */
2105  message = god_info_msg(level, book_buf_size, book);
2106  break;
2107 
2108  case MSGTYPE_LIB: /* use info list in lib/ */
2109  default:
2110  message = msgfile_msg(book, book_buf_size);
2111  break;
2112  }
2113 
2114  if (message != NULL) {
2115  char *final;
2116  stringbuffer_append_string(message, "\n");
2117  final = stringbuffer_finish(message);
2118  object_set_msg(book, final);
2119  free(final);
2120  /* lets give the "book" a new name, which may be a compound word */
2121  change_book(book, msg_type);
2122  }
2123 }
2124 
2125 /*****************************************************************************
2126  *
2127  * Cleanup routine for readable stuff.
2128  *
2129  *****************************************************************************/
2130 
2134 void free_all_readable(void) {
2135  titlelist *tlist, *tnext;
2136  title *title1, *titlenext;
2137  GeneralMessage *lmsg, *nextmsg;
2138  objectlink *monlink, *nextmon;
2139 
2140  LOG(llevDebug, "freeing all book information\n");
2141 
2142  for (tlist = booklist; tlist != NULL; tlist = tnext) {
2143  tnext = tlist->next;
2144  for (title1 = tlist->first_book; title1; title1 = titlenext) {
2145  titlenext = title1->next;
2146  if (title1->name)
2147  free_string(title1->name);
2148  if (title1->authour)
2149  free_string(title1->authour);
2150  if (title1->archname)
2151  free_string(title1->archname);
2152  free(title1);
2153  }
2154  free(tlist);
2155  }
2156  for (lmsg = first_msg; lmsg; lmsg = nextmsg) {
2157  nextmsg = lmsg->next;
2158  if (lmsg->identifier)
2159  free_string(lmsg->identifier);
2160  if (lmsg->title)
2161  free_string(lmsg->title);
2162  if (lmsg->message)
2163  free_string(lmsg->message);
2164  if (lmsg->quest_code)
2165  free_string(lmsg->quest_code);
2166  free(lmsg);
2167  }
2168  for (monlink = first_mon_info; monlink; monlink = nextmon) {
2169  nextmon = monlink->next;
2170  free(monlink);
2171  }
2172 }
2173 
2174 /*****************************************************************************
2175  *
2176  * Writeback routine for updating the bookarchive.
2177  *
2178  ****************************************************************************/
2179 
2184  FILE *fp;
2185  OutputFile of;
2186  int index;
2187  char fname[MAX_BUF];
2188  title *book;
2189  titlelist *bl;
2190 
2191  /* If nothing changed, don't write anything */
2193  return;
2194 
2195  snprintf(fname, sizeof(fname), "%s/bookarch", settings.localdir);
2196  LOG(llevDebug, "Updating book archive: %s...\n", fname);
2197 
2198  fp = of_open(&of, fname);
2199  if (fp == NULL)
2200  return;
2201 
2202  for (bl = get_titlelist(0), index = 0; bl; bl = bl->next, index++) {
2203  for (book = bl->first_book; book; book = book->next)
2204  if (book && book->authour) {
2205  fprintf(fp, "title %s\n", book->name);
2206  fprintf(fp, "authour %s\n", book->authour);
2207  fprintf(fp, "arch %s\n", book->archname);
2208  fprintf(fp, "level %d\n", book->level);
2209  fprintf(fp, "type %d\n", index);
2210  /* C89 doesn't have %zu... */
2211  fprintf(fp, "size %lu\n", (unsigned long)book->size);
2212  fprintf(fp, "index %d\n", book->msg_index);
2213  fprintf(fp, "end\n");
2214  }
2215  }
2216  if (!of_close(&of))
2217  return;
2218 
2219  if (chmod(fname, SAVE_MODE) != 0) {
2220  LOG(llevError, "Could not set permissions on '%s'\n", fname);
2221  }
2222 
2224 }
2225 
2234  uint8_t subtype = readable->subtype;
2235 
2236  if (subtype > last_readable_subtype)
2237  return &readable_message_types[0];
2238  return &readable_message_types[subtype];
2239 }
2240 
2246 const GeneralMessage *get_message_from_identifier(const char *identifier) {
2247  GeneralMessage *msg = first_msg;
2248  while (msg && ((msg->identifier == 0) || (strcmp(msg->identifier, identifier) != 0)))
2249  msg = msg->next;
2250 
2251  return msg;
2252 }
2253 
2260  return message->title;
2261 }
2262 
2269  return message->message;
2270 }
2271 
2277 const Face *get_message_face(const GeneralMessage *message) {
2278  return message->face;
2279 }
#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:407
archetype * find_archetype(const char *name)
Definition: arch.c:695
#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:444
#define MSG_TYPE_CARD_ELEGANT_1
Definition: newclient.h:417
static void init_msgfile(void)
Definition: readable.c:740
#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:2259
static void make_formula_book(object *book, int level)
Definition: readable.c:1796
const artifactlist * find_artifactlist(int type)
Definition: artifact.c:661
static titlelist * get_titlelist(int i)
Definition: readable.c:631
static void add_book_to_list(const object *book, int msgtype)
Definition: readable.c:1225
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:1399
#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
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:79
const char * object_get_value(const object *op, const char *const key)
Definition: object.c:4125
#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:472
linked_char * ingred
Definition: recipe.h:22
static const int last_readable_subtype
Definition: readable.c:559
#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:865
#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:341
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:1065
Definition: object.h:139
#define MSG_TYPE_BOOK_SPELL_PRAYER
Definition: newclient.h:408
uint32_t path_attuned
Definition: object.h:345
#define MSGTYPE_ALCHEMY
Definition: readable.c:85
int16_t sp
Definition: living.h:42
static object * get_next_mon(const object *tmp)
Definition: readable.c:1480
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:447
Definition: object.h:467
#define MSG_TYPE_PAPER_NOTE_3
Definition: newclient.h:430
const char *const spellpathnames[NRSPELLPATHS]
Definition: init.c:105
#define MSG_TYPE_CARD_STRANGE_1
Definition: newclient.h:420
struct titlestruct title
object * ob
Definition: object.h:445
#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:4248
int chance
Definition: recipe.h:14
void object_free_drop_inventory(object *ob)
Definition: object.c:1316
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:282
int nstrtok(const char *buf1, const char *buf2)
Definition: readable.c:662
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
const Face * face
Definition: readable.c:141
object * object_new(void)
Definition: object.c:1011
sstring title
Definition: recipe.h:11
const char * name_pl
Definition: object.h:315
object * create_archetype(const char *name)
Definition: arch.c:620
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:2591
int total_chance
Definition: recipe.h:38
static StringBuffer * artifact_msg(unsigned int level, size_t booksize)
Definition: readable.c:1660
int32_t weight
Definition: object.h:367
#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:1508
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:1565
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:644
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:162
void tailor_readable_ob(object *book, int msg_type)
Definition: readable.c:2052
#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:421
#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:334
archetype * try_find_archetype(const char *name)
Definition: arch.c:666
#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:983
Definition: object.h:214
#define QUERY_FLAG(xyz, p)
Definition: define.h:225
void init_readable(void)
Definition: readable.c:1033
#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:2277
static void add_author(object *op, int msgtype)
Definition: readable.c:1153
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:1982
object * object_create_arch(archetype *at)
Definition: arch.c:736
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:300
#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:1924
#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:1201
static const char *const gods_book_name[]
Definition: readable.c:319
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:247
#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:935
#define PATH_FROST
Definition: spells.h:15
static const char *const art_author[]
Definition: readable.c:268
static titlelist * get_empty_booklist(void)
Definition: readable.c:586
#define RANDOM()
Definition: define.h:681
static const readable_message_type readable_message_types[]
Definition: readable.c:495
#define FREE_AND_COPY(sv, nv)
Definition: global.h:210
int16_t grace
Definition: living.h:44
#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:248
living stats
Definition: object.h:370
#define MSG_TYPE_CARD_MONEY_2
Definition: newclient.h:424
static const char *const formula_author[]
Definition: readable.c:366
struct archt * arch
Definition: object.h:415
int type
Definition: readable.c:128
int allowed_size
Definition: artifact.h:20
struct oblnk * next
Definition: object.h:446
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:340
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:722
struct archt * next
Definition: object.h:469
#define MSGTYPE_MSGFILE
Definition: readable.c:89
#define SAVE_MODE
Definition: config.h:566
const GeneralMessage * get_message_from_identifier(const char *identifier)
Definition: readable.c:2246
#define MSG_TYPE_PAPER_LETTER_OLD_2
Definition: newclient.h:432
void free_all_readable(void)
Definition: readable.c:2134
struct recipestruct * next
Definition: recipe.h:24
static const int max_titles[6]
Definition: readable.c:562
static const char *const path_author[]
Definition: readable.c:208
void add_abilities(object *op, const object *change)
Definition: artifact.c:288
const char * msg
Definition: object.h:322
void write_book_archive(void)
Definition: readable.c:2183
sstring get_message_body(const GeneralMessage *message)
Definition: readable.c:2268
static const char *const light_book_name[]
Definition: readable.c:388
static StringBuffer * mon_desc(const object *mon)
Definition: readable.c:1462
#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:692
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:1267
#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:1001
const readable_message_type * get_readable_message_type(object *readable)
Definition: readable.c:2233
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:4559
static const char *const formula_book_name[]
Definition: readable.c:352
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
const Face * find_face(const char *name, const Face *error)
Definition: image.c:316
#define MSG_TYPE_BOOK_QUARTO_1
Definition: newclient.h:405
static const char *const book_descrpt[]
Definition: readable.c:468
#define MSGTYPE_GODS
Definition: readable.c:87
static void new_text_name(object *book, int msgtype)
Definition: readable.c:1104
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:353
#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:1745
EXTERN archetype * first_archetype
Definition: global.h:120
int32_t value
Definition: object.h:352
sstring cauldron
Definition: recipe.h:27
const char * name
Definition: object.h:468
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:366
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:606