Crossfire Server, Trunk  R20608
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  "exposition",
202  "tables",
203  "treatise"
204 };
205 
207 static const char *const path_author[] = {
208  "aether",
209  "astral byways",
210  "connections",
211  "the Grey Council",
212  "deep pathways",
213  "knowledge",
214  "magic",
215  "mystic ways",
216  "pathways",
217  "power",
218  "spells",
219  "transforms",
220  "the mystic veil",
221  "unknown spells"
222 };
223 
230 static const arttypename art_name_array[] = {
231  { "Helmet", HELMET },
232  { "Amulet", AMULET },
233  { "Shield", SHIELD },
234  { "Bracers", BRACERS },
235  { "Boots", BOOTS },
236  { "Cloak", CLOAK },
237  { "Gloves", GLOVES },
238  { "Gridle", GIRDLE },
239  { "Ring", RING },
240  { "Horn", ROD },
241  { "Missile Weapon", BOW },
242  { "Missile", ARROW },
243  { "Hand Weapon", WEAPON },
244  { "Artifact", SKILL },
245  { "Food", FOOD },
246  { "Body Armour", ARMOUR }
247 };
248 
250 static const char *const art_book_name[] = {
251  "collection",
252  "file",
253  "files",
254  "guide",
255  "handbook",
256  "index",
257  "inventory",
258  "list",
259  "listing",
260  "record",
261  "record book"
262 };
263 
265 static const char *const art_author[] = {
266  "ancient things",
267  "artifacts",
268  "Havlor", /* ancient warrior scribe :) */
269  "items",
270  "lost artifacts",
271  "the ancients",
272  "useful things"
273 };
274 
278 static const char *const mon_book_name[] = {
279  "beastuary",
280  "catalog",
281  "compilation",
282  "collection",
283  "encyclopedia",
284  "guide",
285  "handbook",
286  "list",
287  "manual",
288  "notes",
289  "record",
290  "register",
291  "volume"
292 };
293 
295 static const char *const mon_author[] = {
296  "beasts",
297  "creatures",
298  "dezidens",
299  "dwellers",
300  "evil nature",
301  "life",
302  "monsters",
303  "nature",
304  "new life",
305  "residents",
306  "the spawn",
307  "the living",
308  "things"
309 };
310 
314 static const char *const gods_book_name[] = {
315  "devotional",
316  "devout notes",
317  "divine text",
318  "divine work",
319  "holy book",
320  "holy record",
321  "illuminated text",
322  "moral text",
323  "sacred guide",
324  "testament",
325  "transcript"
326 };
327 
329 static const char *const gods_author[] = {
330  "cults",
331  "joy",
332  "lasting curse",
333  "madness",
334  "religions",
335  "the dead",
336  "the gods",
337  "the heirophant",
338  "the poor priest",
339  "the priestess",
340  "pain",
341  "white"
342 };
343 
347 static const char *const formula_book_name[] = {
348  "cookbook",
349  "formulary",
350  "lab book",
351  "lab notes",
352  "recipe book",
353  "experiment record",
354  "work plan",
355  "design notes"
356 };
357 
359 static const char *const formula_author[] = {
360  "Albertus Magnus",
361  "alchemy",
362  "balms",
363  "creation",
364  "dusts",
365  "magical manufacture",
366  "making",
367  "philosophical items",
368  "potions",
369  "powders",
370  "the cauldron",
371  "the lamp black",
372  "transmutation",
373  "waters"
374 };
375 
381 static const char *const light_book_name[] = {
382  "calendar",
383  "datebook",
384  "diary",
385  "guidebook",
386  "handbook",
387  "ledger",
388  "notes",
389  "notebook",
390  "octavo",
391  "pamphlet",
392  "practicum",
393  "script",
394  "transcript"
395 };
396 
398 static const char *const heavy_book_name[] = {
399  "catalog",
400  "compendium",
401  "guide",
402  "manual",
403  "opus",
404  "tome",
405  "treatise",
406  "volume",
407  "work"
408 };
409 
411 static const char *const book_author[] = {
412  "Abdulah",
413  "Al'hezred",
414  "Alywn",
415  "Arundel",
416  "Arvind",
417  "Aerlingas",
418  "Bacon",
419  "Baliqendii",
420  "Bosworth",
421  "Beathis",
422  "Bertil",
423  "Cauchy",
424  "Chakrabarti",
425  "der Waalis",
426  "Dirk",
427  "Djwimii",
428  "Eisenstaadt",
429  "Fendris",
430  "Frank",
431  "Habbi",
432  "Harlod",
433  "Ichibod",
434  "Janus",
435  "June",
436  "Magnuson",
437  "Nandii",
438  "Nitfeder",
439  "Norris",
440  "Parael",
441  "Penhew",
442  "Sophia",
443  "Skilly",
444  "Tahir",
445  "Thockmorton",
446  "Thomas",
447  "van Helsing",
448  "van Pelt",
449  "Voormis",
450  "Xavier",
451  "Xeno",
452  "Zardoz",
453  "Zagy"
454 };
455 
457 static const char *const book_descrpt[] = {
458  "ancient",
459  "cryptic",
460  "cryptical",
461  "dusty",
462  "hierarchical",
463  "grizzled",
464  "gold-gilt",
465  "great",
466  "lost",
467  "magnificent",
468  "musty",
469  "mythical",
470  "mystical",
471  "rustic",
472  "stained",
473  "silvered",
474  "transcendental",
475  "weathered"
476 };
477 
485  /*subtype 0 */ { 0, 0 },
486  /* book messages subtypes */
487  /*subtype 1 */ { MSG_TYPE_BOOK, MSG_TYPE_BOOK_CLASP_1 },
496  /*subtype 10 */ { MSG_TYPE_BOOK, MSG_TYPE_BOOK_SPELL_SORCERER },
498  /* card messages subtypes*/
507  /*subtype 20 */ { MSG_TYPE_CARD, MSG_TYPE_CARD_STRANGE_3 },
511  /* Paper messages subtypes */
518  /*subtype 30 */ { MSG_TYPE_PAPER, MSG_TYPE_PAPER_LETTER_NEW_2 },
526  /* road signs messages subtypes */
529  /*subtype 40 */ { MSG_TYPE_SIGN, MSG_TYPE_SIGN_DIR_RIGHT },
531  /* stones and monument messages */
545 };
546 
548 static const int last_readable_subtype = arraysize(readable_message_types);
549 
551 static const int max_titles[6] = {
553  arraysize(mon_book_name)*arraysize(mon_author), /* MSGTYPE_MONSTER */
554  arraysize(art_book_name)*arraysize(art_author), /* MSGTYPE_ARTIFACT */
555  arraysize(path_book_name)*arraysize(path_author), /* MSGTYPE_SPELLPATH */
556  arraysize(formula_book_name)*arraysize(formula_author), /* MSGTYPE_ALCHEMY */
557  arraysize(gods_book_name)*arraysize(gods_author), /* MSGTYPE_GODS */
558 };
559 
560 /******************************************************************************
561  *
562  * Start of misc. readable functions used by others functions in this file
563  *
564  *****************************************************************************/
565 
576  titlelist *bl = (titlelist *)malloc(sizeof(titlelist));
577 
578  if (bl == NULL)
580  bl->number = 0;
581  bl->first_book = NULL;
582  bl->next = NULL;
583  return bl;
584 }
585 
595 static title *get_empty_book(void) {
596  title *t = (title *)malloc(sizeof(title));
597 
598  if (t == NULL)
600  t->name = NULL;
601  t->archname = NULL;
602  t->authour = NULL;
603  t->level = 0;
604  t->size = 0;
605  t->msg_index = 0;
606  t->next = NULL;
607  return t;
608 }
609 
620 static titlelist *get_titlelist(int i) {
621  titlelist *tl;
622  int number;
623 
624  if (i < 0 || i >= (int)arraysize(max_titles)) {
625  LOG(llevInfo, "Warning: invalid book index %d, using 0 instead\n", i);
626  return booklist;
627  }
628 
629  for (tl = booklist, number = i; tl && number; tl = tl->next, number--) {
630  if (!tl->next)
631  tl->next = get_empty_booklist();
632  }
633 
634  return tl;
635 }
636 
637 /* HANDMADE STRING FUNCTIONS.., perhaps these belong in another file
638  * (shstr.c ?), but the quantity BOOK_BUF will need to be defined. */
639 
651 int nstrtok(const char *buf1, const char *buf2) {
652  char *tbuf, buf[MAX_BUF];
653  int number = 0;
654 
655  if (!buf1 || !buf2)
656  return 0;
657 
658  snprintf(buf, sizeof(buf), "%s", buf1);
659  for (tbuf = strtok(buf, buf2); tbuf; tbuf = strtok(NULL, buf2)) {
660  number++;
661  }
662  return number;
663 }
664 
681 char *strtoktolin(const char *buf1, const char *buf2, char *retbuf, size_t size) {
682  int maxi, i = nstrtok(buf1, buf2);
683  char *tbuf, buf[MAX_BUF];
684 
685  maxi = i;
686  snprintf(buf, sizeof(buf), "%s", buf1);
687  snprintf(retbuf, size, " ");
688  for (tbuf = strtok(buf, buf2); tbuf && i > 0; tbuf = strtok(NULL, buf2)) {
689  snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "%s", tbuf);
690  i--;
691  if (i == 1 && maxi > 1)
692  snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), " and ");
693  else if (i > 0 && maxi > 1)
694  snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), ", ");
695  else
696  snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), ".");
697  }
698  return retbuf;
699 }
700 
711 int book_overflow(const char *buf1, const char *buf2, size_t booksize) {
712  if (buf_overflow(buf1, buf2, BOOK_BUF-2) /* 2 less so always room for trailing \n */
713  || buf_overflow(buf1, buf2, booksize))
714  return 1;
715  return 0;
716 }
717 
718 /*****************************************************************************
719  *
720  * Start of initialization related functions.
721  *
722  ****************************************************************************/
723 
729 static void init_msgfile(void) {
730  FILE *fp;
731  char buf[MAX_BUF], msgbuf[HUGE_BUF], fname[MAX_BUF], *cp;
732  int text = 0, nrofmsg = 0;
733  static int did_init_msgfile = 0;
734 
735  if (did_init_msgfile)
736  return;
737  did_init_msgfile = 1;
738 
739  snprintf(fname, sizeof(fname), "%s/messages", settings.datadir);
740  LOG(llevDebug, "Reading messages from %s...\n", fname);
741 
742  fp = fopen(fname, "r");
743  if (fp != NULL) {
744  GeneralMessage *tmp = NULL;
745  int lineno;
746  int error_lineno;
747 
748  error_lineno = 0;
749  for (lineno = 1; fgets(buf, MAX_BUF, fp) != NULL; lineno++) {
750  if (*buf == '#')
751  continue;
752  cp = strchr(buf, '\n');
753  if (cp != NULL) {
754  // Remove trailing whitespace
755  while (cp > buf && (cp[-1] == ' ' || cp[-1] == '\t'))
756  cp--;
757  /* If we make sure there is a newline here,
758  * we can avoid the auto-append of it and make long
759  * blocks of text not get split.
760  * But only do that if we are getting the message.
761  * Everywhere else we do not want the newline.
762  * Daniel Hawkins 2018-10-24
763  */
764  if (text)
765  {
766  *cp = '\n';
767  // to have found a newline means we have room for a null terminator, too
768  *(++cp)= '\0';
769  }
770  else
771  *cp = '\0';
772  }
773  if (tmp != NULL) {
774  if (text && strncmp(buf, "ENDMSG", 6) == 0) {
775  if (strlen(msgbuf) > BOOK_BUF) {
776  LOG(llevDebug, "Warning: this string exceeded max book buf size:\n");
777  LOG(llevDebug, " %s\n", msgbuf);
778  }
779  tmp->message = add_string(msgbuf);
780  tmp->next = first_msg;
781  first_msg = tmp;
782  nrofmsg++;
783  if (tmp->identifier != NULL && tmp->title == NULL) {
784  LOG(llevError, "Error: message can't have identifier without title, on line %d\n", error_lineno);
786  }
787  tmp = NULL;
788  text = 0;
789  } else if (text) {
790  if (!buf_overflow(msgbuf, buf, HUGE_BUF-1)) {
791  strcat(msgbuf, buf);
792  // If there is a newline in the text, it will be included in the output where it belongs
793  // We should avoid really long lines of text getting split up this way.
794  } else if (error_lineno != 0) {
795  LOG(llevInfo, "Warning: truncating book at %s, line %d\n", fname, error_lineno);
796  }
797  } else if (strcmp(buf, "TEXT") == 0) {
798  text = 1;
799  } else if (strncmp(buf, "CHANCE ", 7) == 0) {
800  tmp->chance = atoi(buf + 7);
801  msg_total_chance += tmp->chance;
802  } else if (strncmp(buf, "TITLE ", 6) == 0) {
803  tmp->title = add_string(buf + 6);
804  } else if (strncmp(buf, "QUEST ", 6) == 0) {
805  tmp->quest_code = add_string(buf + 6);
806  } else if (strncmp(buf, "FACE ", 5) == 0) {
807  unsigned int face = find_face(buf + 5, (unsigned int)-1);
808  if (face != (unsigned int)-1) {
809  tmp->face = face;
810  } else {
811  LOG(llevInfo, "Warning: unknown face %s for message %s, line %d\n", buf + 5, tmp->identifier, error_lineno);
812  }
813  } else if (error_lineno != 0) {
814  LOG(llevInfo, "Warning: unknown line %s, line %d\n", buf, error_lineno);
815  }
816  } else if (strncmp(buf, "MSG", 3) == 0) {
817  error_lineno = lineno;
818  tmp = (GeneralMessage *)calloc(1, sizeof(GeneralMessage));
819  tmp->face = -1;
820  strcpy(msgbuf, " "); /* reset msgbuf for new message */
821  if (buf[3] == ' ') {
822  int i = 4;
823  while (buf[i] == ' ' && buf[i] != '\0')
824  i++;
825  if (buf[i] != '\0') {
826  tmp->identifier = add_string(buf + i);
827  if (get_message_from_identifier(buf + i)) {
828  LOG(llevError, "Duplicated message identifier %s at line %d\n", buf + i, error_lineno);
830  }
831  }
832  }
833  } else {
834  LOG(llevInfo, "Warning: syntax error at %s, line %d\n", fname, lineno);
835  }
836  }
837  fclose(fp);
838 
839  if (tmp != NULL) {
840  LOG(llevError, "Invalid file %s", fname);
842  }
843  }
844 
845  LOG(llevDebug, "done messages, found %d for total chance %d.\n", nrofmsg, msg_total_chance);
846 }
847 
854 static void init_book_archive(void) {
855  FILE *fp;
856  int nroftitle = 0;
857  char buf[MAX_BUF], fname[MAX_BUF], *cp;
858  static int did_init_barch = 0;
859 
860  if (did_init_barch)
861  return;
862  did_init_barch = 1;
863 
864  if (!booklist)
865  booklist = get_empty_booklist();
866 
867  snprintf(fname, sizeof(fname), "%s/bookarch", settings.localdir);
868  LOG(llevDebug, " Reading bookarch from %s...\n", fname);
869 
870  fp = fopen(fname, "r");
871  if (fp != NULL) {
872  int type;
873  size_t i;
874  titlelist *bl;
875  int lineno;
876  title *book;
877  int skipping;
878 
879  skipping = 0;
880  book = NULL;
881  type = -1;
882  for (lineno = 1; fgets(buf, MAX_BUF, fp) != NULL; lineno++) {
883  int len;
884  int value;
885 
886  if (*buf == '#')
887  continue;
888  cp = strchr(buf, '\n');
889  if (cp != NULL) {
890  while (cp > buf && (cp[-1] == ' ' || cp[-1] == '\t'))
891  cp--;
892  *cp = '\0';
893  }
894  cp = buf;
895  if (strncmp(buf, "title ", 6) == 0) {
896  skipping = 0;
897  cp = buf+6;
898  while (*cp == ' ' || *cp == '\t')
899  cp++;
900  if (*cp == '\0') {
901  LOG(llevInfo, "Warning: missing book title at %s, line %d\n", fname, lineno);
902  book = NULL;
903  } else {
904  book = get_empty_book(); /* init new book entry */
905  book->name = add_string(cp);
906  type = -1;
907  nroftitle++;
908  }
909  } else if (book == NULL) {
910  if (!skipping) {
911  skipping = 1;
912  LOG(llevInfo, "Warning: expecting 'title' at %s, line %d\n", fname, lineno);
913  }
914  } else if (strncmp(buf, "authour ", 8) == 0) {
915  cp = buf+8;
916  while (*cp == ' ' || *cp == '\t')
917  cp++;
918  if (*cp == '\0') {
919  LOG(llevInfo, "Warning: missing book authour at %s, line %d\n", fname, lineno);
920  } else {
921  book->authour = add_string(cp);
922  }
923  } else if (strncmp(buf, "arch ", 5) == 0) {
924  cp = buf+5;
925  while (*cp == ' ' || *cp == '\t')
926  cp++;
927  if (*cp == '\0') {
928  LOG(llevInfo, "Warning: missing book arch at %s, line %d\n", fname, lineno);
929  } else {
930  book->archname = add_string(cp);
931  }
932  } else if (sscanf(buf, "level %d%n", &value, &len) == 1 && len == (int)strlen(buf)) {
933  book->level = value;
934  } else if (sscanf(buf, "type %d%n", &value, &len) == 1 && len == (int)strlen(buf)) {
935  type = value;
936  } else if (sscanf(buf, "size %d%n", &value, &len) == 1 && len == (int)strlen(buf)) {
937  book->size = value;
938  } else if (sscanf(buf, "index %d%n", &value, &len) == 1 && len == (int)strlen(buf)) {
939  book->msg_index = value;
940  } else if (strcmp(buf, "end") == 0) { /* link it */
941  add_book(book, type, fname, lineno);
942  book = NULL;
943  type = -1;
944  } else {
945  LOG(llevInfo, "Warning: syntax error at %s, line %d\n", fname, lineno);
946  }
947  }
948  if (book != NULL) {
949  LOG(llevInfo, "Warning: missing 'end' at %s, line %d\n", fname, lineno);
950  add_book(book, type, fname, lineno);
951  }
952  LOG(llevDebug, " book archives(used/avail):\n");
953  for (bl = booklist, i = 0; bl != NULL && i < arraysize(max_titles); bl = bl->next, i++) {
954  LOG(llevDebug, "(%d/%d)\n", bl->number, max_titles[i]);
955  }
956  fclose(fp);
957  }
958 
959 #ifdef BOOK_MSG_DEBUG
960  LOG(llevDebug, "\n init_book_archive() got %d titles.\n", nroftitle);
961 #endif
962  LOG(llevDebug, " done.\n");
963 }
964 
972 static void add_book(title *book, int type, const char *fname, int lineno) {
973  titlelist *bl;
974 
975  if (type == -1) {
976  LOG(llevInfo, "Warning: book with no type at %s, line %d; using type 0\n", fname, lineno);
977  type = 0;
978  }
979 
980  bl = get_titlelist(type);
981  book->next = bl->first_book;
982  bl->first_book = book;
983  bl->number++;
984 }
985 
990 static void init_mon_info(void) {
991  archetype *at;
992  static int did_init_mon_info = 0;
993 
994  if (did_init_mon_info)
995  return;
996  did_init_mon_info = 1;
997 
998  for (at = first_archetype; at != NULL; at = at->next) {
999  if (QUERY_FLAG(&at->clone, FLAG_MONSTER)
1001  objectlink *mon = (objectlink *)malloc(sizeof(objectlink));
1002  if (!mon) {
1003  LOG(llevError, "init_mon_info: malloc failed!\n");
1004  abort();
1005  }
1006  mon->ob = &at->clone;
1007  mon->id = nrofmon;
1008  mon->next = first_mon_info;
1009  first_mon_info = mon;
1010  nrofmon++;
1011  }
1012  }
1013  LOG(llevDebug, "init_mon_info() got %d monsters\n", nrofmon);
1014 }
1015 
1022 void init_readable(void) {
1023  static int did_this = 0;
1024 
1025  if (did_this)
1026  return;
1027  did_this = 1;
1028 
1029  LOG(llevDebug, "Initializing reading data...\n");
1030  init_msgfile();
1032  init_mon_info();
1033  LOG(llevDebug, " done reading data\n");
1034 }
1035 
1036 /*****************************************************************************
1037  *
1038  * This is the start of the administrative functions when creating
1039  * new books (ie, updating title and the like)
1040  *
1041  *****************************************************************************/
1042 
1054 static title *find_title(const object *book, int msgtype) {
1055  title *t;
1056  titlelist *tl;
1057  size_t length;
1058  int index;
1059 
1060  if (msgtype < 0)
1061  return (title *)NULL;
1062 
1063  tl = get_titlelist(msgtype);
1064  if (!tl)
1065  return (title *)NULL;
1066 
1067  length = strlen(book->msg);
1068  index = strtoint(book->msg);
1069  for (t = tl->first_book; t; t = t->next)
1070  if (t->size == length && t->msg_index == index) {
1071 #ifdef ARCHIVE_DEBUG
1072  LOG(llevDebug, "Found title match (list %d): %s %s (%d)\n", msgtype, t->name, t->authour, t->msg_index);
1073 #endif
1074  return t;
1075  }
1076 
1077  return (title *)NULL;
1078 }
1079 
1093 static void new_text_name(object *book, int msgtype) {
1094  const char *name;
1095 
1096  if (book->type != BOOK)
1097  return;
1098 
1099  switch (msgtype) {
1100  case MSGTYPE_MONSTER:
1102  break;
1103 
1104  case MSGTYPE_ARTIFACT:
1106  break;
1107 
1108  case MSGTYPE_SPELLPATH:
1110  break;
1111 
1112  case MSGTYPE_ALCHEMY:
1114  break;
1115 
1116  case MSGTYPE_GODS:
1118  break;
1119 
1120  case MSGTYPE_MSGFILE:
1121  default:
1122  if (book->weight > 2000) { /* based on weight */
1124  } else {
1126  }
1127  break;
1128  }
1129  free_string(book->name);
1130  book->name = add_string(name);
1131 }
1132 
1142 static void add_author(object *op, int msgtype) {
1143  char title[MAX_BUF];
1144  const char *name;
1145 
1146  if (msgtype < 0 || strlen(op->msg) < 5)
1147  return;
1148 
1149  switch (msgtype) {
1150  case MSGTYPE_MONSTER:
1152  break;
1153 
1154  case MSGTYPE_ARTIFACT:
1156  break;
1157 
1158  case MSGTYPE_SPELLPATH:
1160  break;
1161 
1162  case MSGTYPE_ALCHEMY:
1164  break;
1165 
1166  case MSGTYPE_GODS:
1168  break;
1169 
1170  case MSGTYPE_MSGFILE:
1171  default:
1173  }
1174 
1175  snprintf(title, sizeof(title), "of %s", name);
1176  op->title = add_string(title);
1177 }
1178 
1190 static int unique_book(const object *book, int msgtype) {
1191  title *test;
1192 
1193  if (!booklist)
1194  return 1; /* No archival entries! Must be unique! */
1195 
1196  /* Go through the booklist. If the author and name match, not unique so
1197  * return 0.
1198  */
1199  for (test = get_titlelist(msgtype)->first_book; test; test = test->next) {
1200  if (!strcmp(test->name, book->name) && !strcmp(book->title, test->authour))
1201  return 0;
1202  }
1203  return 1;
1204 }
1205 
1214 static void add_book_to_list(const object *book, int msgtype) {
1215  titlelist *tl = get_titlelist(msgtype);
1216  title *t;
1217 
1218  if (!tl) {
1219  LOG(llevError, "add_book_to_list can't get booklist!\n");
1220  return;
1221  }
1222 
1223  t = get_empty_book();
1224  t->name = add_string(book->name);
1225  t->authour = add_string(book->title);
1226  t->size = strlen(book->msg);
1227  t->msg_index = strtoint(book->msg);
1228  t->archname = add_string(book->arch->name);
1229  t->level = book->level;
1230 
1231  t->next = tl->first_book;
1232  tl->first_book = t;
1233  tl->number++;
1234 
1235  /* We have stuff we need to write now */
1237 
1238 #ifdef ARCHIVE_DEBUG
1239  LOG(llevDebug, "Archiving new title: %s %s (%d)\n", book->name, book->title, msgtype);
1240 #endif
1241 }
1242 
1256 static void change_book(object *book, int msgtype) {
1257  titlelist *tl;
1258  title *t;
1259  int tries;
1260 
1261  if (book->type != BOOK) {
1262  LOG(llevError, "change_book_name() called w/ illegal obj type.\n");
1263  return;
1264  }
1265 
1266  tl = get_titlelist(msgtype);
1267  t = NULL;
1268  tries = 0;
1269 
1270  /* look to see if our msg already been archived. If so, alter
1271  * the book to match the archival text. If we fail to match,
1272  * then we archive the new title/name/msg combo if there is
1273  * room on the titlelist.
1274  */
1275 
1276  if (strlen(book->msg) > 5 && (t = find_title(book, msgtype))) {
1277  object *tmpbook;
1278  sstring marker = object_get_value(book, "knowledge_marker");
1279 
1280  /* alter book properties */
1281  tmpbook = create_archetype(t->archname);
1282  if (marker != NULL)
1283  /* need to copy the knowledge_marker */
1284  object_set_value(tmpbook, "knowledge_marker", marker, 1);
1285  object_set_msg(tmpbook, book->msg);
1286  object_copy(tmpbook, book);
1287  object_free_drop_inventory(tmpbook);
1288 
1289  book->title = add_string(t->authour);
1290  free_string(book->name);
1291  book->name = add_string(t->name);
1292  book->level = t->level;
1293  } else { /* Don't have any default title, so lets make up a new one */
1294  int numb, maxnames = max_titles[msgtype];
1295  const char *old_title;
1296  const char *old_name;
1297 
1298  old_title = book->title ? add_string(book->title) : NULL;
1299  old_name = add_string(book->name);
1300 
1301  /* some pre-generated books have title already set (from
1302  * maps), also don't bother looking for unique title if
1303  * we already used up all the available names! */
1304 
1305  if (!tl) {
1306  LOG(llevError, "change_book_name(): can't find title list\n");
1307  numb = 0;
1308  } else
1309  numb = tl->number;
1310 
1311  if (numb == maxnames) {
1312 #ifdef ARCHIVE_DEBUG
1313  LOG(llevDebug, "titles for list %d full (%d possible).\n", msgtype, maxnames);
1314 #endif
1315  if (old_title != NULL)
1316  free_string(old_title);
1317  free_string(old_name);
1318  return;
1319  }
1320  /* shouldnt change map-maker books */
1321  if (!book->title)
1322  do {
1323  /* random book name */
1324  new_text_name(book, msgtype);
1325  add_author(book, msgtype); /* random author */
1326  tries++;
1327  } while (!unique_book(book, msgtype) && tries < MAX_TITLE_CHECK);
1328 
1329  /* Now deal with 2 cases.
1330  * 1) If no space for a new title exists lets just restore
1331  * the old book properties. Remember, if the book had
1332  * matchd an older entry on the titlelist, we shouldnt
1333  * have called this routine in the first place!
1334  * 2) If we got a unique title, we need to add it to
1335  * the list.
1336  */
1337 
1338  if (tries == MAX_TITLE_CHECK) {
1339 #ifdef ARCHIVE_DEBUG
1340  LOG(llevDebug, "Failed to obtain unique title for %s %s (names:%d/%d)\n", book->name, book->title, numb, maxnames);
1341 #endif
1342  /* restore old book properties here */
1343  free_string(book->name);
1344  free_string(book->title);
1345  book->title = old_title != NULL ? add_string(old_title) : NULL;
1346 
1347  if (RANDOM()%4) {
1348  /* Lets give the book a description to individualize it some */
1349  char new_name[MAX_BUF];
1350 
1351  snprintf(new_name, MAX_BUF, "%s %s", book_descrpt[RANDOM()%arraysize(book_descrpt)], old_name);
1352  book->name = add_string(new_name);
1353  } else {
1354  book->name = add_string(old_name);
1355  }
1356  } else if (book->title && strlen(book->msg) > 5) { /* archive if long msg texts */
1357  add_book_to_list(book, msgtype);
1358  }
1359 
1360  if (old_title != NULL)
1361  free_string(old_title);
1362  free_string(old_name);
1363  }
1364 }
1365 
1366 /*****************************************************************************
1367  *
1368  * This is the start of the area that generates the actual contents
1369  * of the book.
1370  *
1371  *****************************************************************************/
1372 
1373 /*****************************************************************************
1374  * Monster msg generation code.
1375  ****************************************************************************/
1376 
1388 object *get_random_mon(int level) {
1389  objectlink *mon;
1390  int i, monnr;
1391 
1392  /* safety check. Problem w/ init_mon_info list? */
1393  if (!nrofmon || !first_mon_info)
1394  return (object *)NULL;
1395 
1396  if (!level) {
1397  /* lets get a random monster from the mon_info linked list */
1398  monnr = RANDOM()%nrofmon;
1399 
1400  for (mon = first_mon_info, i = 0; mon; mon = mon->next, i++)
1401  if (i == monnr)
1402  break;
1403 
1404  if (!mon) {
1405  LOG(llevError, "get_random_mon: Didn't find a monster when we should have\n");
1406  return NULL;
1407  }
1408  return mon->ob;
1409  }
1410 
1411  /* Case where we are searching by level. Redone 971225 to be clearer
1412  * and more random. Before, it looks like it took a random monster from
1413  * the list, and then returned the first monster after that which was
1414  * appropriate level. This wasn't very random because if you had a
1415  * bunch of low level monsters and then a high level one, if the random
1416  * determine took one of the low level ones, it would just forward to the
1417  * high level one and return that. Thus, monsters that immediately followed
1418  * a bunch of low level monsters would be more heavily returned. It also
1419  * means some of the dragons would be poorly represented, since they
1420  * are a group of high level monsters all around each other.
1421  */
1422 
1423  /* First count number of monsters meeting level criteria */
1424  for (mon = first_mon_info, i = 0; mon; mon = mon->next)
1425  if (mon->ob->level >= level)
1426  i++;
1427 
1428  if (i == 0) {
1429  LOG(llevError, "get_random_mon() couldn't return monster for level %d\n", level);
1430  return NULL;
1431  }
1432 
1433  monnr = RANDOM()%i;
1434  for (mon = first_mon_info; mon; mon = mon->next)
1435  if (mon->ob->level >= level && monnr-- == 0)
1436  return mon->ob;
1437 
1438  LOG(llevError, "get_random_mon(): didn't find a monster when we should have\n");
1439  return NULL;
1440 }
1441 
1451 static StringBuffer *mon_desc(const object *mon) {
1452  StringBuffer *desc = stringbuffer_new();
1453  stringbuffer_append_printf(desc, "\n---\n *** %s ***\n", mon->name);
1454  describe_item(mon, NULL, 0, desc);
1455  return desc;
1456 }
1457 
1469 static object *get_next_mon(const object *tmp) {
1470  objectlink *mon;
1471 
1472  for (mon = first_mon_info; mon; mon = mon->next)
1473  if (mon->ob == tmp)
1474  break;
1475 
1476  /* didn't find a match */
1477  if (!mon)
1478  return NULL;
1479  if (mon->next)
1480  return mon->next->ob;
1481  else
1482  return first_mon_info->ob;
1483 }
1484 
1497 static StringBuffer *mon_info_msg(int level, size_t booksize, object *book) {
1498  object *tmp;
1499  StringBuffer *marker = stringbuffer_new(), *desc = stringbuffer_new(), *mon = NULL;
1500  int added = 0;
1501  sstring final;
1502 
1503  /*preamble */
1504  stringbuffer_append_string(desc, "This beastiary contains:");
1505  stringbuffer_append_string(marker, "monster");
1506 
1507  /* lets print info on as many monsters as will fit in our
1508  * document.
1509  * 8-96 Had to change this a bit, otherwise there would
1510  * have been an impossibly large number of combinations
1511  * of text! (and flood out the available number of titles
1512  * in the archive in a snap!) -b.t.
1513  */
1514  for (tmp = get_random_mon(level*3); tmp; tmp = get_next_mon(tmp)) {
1515  /* monster description */
1516  mon = mon_desc(tmp);
1517 
1518  if (stringbuffer_length(desc) + stringbuffer_length(mon) >= booksize)
1519  break;
1520  added++;
1521  stringbuffer_append_printf(marker, ":%s", tmp->arch->name);
1524  mon = NULL;
1525  }
1526 
1527  if (mon != NULL) {
1529  }
1530 
1531  final = stringbuffer_finish_shared(marker);
1532  if (added)
1533  object_set_value(book, "knowledge_marker", final, 1);
1534  free_string(final);
1535 
1536  return desc;
1537 }
1538 
1539 /*****************************************************************************
1540  * Artifact msg generation code.
1541  ****************************************************************************/
1542 
1552 static StringBuffer *artifact_describe(const artifact *art, const artifactlist *al, int message, int art_name, int separator) {
1553  object *tmp;
1554  int chance;
1555  StringBuffer *desc = stringbuffer_new(), *sbuf;
1556 
1557  if (separator)
1558  stringbuffer_append_string(desc, "---\n");
1559 
1560  /* Name */
1561  if (art->allowed != NULL) {
1562  archetype *arch;
1563  linked_char *temp = art->allowed;
1564  int inv = 0, w;
1565 
1566  assert(art->allowed_size > 0);
1567  if (art->allowed_size > 1)
1568  w = 1 + RANDOM() % art->allowed_size;
1569  else
1570  w = 1;
1571 
1572  while (w > 1) {
1573  assert(temp);
1574  temp = temp->next;
1575  w--;
1576  }
1577 
1578  if (temp->name[0] == '!')
1579  inv = 1;
1580 
1582  arch = try_find_archetype(temp->name + inv);
1583  if (!arch)
1584  arch = find_archetype_by_object_name(temp->name + inv);
1585 
1586  if (!arch)
1587  LOG(llevError, "artifact_msg: missing archetype %s for artifact %s (type %d)\n", temp->name + inv, art->item->name, art->item->type);
1588  else {
1589  if (inv)
1590  stringbuffer_append_printf(desc, " A %s (excepted %s) of %s", art_name_array[art_name].name, arch->clone.name_pl, art->item->name);
1591  else
1592  stringbuffer_append_printf(desc, " A %s of %s", arch->clone.name, art->item->name);
1593  }
1594  } else { /* default name is used */
1595  /* use the base 'generic' name for our artifact */
1596  stringbuffer_append_printf(desc, " The %s of %s", art_name_array[art_name].name, art->item->name);
1597  }
1598 
1599  /* chance of finding */
1600  stringbuffer_append_string(desc, " is ");
1601  chance = 100*((float)art->chance/al->total_chance);
1602  if (chance >= 20)
1603  stringbuffer_append_string(desc, "an uncommon");
1604  else if (chance >= 10)
1605  stringbuffer_append_string(desc, "an unusual");
1606  else if (chance >= 5)
1607  stringbuffer_append_string(desc, "a rare");
1608  else
1609  stringbuffer_append_string(desc, "a very rare");
1610 
1611  /* value of artifact */
1612  stringbuffer_append_printf(desc, " item with a value that is %d times normal.\n", art->item->value);
1613 
1614  /* include the message about the artifact, if exists, and book
1615  * level is kinda high */
1616  if (message && !(strlen(art->item->msg) > BOOK_BUF))
1617  stringbuffer_append_string(desc, art->item->msg);
1618 
1619  /* properties of the artifact */
1620  tmp = object_new();
1621  add_abilities(tmp, art->item);
1622  tmp->type = al->type;
1623  SET_FLAG(tmp, FLAG_IDENTIFIED);
1624  sbuf = describe_item(tmp, NULL, 0, NULL);
1625  if (stringbuffer_length(sbuf) > 1) {
1626  stringbuffer_append_string(desc, " Properties of this artifact include:\n ");
1628  stringbuffer_append_string(desc, "\n");
1629  }
1630  free(stringbuffer_finish(sbuf));
1632 
1633  return desc;
1634 }
1635 
1647 static StringBuffer *artifact_msg(unsigned int level, size_t booksize) {
1648  const artifactlist *al;
1649  const artifact *art;
1650  int i, type, index;
1651  int book_entries = level > 5 ? RANDOM()%3+RANDOM()%3+2 : RANDOM()%level+1;
1652  StringBuffer *desc, *message = stringbuffer_new();
1653 
1654  /* values greater than 5 create msg buffers that are too big! */
1655  if (book_entries > 5)
1656  book_entries = 5;
1657 
1658  /* lets determine what kind of artifact type randomly.
1659  * Right now legal artifacts only come from those listed
1660  * in art_name_array. Also, we check to be sure an artifactlist
1661  * for that type exists!
1662  */
1663  i = 0;
1664  do {
1665  index = RANDOM()%arraysize(art_name_array);
1666  type = art_name_array[index].type;
1667  al = find_artifactlist(type);
1668  i++;
1669  } while (al == NULL && i < 10);
1670 
1671  if (i == 10) { /* Unable to find a message */
1672  stringbuffer_append_string(message, "None");
1673  return message;
1674  }
1675 
1676  /* There is no reason to start on the artifact list at the beginning. Lets
1677  * take our starting position randomly... */
1678  art = al->items;
1679  for (i = RANDOM()%level+RANDOM()%2+1; i > 0; i--) {
1680  if (art == NULL)
1681  art = al->items; /* hmm, out of stuff, loop back around */
1682  art = art->next;
1683  }
1684 
1685  /* Ok, lets print out the contents */
1686  stringbuffer_append_printf(message, "Herein %s detailed %s...\n", book_entries > 1 ? "are" : "is", book_entries > 1 ? "some artifacts" : "an artifact");
1687 
1688  i = 0;
1689  /* artifact msg attributes loop. Lets keep adding entries to the 'book'
1690  * as long as we have space up to the allowed max # (book_entires)
1691  */
1692  while (book_entries > 0) {
1693  int with_message;
1694  if (art == NULL)
1695  art = al->items;
1696  with_message = (art->item->msg && RANDOM()%4+1 < level) ? 1 : 0;
1697 
1698  desc = artifact_describe(art, al, with_message, index, i++);
1699 
1700  if (stringbuffer_length(message) + stringbuffer_length(desc) >= booksize) {
1701  stringbuffer_delete(desc);
1702  break;
1703  }
1704 
1705  stringbuffer_append_stringbuffer(message, desc);
1706  stringbuffer_delete(desc);
1707 
1708  art = art->next;
1709  book_entries--;
1710  }
1711 
1712  return message;
1713 }
1714 
1715 /*****************************************************************************
1716  * Spellpath message generation
1717  *****************************************************************************/
1718 
1732 static StringBuffer *spellpath_msg(int level, size_t booksize, StringBuffer *buf) {
1733  int path = RANDOM()%NRSPELLPATHS, prayers = RANDOM()%2;
1734  int did_first_sp = 0;
1735  uint32_t pnum = spellpathdef[path];
1736  archetype *at;
1737 
1738  if (buf == NULL) {
1739  buf = stringbuffer_new();
1740  /* Preamble */
1741  stringbuffer_append_printf(buf, "Herein are detailed the names of %s", prayers ? "prayers" : "incantations");
1742  stringbuffer_append_printf(buf, " belonging to the path of %s:\n ", spellpathnames[path]);
1743  }
1744 
1745  for (at = first_archetype; at != NULL; at = at->next) {
1746  /* Determine if this is an appropriate spell. Must
1747  * be of matching path, must be of appropriate type (prayer
1748  * or not), and must be within the valid level range.
1749  */
1750  if (at->clone.type == SPELL
1751  && at->clone.path_attuned&pnum
1752  && ((at->clone.stats.grace && prayers) || (at->clone.stats.sp && !prayers))
1753  && at->clone.level < level*8) {
1754  if (strlen(at->clone.name) + stringbuffer_length(buf) >= booksize)
1755  break;
1756 
1757  if (did_first_sp)
1758  stringbuffer_append_string(buf, ",\n ");
1759  did_first_sp = 1;
1761  }
1762  }
1763 
1764  /* Geez, no spells were generated. */
1765  if (!did_first_sp) {
1766  if (RANDOM()%4) { /* usually, lets make a recursive call... */
1767  return spellpath_msg(level, booksize, buf);
1768  }
1769  /* give up, cause knowing no spells exist for path is info too. need the header too. */
1770  stringbuffer_append_string(buf, "- no known spells exist -\n");
1771  }
1772  return buf;
1773 }
1774 
1783 static void make_formula_book(object *book, int level) {
1784  recipelist *fl;
1785  recipe *formula;
1786  int chance, count = 0;
1787  const char *op_name;
1788  archetype *at;
1789  StringBuffer *text, *title;
1790  char *final, km[MAX_BUF];
1791 
1792  /* the higher the book level, the more complex (ie number of
1793  * ingredients) the formula can be.
1794  */
1795  fl = get_formulalist((RANDOM()%level)/3+1);
1796  if (!fl)
1797  fl = get_formulalist(1); /* safety */
1798 
1799  if (fl->total_chance == 0) {
1800  object_set_msg(book, " <indecipherable text>\n");
1802  add_author(book, MSGTYPE_ALCHEMY);
1803  return;
1804  }
1805 
1806  /* get a random formula, weighted by its bookchance */
1807  chance = RANDOM()%fl->total_chance;
1808  for (formula = fl->items; formula != NULL; formula = formula->next) {
1809  chance -= formula->chance;
1810  if (chance <= 0 && formula->chance != 0 && !formula->is_combination)
1811  break;
1812  }
1813 
1814  if (!formula || formula->arch_names <= 0) {
1815  object_set_msg(book, " <indecipherable text>\n");
1817  add_author(book, MSGTYPE_ALCHEMY);
1818  return;
1819  }
1820 
1821  /* looks like a formula was found. Base the amount
1822  * of information on the booklevel and the spellevel
1823  * of the formula. */
1824 
1825  op_name = formula->arch_name[RANDOM()%formula->arch_names];
1826  at = find_archetype(op_name);
1827  if (at == (archetype *)NULL) {
1828  LOG(llevError, "formula_msg() can't find arch %s for formula.\n", op_name);
1829  object_set_msg(book, " <indecipherable text>\n");
1831  add_author(book, MSGTYPE_ALCHEMY);
1832  return;
1833  }
1834  op_name = at->clone.name;
1835 
1836  text = stringbuffer_new();
1837  title = stringbuffer_new();
1838 
1839  /* preamble */
1840  stringbuffer_append_printf(text, "Herein is described a project using %s:\n", formula->skill ? formula->skill : "an unknown skill");
1841 
1842  /* item name */
1843  if (strcmp(formula->title, "NONE")) {
1844  stringbuffer_append_printf(text, "The %s of %s", op_name, formula->title);
1845  /* This results in things like pile of philo. sulfur.
1846  * while philo. sulfur may look better, without this,
1847  * you get things like 'the wise' because its missing the
1848  * water of section.
1849  */
1850  stringbuffer_append_printf(title, "%s: %s of %s", formula_book_name[RANDOM()%arraysize(formula_book_name)], op_name, formula->title);
1851  } else {
1852  stringbuffer_append_printf(text, "The %s", op_name);
1854  if (at->clone.title) {
1855  stringbuffer_append_printf(text, " %s", at->clone.title);
1856  stringbuffer_append_printf(title, " %s", at->clone.title);
1857  }
1858  }
1859  /* Lets name the book something meaningful ! */
1860  if (book->name)
1861  free_string(book->name);
1862  book->name = stringbuffer_finish_shared(title);
1863  if (book->title) {
1864  free_string(book->title);
1865  book->title = NULL;
1866  }
1867 
1868  /* ingredients to make it */
1869  if (formula->ingred != NULL) {
1870  linked_char *next;
1871  archetype *at;
1872  char name[MAX_BUF];
1873 
1874  at = find_archetype(formula->cauldron);
1875  if (at)
1876  query_name(&at->clone, name, MAX_BUF);
1877  else
1878  snprintf(name, sizeof(name), "an unknown place");
1879 
1880  stringbuffer_append_printf(text, " may be made at %s using the following ingredients:\n", name);
1881 
1882  for (next = formula->ingred; next != NULL; next = next->next) {
1883  count++;
1884  stringbuffer_append_printf(text, "%s\n", next->name);
1885  }
1886  } else {
1887  LOG(llevError, "formula_msg() no ingredient list for object %s of %s\n", op_name, formula->title);
1888  stringbuffer_append_string(text, "\n");
1889  }
1890 
1891  final = stringbuffer_finish(text);
1892  object_set_msg(book, final);
1893  free(final);
1894 
1897  snprintf(km, sizeof(km), "alchemy:%d:%d:%s", count, formula->index, formula->title);
1898  object_set_value(book, "knowledge_marker", km, 1);
1899 }
1900 
1911 static StringBuffer *msgfile_msg(object *book, size_t booksize) {
1912  int weight;
1913  GeneralMessage *msg = NULL;
1914  StringBuffer *ret = stringbuffer_new();
1915 
1916  /* get a random message for the 'book' from linked list */
1917  if (msg_total_chance > 0) {
1918  assert(first_msg != NULL);
1919  msg = first_msg;
1920  weight = (RANDOM() % msg_total_chance);
1921  while (msg) {
1922  weight -= msg->chance;
1923  if (weight < 0)
1924  break;
1925  msg = msg->next;
1926  }
1927  /* if msg is NULL, then something is really wrong in the computation! */
1928  assert(msg != NULL);
1929  }
1930 
1931  if (msg && strlen(msg->message) <= booksize) {
1933  if (msg->identifier != NULL) {
1934  char km[HUGE_BUF];
1937  snprintf(km, sizeof(km), "message:%s", msg->identifier);
1938  object_set_value(book, "knowledge_marker", km, 1);
1939  }
1940  if (msg->quest_code) {
1941  /* add a 'apply' hook to launch the quest */
1942  object *event = object_create_arch(find_archetype("quest_advance_apply"));
1943  FREE_AND_COPY(event->name, msg->quest_code);
1944  object_insert_in_ob(event, book);
1945  }
1946  } else
1947  stringbuffer_append_string(ret, "\n <undecipherable text>");
1948 
1949  return ret;
1950 }
1951 
1966 static StringBuffer *god_info_msg(int level, size_t booksize, object *book) {
1967  int what = 0;
1968  const object *god = pntr_to_god_obj(get_rand_god());
1969  StringBuffer *desc = NULL;
1970 
1971  if (!god)
1972  return NULL; /* oops, problems... */
1973 
1974  if (booksize > BOOK_BUF) {
1975  LOG(llevError, "common/readable.c:god_info_msg() - passed in booksize (%lu) is larger than book buffer (%d)\n", (unsigned long)booksize, BOOK_BUF);
1976  booksize = BOOK_BUF;
1977  }
1978 
1979  if (level >= 2 && RANDOM()%2) {
1980  what |= GOD_ENEMY;
1981  }
1982  if (level >= 3 && RANDOM()%2) {
1983  what |= GOD_HOLYWORD;
1984  }
1985  if (level >= 4 && RANDOM()%2) {
1986  what |= GOD_RESISTANCES;
1987  }
1988  if (level >= 5 && RANDOM()%2) {
1989  what |= GOD_SACRED;
1990  }
1991  if (level >= 6 && RANDOM()%2) {
1992  what |= GOD_BLESSED;
1993  }
1994  if (level >= 8 && RANDOM()%2) {
1995  what |= GOD_IMMUNITIES;
1996  }
1997  if (level >= 12 && RANDOM()%2) {
1998  what |= GOD_PATHS;
1999  }
2000 
2001  desc = stringbuffer_new();
2002  what = describe_god(god, what, desc, booksize);
2003 
2004  /* check to be sure new buffer size dont exceed either
2005  * the maximum buffer size, or the 'natural' size of the
2006  * book... */
2007  if (stringbuffer_length(desc) > 1 && stringbuffer_length(desc) <= booksize) {
2008  char buf[BOOK_BUF];
2009  snprintf(buf, sizeof(buf), "god:%s:%d", god->name, what);
2010  object_set_value(book, "knowledge_marker", buf, 1);
2011  return desc;
2012  }
2013 
2014  stringbuffer_delete(desc);
2015  return NULL;
2016 }
2017 
2036 void tailor_readable_ob(object *book, int msg_type) {
2037  int level = book->level ? RANDOM()%book->level+1 : 1;
2038  size_t book_buf_size;
2039  StringBuffer *message = NULL;
2040 
2041  /* safety */
2042  if (book->type != BOOK)
2043  return;
2044 
2045  if (level <= 0)
2046  return; /* if no level no point in doing any more... */
2047 
2048  /* Max text length this book can have. */
2049  book_buf_size = BOOKSIZE(book);
2050  book_buf_size -= strlen("\n"); /* Keep enough for final \n. */
2051  assert(book_buf_size < BOOK_BUF);
2052 
2053  /* &&& The message switch &&& */
2054  /* Below all of the possible types of messages in the "book"s.
2055  */
2056  /*
2057  * IF you add a new type of book msg, you will have to do several things.
2058  * 1) make sure there is an entry in the msg switch below!
2059  * 2) make sure there is an entry in max_titles[] array.
2060  * 3) make sure there are entries for your case in new_text_title()
2061  * and add_authour().
2062  * 4) you may want separate authour/book name arrays in read.h
2063  */
2064 
2065  if (msg_type >= (int)arraysize(max_titles))
2066  msg_type = 0;
2067 
2068  msg_type = msg_type > 0 ? msg_type : RANDOM()%6;
2069  switch (msg_type) {
2070  case MSGTYPE_MONSTER:
2071  message = mon_info_msg(level, book_buf_size, book);
2072  break;
2073 
2074  case MSGTYPE_ARTIFACT:
2075  message = artifact_msg(level, book_buf_size);
2076  break;
2077 
2078  case MSGTYPE_SPELLPATH: /* grouping incantations/prayers by path */
2079  message = spellpath_msg(level, book_buf_size, NULL);
2080  break;
2081 
2082  case MSGTYPE_ALCHEMY: /* describe an alchemy formula */
2083  make_formula_book(book, level);
2084  /* make_formula_book already gives title */
2085  return;
2086  break;
2087 
2088  case MSGTYPE_GODS: /* bits of information about a god */
2089  message = god_info_msg(level, book_buf_size, book);
2090  break;
2091 
2092  case MSGTYPE_LIB: /* use info list in lib/ */
2093  default:
2094  message = msgfile_msg(book, book_buf_size);
2095  break;
2096  }
2097 
2098  if (message != NULL) {
2099  char *final;
2100  stringbuffer_append_string(message, "\n");
2101  final = stringbuffer_finish(message);
2102  object_set_msg(book, final);
2103  free(final);
2104  /* lets give the "book" a new name, which may be a compound word */
2105  change_book(book, msg_type);
2106  }
2107 }
2108 
2109 /*****************************************************************************
2110  *
2111  * Cleanup routine for readable stuff.
2112  *
2113  *****************************************************************************/
2114 
2118 void free_all_readable(void) {
2119  titlelist *tlist, *tnext;
2120  title *title1, *titlenext;
2121  GeneralMessage *lmsg, *nextmsg;
2122  objectlink *monlink, *nextmon;
2123 
2124  LOG(llevDebug, "freeing all book information\n");
2125 
2126  for (tlist = booklist; tlist != NULL; tlist = tnext) {
2127  tnext = tlist->next;
2128  for (title1 = tlist->first_book; title1; title1 = titlenext) {
2129  titlenext = title1->next;
2130  if (title1->name)
2131  free_string(title1->name);
2132  if (title1->authour)
2133  free_string(title1->authour);
2134  if (title1->archname)
2135  free_string(title1->archname);
2136  free(title1);
2137  }
2138  free(tlist);
2139  }
2140  for (lmsg = first_msg; lmsg; lmsg = nextmsg) {
2141  nextmsg = lmsg->next;
2142  if (lmsg->identifier)
2143  free_string(lmsg->identifier);
2144  if (lmsg->title)
2145  free_string(lmsg->title);
2146  if (lmsg->message)
2147  free_string(lmsg->message);
2148  if (lmsg->quest_code)
2149  free_string(lmsg->quest_code);
2150  free(lmsg);
2151  }
2152  for (monlink = first_mon_info; monlink; monlink = nextmon) {
2153  nextmon = monlink->next;
2154  free(monlink);
2155  }
2156 }
2157 
2158 /*****************************************************************************
2159  *
2160  * Writeback routine for updating the bookarchive.
2161  *
2162  ****************************************************************************/
2163 
2168  FILE *fp;
2169  OutputFile of;
2170  int index;
2171  char fname[MAX_BUF];
2172  title *book;
2173  titlelist *bl;
2174 
2175  /* If nothing changed, don't write anything */
2177  return;
2178 
2179  snprintf(fname, sizeof(fname), "%s/bookarch", settings.localdir);
2180  LOG(llevDebug, "Updating book archive: %s...\n", fname);
2181 
2182  fp = of_open(&of, fname);
2183  if (fp == NULL)
2184  return;
2185 
2186  for (bl = get_titlelist(0), index = 0; bl; bl = bl->next, index++) {
2187  for (book = bl->first_book; book; book = book->next)
2188  if (book && book->authour) {
2189  fprintf(fp, "title %s\n", book->name);
2190  fprintf(fp, "authour %s\n", book->authour);
2191  fprintf(fp, "arch %s\n", book->archname);
2192  fprintf(fp, "level %d\n", book->level);
2193  fprintf(fp, "type %d\n", index);
2194  /* C89 doesn't have %zu... */
2195  fprintf(fp, "size %lu\n", (unsigned long)book->size);
2196  fprintf(fp, "index %d\n", book->msg_index);
2197  fprintf(fp, "end\n");
2198  }
2199  }
2200  if (!of_close(&of))
2201  return;
2202 
2203  if (chmod(fname, SAVE_MODE) != 0) {
2204  LOG(llevError, "Could not set permissions on '%s'\n", fname);
2205  }
2206 
2208 }
2209 
2218  uint8_t subtype = readable->subtype;
2219 
2220  if (subtype > last_readable_subtype)
2221  return &readable_message_types[0];
2222  return &readable_message_types[subtype];
2223 }
2224 
2230 const GeneralMessage *get_message_from_identifier(const char *identifier) {
2231  GeneralMessage *msg = first_msg;
2232  while (msg && ((msg->identifier == 0) || (strcmp(msg->identifier, identifier) != 0)))
2233  msg = msg->next;
2234 
2235  return msg;
2236 }
2237 
2244  return message->title;
2245 }
2246 
2253  return message->message;
2254 }
2255 
2261 unsigned int get_message_face(const GeneralMessage *message) {
2262  return message->face;
2263 }
Error, serious thing.
Definition: logger.h:11
#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[]
Name for big books.
Definition: readable.c:398
archetype * find_archetype(const char *name)
Finds, using the hashtable, which archetype matches the given name.
Definition: arch.c:692
uint8_t type
Object type that this list represents.
Definition: artifact.h:27
List of recipes with a certain number of ingredients.
Definition: recipe.h:37
#define GOD_BLESSED
Write various information (resistances?).
Definition: god.h:28
Spell-related defines: spellpath, subtypes, ...
Information.
Definition: logger.h:12
See Ring.
Definition: object.h:185
static const uint32_t spellpathdef[NRSPELLPATHS]
Spellpath information.
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
Artifact-related information.
Definition: readable.c:81
Used to link together several objects.
Definition: object.h:442
#define MSG_TYPE_CARD_ELEGANT_1
Definition: newclient.h:417
static void init_msgfile(void)
If not called before, initialize the info list.
Definition: readable.c:729
#define PATH_MIND
Definition: spells.h:23
#define SET_FLAG(xyz, p)
Definition: define.h:223
See Bracers.
Definition: object.h:217
sstring get_message_title(const GeneralMessage *message)
Get a message's title.
Definition: readable.c:2243
static void make_formula_book(object *book, int level)
Generate a message detailing the properties of a randomly selected alchemical formula.
Definition: readable.c:1783
const artifactlist * find_artifactlist(int type)
Searches the artifact lists and returns one that has the same type of objects on it.
Definition: artifact.c:647
static titlelist * get_titlelist(int i)
Gets the ith titlelist.
Definition: readable.c:620
static void add_book_to_list(const object *book, int msgtype)
Adds a book to the list of existing books.
Definition: readable.c:1214
unsigned char uint8_t
Definition: win32.h:161
#define PATH_TURNING
Definition: spells.h:29
void stringbuffer_append_stringbuffer(StringBuffer *sb, const StringBuffer *sb2)
Append the contents of a string buffer instance to another string buffer instance.
Definition: stringbuffer.c:131
const char * archname
the archetype name of the book
Definition: readable.c:109
#define MSG_TYPE_CARD_MONEY_1
Definition: newclient.h:423
object * get_random_mon(int level)
Returns a random monster selected from linked list of all monsters in the current game...
Definition: readable.c:1388
#define MSG_TYPE_CARD_MONEY_3
Definition: newclient.h:425
See Cloak.
Definition: object.h:204
See Food.
Definition: object.h:112
#define PATH_LIGHT
Definition: spells.h:32
Information on one title.
Definition: readable.c:106
See Projectile.
Definition: object.h:117
sstring title
The message's title, only used for knowledge.
Definition: readable.c:138
sstring stringbuffer_finish_shared(StringBuffer *sb)
Deallocate the string buffer instance and return the string as a shared string.
Definition: stringbuffer.c:85
void fatal(enum fatal_error err)
fatal() is meant to be called whenever a fatal signal is intercepted.
Definition: utils.c:596
struct artifactstruct * items
Artifacts for this type.
Definition: artifact.h:30
object * mon
Definition: comet_perf.c:74
static const arttypename art_name_array[]
Artiface/item information.
Definition: readable.c:230
#define MSG_TYPE_CARD_SIMPLE_2
Definition: newclient.h:415
StringBuffer * stringbuffer_new(void)
Create a new string buffer.
Definition: stringbuffer.c:57
void free_string(sstring str)
This will reduce the refcount, and if it has reached 0, str will be freed.
Definition: shstr.c:280
#define MSG_TYPE_MONUMENT_STATUE_3
Definition: newclient.h:456
#define HUGE_BUF
Used for messages - some can be quite long.
Definition: define.h:37
unsigned int get_message_face(const GeneralMessage *message)
Get a message's face.
Definition: readable.c:2261
#define MSGTYPE_MONSTER
Monster-related information.
Definition: readable.c:79
const char * object_get_value(const object *op, const char *const key)
Get an extra value by key.
Definition: object.c:4246
#define MSG_TYPE_MONUMENT_WALL_2
Definition: newclient.h:461
#define MSG_TYPE_PAPER_NOTE_2
Definition: newclient.h:429
object clone
An object from which to do object_copy()
Definition: object.h:470
linked_char * ingred
List of ingredients.
Definition: recipe.h:22
static const int last_readable_subtype
Number of elements in readable_message_types.
Definition: readable.c:548
#define MSG_TYPE_SIGN_DIR_RIGHT
Definition: newclient.h:446
static const char *const path_book_name[]
Book names for path information.
Definition: readable.c:198
See Weapon.
Definition: object.h:119
static void init_book_archive(void)
If not called before, initialize the info list.
Definition: readable.c:854
#define MSG_TYPE_PAPER_ENVELOPE_1
Definition: newclient.h:435
See Helmet.
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
Total chance of messages (GeneralMessage), to randomly select one.
Definition: readable.c:169
#define PATH_RESTORE
Definition: spells.h:21
#define GOD_HOLYWORD
Write holy word information.
Definition: god.h:25
int msg_index
an index value derived from book message
Definition: readable.c:112
uint8_t subtype
Subtype of object.
Definition: object.h:339
See Rod.
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
the name of the book
Definition: readable.c:107
#define MSG_TYPE_MONUMENT_STONE_1
Definition: newclient.h:451
See Girdle.
Definition: object.h:223
#define MSG_TYPE_MONUMENT_STONE_3
Definition: newclient.h:453
int is_combination
Whather this is an alchemy recipe, or an item transformation description.
Definition: recipe.h:31
static title * find_title(const object *book, int msgtype)
Search the titlelist (based on msgtype) to see if book matches something already there.
Definition: readable.c:1054
See Amulet.
Definition: object.h:139
#define MSG_TYPE_BOOK_SPELL_PRAYER
Definition: newclient.h:408
uint32_t path_attuned
Paths the object is attuned to.
Definition: object.h:343
#define MSGTYPE_ALCHEMY
Alchemy-related information.
Definition: readable.c:85
int16_t sp
Spell points.
Definition: living.h:41
static object * get_next_mon(const object *tmp)
This function returns the next monster after 'tmp' in the monster list.
Definition: readable.c:1469
sstring identifier
Message identifier, can be NULL.
Definition: readable.c:137
#define MSG_TYPE_BOOK_SPELL_PYRO
Definition: newclient.h:409
Global type definitions and header inclusions.
See Boots.
Definition: object.h:212
#define MSG_TYPE_BOOK_ELEGANT_1
Definition: newclient.h:403
tag_t id
ob's tag, in case it is removed.
Definition: object.h:445
The archetype structure is a set of rules on how to generate and manipulate objects which point to ar...
Definition: object.h:465
#define MSG_TYPE_PAPER_NOTE_3
Definition: newclient.h:430
const char *const spellpathnames[NRSPELLPATHS]
Perhaps not the best place for this, but needs to be in some file in the common area so that standalo...
Definition: init.c:120
#define MSG_TYPE_CARD_STRANGE_1
Definition: newclient.h:420
struct titlestruct title
Information on one title.
object * ob
Item to link to.
Definition: object.h:443
unsigned int face
Face the message displays at in the knowledge dialog, -1 if no face defined.
Definition: readable.c:141
#define GOD_RESISTANCES
Write resistances.
Definition: god.h:26
int object_set_value(object *op, const char *key, const char *value, int add_key)
Updates the key in op to value.
Definition: object.c:4375
int chance
Chance that recipe for this item will appear in an alchemical grimore.
Definition: recipe.h:14
void object_free_drop_inventory(object *ob)
Frees everything allocated by an object, removes it from the list of used objects, and puts it on the list of free objects.
Definition: object.c:1368
const char * title
Of foo, etc.
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[]
Monster book information.
Definition: readable.c:278
int nstrtok(const char *buf1, const char *buf2)
Simple routine to return the number of list items in buf1 as separated by the value of buf2...
Definition: readable.c:651
int buf_overflow(const char *buf1, const char *buf2, size_t bufsize)
We don't want to exceed the buffer size of buf1 by adding on buf2!
Definition: shstr.c:398
#define MSG_TYPE_PAPER_SCROLL_OLD_2
Definition: newclient.h:438
struct titlestruct * next
next item in the list
Definition: readable.c:113
See Shooting Weapon.
Definition: object.h:118
const char * name
generic name to call artifacts of this type
Definition: readable.c:127
struct titleliststruct titlelist
Titles for one message type.
uint16_t total_chance
Sum of chance for are artifacts on this list.
Definition: artifact.h:28
See Book.
Definition: object.h:114
#define MSG_TYPE_CARD_SIMPLE_3
Definition: newclient.h:416
object * object_new(void)
Grabs an object from the list of unused objects, makes sure it is initialised, and returns it...
Definition: object.c:1037
sstring title
Distinguishing name of product.
Definition: recipe.h:11
const char * name_pl
The plural name of the object.
Definition: object.h:315
object * create_archetype(const char *name)
Finds which archetype matches the given name, and returns a new object containing a copy of the arche...
Definition: arch.c:617
void stringbuffer_append_string(StringBuffer *sb, const char *str)
Append a string to a string buffer instance.
Definition: stringbuffer.c:95
char ** arch_name
Possible archetypes of the final product made.
Definition: recipe.h:13
object * object_insert_in_ob(object *op, object *where)
This function inserts the object op in the linked list inside the object environment.
Definition: object.c:2690
int total_chance
Total chance of the recipes in this list.
Definition: recipe.h:38
static StringBuffer * artifact_msg(unsigned int level, size_t booksize)
Generate a message detailing the properties of 1-6 artifacts drawn sequentially from the artifact lis...
Definition: readable.c:1647
int32_t weight
Attributes of the object.
Definition: object.h:365
#define MSG_TYPE_BOOK_ELEGANT_2
Definition: newclient.h:404
#define FLAG_UNAGGRESSIVE
Monster doesn't attack players.
Definition: define.h:272
static StringBuffer * mon_info_msg(int level, size_t booksize, object *book)
Generate a message detailing the properties of randomly monster(s), and add relevant knowledge marker...
Definition: readable.c:1497
struct namebytype arttypename
special structure, used only by art_name_array[]
#define MSG_TYPE_PAPER_SCROLL_MAGIC
Definition: newclient.h:441
#define BOOK_BUF
Maximum message buf size for books.
Definition: book.h:16
#define snprintf
Definition: win32.h:46
#define MSG_TYPE_MONUMENT_WALL_3
Definition: newclient.h:462
#define GOD_IMMUNITIES
Write immunities.
Definition: god.h:29
static StringBuffer * artifact_describe(const artifact *art, const artifactlist *al, int message, int art_name, int separator)
Describe an artifact.
Definition: readable.c:1552
struct linked_char * next
Definition: global.h:88
#define FLAG_IDENTIFIED
Player knows full info about item.
Definition: define.h:261
#define PATH_TRANSMUTE
Definition: spells.h:27
#define MSG_TYPE_CARD_SIMPLE_1
Definition: newclient.h:414
GeneralMessage * next
Next message in the list.
Definition: readable.c:142
int strtoint(const char *buf)
Convert buf into an integer equal to the coadded sum of the (lowercase) character.
Definition: recipe.c:622
godlink * get_rand_god(void)
Returns a random god.
Definition: holy.c:101
int of_close(OutputFile *of)
Closes an output file.
Definition: output_file.c:61
static int need_to_write_bookarchive
If set then we have information to save.
Definition: readable.c:157
#define PATH_DEATH
Definition: spells.h:31
const char * name
The name of the object, obviously...
Definition: object.h:311
#define FLAG_CHANGING
Changes to other_arch when anim is done.
Definition: define.h:263
linked_char * allowed
List of archetypes the artifact can affect.
Definition: artifact.h:19
struct titleliststruct * next
pointer to next book list
Definition: readable.c:122
unsigned int level
level of difficulty of this message
Definition: readable.c:110
special structure, used only by art_name_array[]
Definition: readable.c:126
#define MSG_TYPE_CARD_STRANGE_3
Definition: newclient.h:422
archetype * find_archetype_by_object_name(const char *name)
This function retrieves an archetype given the name that appears during the game (for example...
Definition: arch.c:57
size_t stringbuffer_length(StringBuffer *sb)
Return the current length of the buffer.
Definition: stringbuffer.c:154
int chance
Relative chance of the message appearing randomly.
Definition: readable.c:135
void tailor_readable_ob(object *book, int msg_type)
The main routine.
Definition: readable.c:2036
Struct to store the message_type and message_subtype for signs and books used by the player...
Definition: book.h:36
#define PATH_PROT
Definition: spells.h:13
const object * pntr_to_god_obj(godlink *godlnk)
Returns a pointer to the object We need to be VERY careful about using this, as we are returning a po...
Definition: holy.c:123
static const char *const book_author[]
Used by 'generic' books.
Definition: readable.c:411
#define arraysize(arrayname)
Returns the element size of an array.
Definition: readable.c:96
This represents all archetypes for one particular object type.
Definition: artifact.h:26
int describe_god(const object *god, int what, StringBuffer *buf, size_t maxlen)
Describe a god.
Definition: holy.c:137
static const char *const gods_author[]
Used by gods texts.
Definition: readable.c:329
archetype * try_find_archetype(const char *name)
Finds, using the hashtable, which archetype matches the given name.
Definition: arch.c:663
#define PATH_SELF
Definition: spells.h:18
static int nrofmon
Number of monsters in the first_mon_info list.
Definition: readable.c:156
static titlelist * booklist
Buffer of books read in from the bookarch file.
Definition: readable.c:151
sstring skill
Skill name used to make this recipe.
Definition: recipe.h:26
static void add_book(title *book, int type, const char *fname, int lineno)
Appends a book to the booklist.
Definition: readable.c:972
See Spell.
Definition: object.h:214
#define QUERY_FLAG(xyz, p)
Definition: define.h:225
Titles for one message type.
Definition: readable.c:119
void init_readable(void)
Initialize linked lists utilized by message functions in tailor_readable_ob()
Definition: readable.c:1022
#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)
A lot like new_text_name() above, but instead chooses an author and sets op->title to that value...
Definition: readable.c:1142
int index
Index value derived from formula ingredients.
Definition: recipe.h:18
sstring quest_code
Optional quest code and state this message will start.
Definition: readable.c:140
#define MAX_BUF
Used for all kinds of things.
Definition: define.h:35
#define NRSPELLPATHS
Number of spell paths.
Definition: spells.h:40
This is one artifact, ie one special item.
Definition: artifact.h:14
static StringBuffer * god_info_msg(int level, size_t booksize, object *book)
Generate a message detailing the properties of a random god.
Definition: readable.c:1966
object * object_create_arch(archetype *at)
Create a full object using the given archetype.
Definition: arch.c:733
static GeneralMessage * first_msg
First message from data read from the messages file.
Definition: readable.c:164
#define MSG_TYPE_CARD
Definition: newclient.h:371
#define MSGTYPE_SPELLPATH
Spellpath-related information.
Definition: readable.c:83
static const char *const mon_author[]
Used by monster beastuary texts.
Definition: readable.c:295
#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)
Generate a message drawn randomly from lib/messages.
Definition: readable.c:1911
#define PATH_CREATE
Definition: spells.h:24
#define BOOKSIZE(xyz)
Get the book buffer size for an individual book object.
Definition: book.h:31
#define MSG_TYPE_PAPER_LETTER_NEW_2
Definition: newclient.h:434
static int unique_book(const object *book, int msgtype)
Check to see if the book title/msg is unique.
Definition: readable.c:1190
static const char *const gods_book_name[]
God book information.
Definition: readable.c:314
const char * sstring
Strings that should be manipulated through add_string() and free_string().
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
Read only data files.
Definition: global.h:242
#define MSG_TYPE_BOOK_CLASP_1
Definition: newclient.h:401
See Shield.
Definition: object.h:135
const char * authour
the name of the book 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)
Describes an item, in all its details.
Definition: item.c:981
#define PATH_FROST
Definition: spells.h:15
static const char *const art_author[]
Used by artifact texts.
Definition: readable.c:265
static titlelist * get_empty_booklist(void)
Creates a titlelist.
Definition: readable.c:575
#define RANDOM()
Definition: define.h:679
static const readable_message_type readable_message_types[]
Each line of this array is a readable subtype.
Definition: readable.c:484
#define FREE_AND_COPY(sv, nv)
Release the shared string if not NULL, and make it a reference to nv.
Definition: global.h:211
int16_t grace
Grace.
Definition: living.h:43
#define MSG_TYPE_PAPER_SCROLL_NEW_2
Definition: newclient.h:440
Also see SKILL_TOOL (74) below.
Definition: object.h:143
#define GOD_ENEMY
Write down god's 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
Read/write data files.
Definition: global.h:243
living stats
Str, Con, Dex, etc.
Definition: object.h:368
#define MSG_TYPE_CARD_MONEY_2
Definition: newclient.h:424
static const char *const formula_author[]
This isn't used except for empty books.
Definition: readable.c:359
struct archt * arch
Pointer to archetype.
Definition: object.h:412
int type
matching type
Definition: readable.c:128
int allowed_size
Length of allowed, for faster computation.
Definition: artifact.h:20
struct oblnk * next
Next item to link to.
Definition: object.h:444
size_t arch_names
Size of the arch_name[] array.
Definition: recipe.h:12
#define MSG_TYPE_SIGN_MAGIC_MOUTH
Definition: newclient.h:448
Only for debugging purposes.
Definition: logger.h:13
void stringbuffer_delete(StringBuffer *sb)
Totally delete a string buffer.
Definition: stringbuffer.c:71
int number
number of items in the list
Definition: readable.c:120
#define MSG_TYPE_PAPER_ENVELOPE_2
Definition: newclient.h:436
#define GOD_PATHS
Path information.
Definition: god.h:30
#define PATH_ABJURE
Definition: spells.h:20
uint8_t type
PLAYER, BULLET, etc.
Definition: object.h:338
struct Settings settings
Server settings.
Definition: init.c:40
FILE * of_open(OutputFile *of, const char *fname)
Opens an output file.
Definition: output_file.c:30
#define MSG_TYPE_PAPER
Definition: newclient.h:372
#define GOD_SACRED
Write sacred creatures.
Definition: god.h:27
int book_overflow(const char *buf1, const char *buf2, size_t booksize)
Checks if buf1 and buf2 can be combined.
Definition: readable.c:711
struct archt * next
Next archetype in a linked list.
Definition: object.h:467
#define MSGTYPE_MSGFILE
Message from the lib/messages file.
Definition: readable.c:89
#define SAVE_MODE
If you have defined SAVE_PLAYER, you might want to change this, too.
Definition: config.h:616
const GeneralMessage * get_message_from_identifier(const char *identifier)
Find the message from its identifier.
Definition: readable.c:2230
#define MSG_TYPE_PAPER_LETTER_OLD_2
Definition: newclient.h:432
void free_all_readable(void)
Free all readable-related information.
Definition: readable.c:2118
Functions for creating text output files.
struct recipestruct * next
Next recipe with the same number of ingredients.
Definition: recipe.h:24
static const int max_titles[6]
Number of titles for different name lists.
Definition: readable.c:551
static const char *const path_author[]
Used by spellpath texts.
Definition: readable.c:207
void add_abilities(object *op, const object *change)
Used in artifact generation.
Definition: artifact.c:284
const char * msg
If this is a book/sign/magic mouth/etc.
Definition: object.h:322
void write_book_archive(void)
Write out the updated book archive to bookarch file.
Definition: readable.c:2167
sstring get_message_body(const GeneralMessage *message)
Get a message's body.
Definition: readable.c:2252
static const char *const light_book_name[]
Generic book information.
Definition: readable.c:381
static StringBuffer * mon_desc(const object *mon)
Returns a description of the monster.
Definition: readable.c:1451
#define MSG_TYPE_PAPER_LETTER_OLD_1
Definition: newclient.h:431
char * strtoktolin(const char *buf1, const char *buf2, char *retbuf, size_t size)
Takes a string in buf1 and separates it into a list of strings delimited by buf2. ...
Definition: readable.c:681
static objectlink * first_mon_info
Information on monsters.
Definition: readable.c:154
sstring add_string(const char *str)
This will add 'str' to the hash table.
Definition: shstr.c:124
struct titlestruct * first_book
pointer to first book in this list
Definition: readable.c:121
#define MSG_TYPE_BOOK_SPELL_EVOKER
Definition: newclient.h:407
void stringbuffer_append_printf(StringBuffer *sb, const char *format,...)
Append a formatted string to a string buffer instance.
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)
Give a new, fancier name to generated objects of type BOOK and SPELLBOOK.
Definition: readable.c:1256
#define FLAG_MONSTER
Will attack players.
Definition: define.h:245
void object_copy(const object *src_ob, object *dest_ob)
Copy object first frees everything allocated by the second object, and then copies the contents of th...
Definition: object.c:838
static void init_mon_info(void)
Creates the linked list of pointers to monster archetype objects if not called previously.
Definition: readable.c:990
const readable_message_type * get_readable_message_type(object *readable)
Get the readable type for an object (hopefully book).
Definition: readable.c:2217
See Gloves.
Definition: object.h:213
#define MSG_TYPE_MONUMENT_GRAVESTONE_3
Definition: newclient.h:459
void LOG(LogLevel logLevel, const char *format,...)
Logs a message to stderr, or to file.
Definition: logger.c:51
#define MSGTYPE_LIB
Message from the lib/messages file.
Definition: readable.c:77
#define MSG_TYPE_BOOK
Definition: newclient.h:370
One general message, from the lib/messages file.
Definition: readable.c:134
Structure containing object statistics.
void object_set_msg(object *op, const char *msg)
Set the message field of an object.
Definition: object.c:4695
static const char *const formula_book_name[]
Alchemy (formula) information.
Definition: readable.c:347
A buffer that will be expanded as content is added to it.
Definition: stringbuffer.c:25
void query_name(const object *op, char *buf, size_t size)
Describes an item.
Definition: item.c:626
object * item
Special values of the artifact.
Definition: artifact.h:15
#define PATH_DETONATE
Definition: spells.h:22
uint16_t chance
Chance of the artifact to happen.
Definition: artifact.h:16
#define MSG_TYPE_BOOK_QUARTO_1
Definition: newclient.h:405
static const char *const book_descrpt[]
Book descriptions.
Definition: readable.c:457
#define MSGTYPE_GODS
God-related information.
Definition: readable.c:87
static void new_text_name(object *book, int msgtype)
Only for objects of type BOOK.
Definition: readable.c:1093
static const char *const art_book_name[]
Book titles for artifact information.
Definition: readable.c:250
size_t size
size of the book message
Definition: readable.c:111
See Breastplate Armor.
Definition: object.h:120
int16_t level
Level of creature or object.
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)
Generate a message detailing the member incantations/prayers (and some of their properties) belonging...
Definition: readable.c:1732
unsigned find_face(const char *name, unsigned error)
This returns an the face number of face 'name'.
Definition: image.c:303
Describes fundental parameters of 'books' - objects with type==BOOK.
EXTERN archetype * first_archetype
First archetype.
Definition: global.h:122
int32_t value
How much money it is worth (or contains)
Definition: object.h:350
sstring cauldron
Arch of the cauldron/workbench used to house the formulae.
Definition: recipe.h:27
const char * name
More definite name, like "generate_kobold".
Definition: object.h:466
char * stringbuffer_finish(StringBuffer *sb)
Deallocate the string buffer instance and return the string.
Definition: stringbuffer.c:76
#define PATH_WOUNDING
Definition: spells.h:30
struct recipestruct * items
Pointer to first recipe in this list.
Definition: recipe.h:40
recipelist * get_formulalist(int i)
Gets a formula list by ingredients count.
Definition: recipe.c:96
struct artifactstruct * next
Next artifact in the list.
Definition: artifact.h:18
#define MSG_TYPE_SIGN
Definition: newclient.h:373
One alchemy recipe.
Definition: recipe.h:10
sstring message
The message's body.
Definition: readable.c:139
#define PATH_TELE
Definition: spells.h:25
#define MAX_TITLE_CHECK
How many times to try to generate a unique name for a book.
Definition: readable.c:74
#define PATH_SUMMON
Definition: spells.h:19
static title * get_empty_book(void)
Creates a title.
Definition: readable.c:595