Crossfire Server, Trunk
readable.cpp
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2014 Mark Wedel and the Crossfire Development Team
5  * Copyright (c) 1992 Frank Tore Johansen
6  *
7  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
8  * welcome to redistribute it under certain conditions. For details, please
9  * see COPYING and LICENSE.
10  *
11  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
12  */
13 
23 /* laid down initial file - dec 1995. -b.t. thomas@astro.psu.edu */
24 
25 #include "global.h"
26 
27 #include <assert.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sys/stat.h>
32 
33 #include "book.h"
34 #include "living.h"
35 #include "output_file.h"
36 #include "spells.h"
37 #include "assets.h"
38 #include "AssetsManager.h"
39 
40 /* Define this if you want to archive book titles by contents.
41  * This option should enforce UNIQUE combinations of titles,authors and
42  * msg contents during and *between *game sessions.
43  * Note: a slight degeneracy exists since books are archived based on an integer
44  * index value calculated from the message text (similar to alchemy formulae).
45  * Sometimes two widely different messages have the same index value (rare). In
46  * this case, it is possible to occasionally generate 2 books with same title and
47  * different message content. Not really a bug, but rather a feature. This action
48  * should keeps player on their toes ;).
49  * Also, note that there is *finite *space available for archiving message and titles.
50  * Once this space is used, books will stop being archived. Not a serious problem
51  * under the current regime, since there are generally fewer possible (random)
52  * messages than space available on the titlelists.
53  * One exception (for sure) are the monster messages. But no worries, you should
54  * see all of the monster info in some order (but not all possble combinations)
55  * before the monster titlelist space is run out. You can increase titlelist
56  * space by increasing the array sizes for the monster book_authours and book_names
57  * (see max_titles[] array and include/read.h). Since the unique_book algorthm is
58  * kinda stupid, this program *may *slow down program execution if defined (but I don't
59  * think its a significant problem, at least, I have no problems running this option
60  * on a Sparc 10! Also, once archive title lists are filled and/or all possible msg
61  * combinations have been generated, unique_book isnt called anymore. It takes 5-10
62  * sessions for this to happen).
63  * Final note: the game remembers book/title/msg combinations from reading the
64  * file lib/bookarch. If you REMOVE this file, you will lose your archive. So
65  * be sure to copy it over to the new lib directory when you change versions.
66  * -b.t.
67  */
68 
69 /* This flag is useful to see what kind of output messages are created */
70 /* #define BOOK_MSG_DEBUG */
71 
72 /* This flag is useful for debugging archiving action */
73 /* #define ARCHIVE_DEBUG */
74 
76 #define MAX_TITLE_CHECK 20
77 
79 #define MSGTYPE_LIB 0
80 
81 #define MSGTYPE_MONSTER 1
82 
83 #define MSGTYPE_ARTIFACT 2
84 
85 #define MSGTYPE_SPELLPATH 3
86 
87 #define MSGTYPE_ALCHEMY 4
88 
89 #define MSGTYPE_GODS 5
90 
91 #define MSGTYPE_MSGFILE 6
92 
98 #define arraysize(arrayname) (sizeof(arrayname)/sizeof(*(arrayname)))
99 
100 /* Moved these structures from struct.h to this file in 0.94.3 - they
101  * are not needed anyplace else, so why have them globally declared?
102  */
103 
108 struct title {
109  const char *name;
110  const char *authour;
111  const char *archname;
112  unsigned int level;
113  size_t size;
114  int msg_index;
116 };
117 
121 struct titlelist {
122  int number;
125 };
126 
128 struct arttypename {
129  const char *name;
130  int type;
131 };
132 
133 static void add_book(title *book, int type, const char *fname, int lineno);
134 
139 static titlelist *booklist = NULL;
140 
142 static std::vector<object *> monsters;
143 
149 static const uint32_t spellpathdef[NRSPELLPATHS] = {
150  PATH_PROT,
151  PATH_FIRE,
152  PATH_FROST,
153  PATH_ELEC,
154  PATH_MISSILE,
155  PATH_SELF,
156  PATH_SUMMON,
157  PATH_ABJURE,
158  PATH_RESTORE,
160  PATH_MIND,
161  PATH_CREATE,
162  PATH_TELE,
163  PATH_INFO,
166  PATH_TURNING,
168  PATH_DEATH,
169  PATH_LIGHT
170 };
171 
173 static const char *const path_book_name[] = {
174  "codex",
175  "compendium",
176  "documentary",
177  "exposition",
178  "tables",
179  "treatise"
180 };
181 
183 static const char *const path_author[] = {
184  "aether",
185  "arcana",
186  "astral byways",
187  "connections",
188  "the Grey Council",
189  "deep pathways",
190  "knowledge",
191  "magic",
192  "mystic ways",
193  "pathways",
194  "power",
195  "spells",
196  "transforms",
197  "the mystic veil",
198  "unknown spells"
199 };
200 
207 static const arttypename art_name_array[] = {
208  { "Helmet", HELMET },
209  { "Amulet", AMULET },
210  { "Shield", SHIELD },
211  { "Bracers", BRACERS },
212  { "Boots", BOOTS },
213  { "Cloak", CLOAK },
214  { "Gloves", GLOVES },
215  { "Gridle", GIRDLE },
216  { "Ring", RING },
217  { "Horn", ROD },
218  { "Missile Weapon", BOW },
219  { "Missile", ARROW },
220  { "Hand Weapon", WEAPON },
221  { "Artifact", SKILL },
222  { "Food", FOOD },
223  { "Body Armour", ARMOUR }
224 };
225 
227 static const char *const art_book_name[] = {
228  "collection",
229  "file",
230  "files",
231  "guide",
232  "handbook",
233  "index",
234  "inventory",
235  "list",
236  "listing",
237  "omnibus",
238  "record",
239  "record book"
240 };
241 
243 static const char *const art_author[] = {
244  "ancient things",
245  "artifacts",
246  "equipment",
247  "Havlor", /* ancient warrior scribe :) */
248  "items",
249  "lost artifacts",
250  "the ancients",
251  "treasures",
252  "useful things"
253 };
254 
258 static const char *const mon_book_name[] = {
259  "beastuary",
260  "catalog",
261  "compilation",
262  "collection",
263  "encyclopedia",
264  "field notes",
265  "guide",
266  "handbook",
267  "list",
268  "manual",
269  "notes",
270  "omnibus",
271  "record",
272  "reference",
273  "register",
274  "volume"
275 };
276 
278 static const char *const mon_author[] = {
279  "beasts",
280  "beings",
281  "creatures",
282  "dezidens",
283  "dwellers",
284  "evil nature",
285  "life",
286  "monsters",
287  "nature",
288  "new life",
289  "residents",
290  "the spawn",
291  "the living",
292  "things"
293 };
294 
298 static const char *const gods_book_name[] = {
299  "devotional",
300  "devout notes",
301  "divine text",
302  "divine work",
303  "holy book",
304  "holy record",
305  "hymnal",
306  "illuminated text",
307  "moral text",
308  "sacred guide",
309  "testament",
310  "transcript"
311 };
312 
314 static const char *const gods_author[] = {
315  "cults",
316  "dieties",
317  "joy",
318  "lasting curse",
319  "madness",
320  "religions",
321  "the dead",
322  "the gods",
323  "the heirophant",
324  "the poor priest",
325  "the priestess",
326  "pain",
327  "white"
328 };
329 
333 static const char *const formula_book_name[] = {
334  "cookbook",
335  "formulary",
336  "lab book",
337  "lab notes",
338  "recipe book",
339  "experiment record",
340  "work plan",
341  "design notes",
342  "research notes",
343  "crafting manual"
344 };
345 
347 static const char *const formula_author[] = {
348  "Albertus Magnus",
349  "alchemy",
350  "balms",
351  "creation",
352  "dusts",
353  "magical manufacture",
354  "making",
355  "philosophical items",
356  "potions",
357  "powders",
358  "the cauldron",
359  "the lamp black",
360  "transmutation",
361  "waters"
362 };
363 
369 static const char *const light_book_name[] = {
370  "calendar",
371  "datebook",
372  "diary",
373  "essay",
374  "guidebook",
375  "handbook",
376  "ledger",
377  "notes",
378  "notebook",
379  "octavo",
380  "opuscule",
381  "pamphlet",
382  "practicum",
383  "script",
384  "transcript"
385 };
386 
388 static const char *const heavy_book_name[] = {
389  "catalog",
390  "compendium",
391  "dissertation",
392  "guide",
393  "manual",
394  "opus",
395  "tome",
396  "treatise",
397  "volume",
398  "work"
399 };
400 
402 static const char *const book_author[] = {
403  "Abdulah",
404  "Al'hezred",
405  "Alywn",
406  "Arundel",
407  "Arvind",
408  "Aerlingas",
409  "Bacon",
410  "Baliqendii",
411  "Bosworth",
412  "Beathis",
413  "Bertil",
414  "Cauchy",
415  "Chakrabarti",
416  "der Waalis",
417  "Dirk",
418  "Djwimii",
419  "Eisenstaadt",
420  "Fendris",
421  "Frank",
422  "Habbi",
423  "Harlod",
424  "Ichibod",
425  "Janus",
426  "June",
427  "Laplace",
428  "Magnuson",
429  "Nandii",
430  "Nitfeder",
431  "Norris",
432  "Parael",
433  "Penhew",
434  "Sophia",
435  "Skilly",
436  "Tahir",
437  "Thockmorton",
438  "Thomas",
439  "van Helsing",
440  "van Pelt",
441  "Voormis",
442  "Xavier",
443  "Xeno",
444  "Zardoz",
445  "Zagy"
446 };
447 
449 static const char *const book_descrpt[] = {
450  "ancient",
451  "cryptic",
452  "cryptical",
453  "dusty",
454  "hierarchical",
455  "grizzled",
456  "gold-gilt",
457  "great",
458  "lost",
459  "magnificent",
460  "musty",
461  "mythical",
462  "mystical",
463  "rustic",
464  "stained",
465  "silvered",
466  "transcendental",
467  "weathered"
468 };
469 
477  /*subtype 0 */ { 0, 0 },
478  /* book messages subtypes */
479  /*subtype 1 */ { MSG_TYPE_BOOK, MSG_TYPE_BOOK_CLASP_1 },
488  /*subtype 10 */ { MSG_TYPE_BOOK, MSG_TYPE_BOOK_SPELL_SORCERER },
490  /* card messages subtypes*/
499  /*subtype 20 */ { MSG_TYPE_CARD, MSG_TYPE_CARD_STRANGE_3 },
503  /* Paper messages subtypes */
510  /*subtype 30 */ { MSG_TYPE_PAPER, MSG_TYPE_PAPER_LETTER_NEW_2 },
518  /* road signs messages subtypes */
521  /*subtype 40 */ { MSG_TYPE_SIGN, MSG_TYPE_SIGN_DIR_RIGHT },
523  /* stones and monument messages */
537 };
538 
541 
543 static const int max_titles[6] = {
545  arraysize(mon_book_name)*arraysize(mon_author), /* MSGTYPE_MONSTER */
546  arraysize(art_book_name)*arraysize(art_author), /* MSGTYPE_ARTIFACT */
547  arraysize(path_book_name)*arraysize(path_author), /* MSGTYPE_SPELLPATH */
548  arraysize(formula_book_name)*arraysize(formula_author), /* MSGTYPE_ALCHEMY */
549  arraysize(gods_book_name)*arraysize(gods_author), /* MSGTYPE_GODS */
550 };
551 
552 /******************************************************************************
553  *
554  * Start of misc. readable functions used by others functions in this file
555  *
556  *****************************************************************************/
557 
568  titlelist *bl = (titlelist *)calloc(1, sizeof(titlelist));
569 
570  if (bl == NULL)
572  return bl;
573 }
574 
584 static title *get_empty_book(void) {
585  title *t = (title *)calloc(1, sizeof(title));
586 
587  if (t == NULL)
589  return t;
590 }
591 
602 static titlelist *get_titlelist(int i) {
603  titlelist *tl;
604  int number;
605 
606  if (i < 0 || i >= (int)arraysize(max_titles)) {
607  LOG(llevInfo, "Warning: invalid book index %d, using 0 instead\n", i);
608  return booklist;
609  }
610 
611  for (tl = booklist, number = i; tl && number; tl = tl->next, number--) {
612  if (!tl->next)
613  tl->next = get_empty_booklist();
614  }
615 
616  return tl;
617 }
618 
619 /* HANDMADE STRING FUNCTIONS.., perhaps these belong in another file
620  * (shstr.c ?), but the quantity BOOK_BUF will need to be defined. */
621 
633 int nstrtok(const char *buf1, const char *buf2) {
634  char *tbuf, buf[MAX_BUF];
635  int number = 0;
636 
637  if (!buf1 || !buf2)
638  return 0;
639 
640  strlcpy(buf, buf1, sizeof(buf));
641  for (tbuf = strtok(buf, buf2); tbuf; tbuf = strtok(NULL, buf2)) {
642  number++;
643  }
644  return number;
645 }
646 
652 const char *trim(const char *buf) {
653  while ((*buf) == ' ') {
654  buf++;
655  }
656  return buf;
657 }
658 
675 char *strtoktolin(const char *buf1, const char *buf2, char *retbuf, size_t size) {
676  int maxi, i = nstrtok(buf1, buf2);
677  char *tbuf, buf[MAX_BUF];
678 
679  maxi = i;
680  strlcpy(buf, buf1, sizeof(buf));
681  snprintf(retbuf, size, " ");
682  for (tbuf = strtok(buf, buf2); tbuf && i > 0; tbuf = strtok(NULL, buf2)) {
683  snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "%s", trim(tbuf));
684  i--;
685  if (i == 1 && maxi > 1)
686  snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), " and ");
687  else if (i > 0 && maxi > 1)
688  snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), ", ");
689  else {
690  // Remove a trailing newline if it is there.
691  int end = strlen(retbuf);
692  if (retbuf[end-1] == '\n')
693  end--;
694  snprintf(retbuf+end, size-end, ".");
695  }
696  }
697  return retbuf;
698 }
699 
710 int book_overflow(const char *buf1, const char *buf2, size_t booksize) {
711  if (buf_overflow(buf1, buf2, BOOK_BUF-2) /* 2 less so always room for trailing \n */
712  || buf_overflow(buf1, buf2, booksize))
713  return 1;
714  return 0;
715 }
716 
717 /*****************************************************************************
718  *
719  * Start of initialization related functions.
720  *
721  ****************************************************************************/
722 
729 static void init_book_archive(void) {
730  FILE *fp;
731 #ifdef BOOK_MSG_DEBUG
732  int nroftitle = 0;
733 #endif
734  char buf[MAX_BUF], fname[MAX_BUF], *cp;
735  static int did_init_barch = 0;
736 
737  if (did_init_barch)
738  return;
739  did_init_barch = 1;
740 
741  if (!booklist)
743 
744  snprintf(fname, sizeof(fname), "%s/bookarch", settings.localdir);
745  LOG(llevDebug, " Reading bookarch from %s...\n", fname);
746 
747  fp = fopen(fname, "r");
748  if (fp != NULL) {
749  int type;
750  size_t i;
751  titlelist *bl;
752  int lineno;
753  title *book;
754  int skipping;
755 
756  skipping = 0;
757  book = NULL;
758  type = -1;
759  for (lineno = 1; fgets(buf, MAX_BUF, fp) != NULL; lineno++) {
760  int len;
761  int value;
762 
763  if (*buf == '#')
764  continue;
765  cp = strchr(buf, '\n');
766  if (cp != NULL) {
767  while (cp > buf && (cp[-1] == ' ' || cp[-1] == '\t'))
768  cp--;
769  *cp = '\0';
770  }
771  cp = buf;
772  if (strncmp(buf, "title ", 6) == 0) {
773  skipping = 0;
774  cp = buf+6;
775  while (*cp == ' ' || *cp == '\t')
776  cp++;
777  if (*cp == '\0') {
778  LOG(llevInfo, "Warning: missing book title at %s, line %d\n", fname, lineno);
779  book = NULL;
780  } else {
781  book = get_empty_book(); /* init new book entry */
782  book->name = add_string(cp);
783  type = -1;
784 #ifdef BOOK_MSG_DEBUG
785  nroftitle++;
786 #endif
787  }
788  } else if (book == NULL) {
789  if (!skipping) {
790  skipping = 1;
791  LOG(llevInfo, "Warning: expecting 'title' at %s, line %d\n", fname, lineno);
792  }
793  } else if (strncmp(buf, "authour ", 8) == 0) {
794  cp = buf+8;
795  while (*cp == ' ' || *cp == '\t')
796  cp++;
797  if (*cp == '\0') {
798  LOG(llevInfo, "Warning: missing book authour at %s, line %d\n", fname, lineno);
799  } else {
800  book->authour = add_string(cp);
801  }
802  } else if (strncmp(buf, "arch ", 5) == 0) {
803  cp = buf+5;
804  while (*cp == ' ' || *cp == '\t')
805  cp++;
806  if (*cp == '\0') {
807  LOG(llevInfo, "Warning: missing book arch at %s, line %d\n", fname, lineno);
808  } else {
809  book->archname = add_string(cp);
810  }
811  } else if (sscanf(buf, "level %d%n", &value, &len) == 1 && len == (int)strlen(buf)) {
812  book->level = value;
813  } else if (sscanf(buf, "type %d%n", &value, &len) == 1 && len == (int)strlen(buf)) {
814  type = value;
815  } else if (sscanf(buf, "size %d%n", &value, &len) == 1 && len == (int)strlen(buf)) {
816  book->size = value;
817  } else if (sscanf(buf, "index %d%n", &value, &len) == 1 && len == (int)strlen(buf)) {
818  book->msg_index = value;
819  } else if (strcmp(buf, "end") == 0) { /* link it */
820  add_book(book, type, fname, lineno);
821  book = NULL;
822  type = -1;
823  } else {
824  LOG(llevInfo, "Warning: syntax error at %s, line %d\n", fname, lineno);
825  }
826  }
827  if (book != NULL) {
828  LOG(llevInfo, "Warning: missing 'end' at %s, line %d\n", fname, lineno);
829  add_book(book, type, fname, lineno);
830  }
831  LOG(llevDebug, " book archives(used/avail):\n");
832  for (bl = booklist, i = 0; bl != NULL && i < arraysize(max_titles); bl = bl->next, i++) {
833  LOG(llevDebug, "(%d/%d)\n", bl->number, max_titles[i]);
834  }
835  fclose(fp);
836  }
837 
838 #ifdef BOOK_MSG_DEBUG
839  LOG(llevDebug, "\n init_book_archive() got %d titles.\n", nroftitle);
840 #endif
841  LOG(llevDebug, " done.\n");
842 }
843 
851 static void add_book(title *book, int type, const char *fname, int lineno) {
852  titlelist *bl;
853 
854  if (type == -1) {
855  LOG(llevInfo, "Warning: book with no type at %s, line %d; using type 0\n", fname, lineno);
856  type = 0;
857  }
858 
859  bl = get_titlelist(type);
860  book->next = bl->first_book;
861  bl->first_book = book;
862  bl->number++;
863 }
864 
865 static void do_monster(archetype *at) {
866  if (QUERY_FLAG(&at->clone, FLAG_MONSTER) && (!at->head)
869  monsters.push_back(&at->clone);
870  }
871 }
872 
877 static void init_mon_info(void) {
878  static int did_init_mon_info = 0;
879 
880  if (did_init_mon_info)
881  return;
882  did_init_mon_info = 1;
883 
885 
886  LOG(llevDebug, "init_mon_info() got %zu monsters\n", monsters.size());
887 }
888 
895 void init_readable(void) {
896  static int did_this = 0;
897 
898  if (did_this)
899  return;
900  did_this = 1;
901 
902  LOG(llevDebug, "Initializing reading data...\n");
904  init_mon_info();
905  LOG(llevDebug, " done reading data\n");
906 }
907 
908 /*****************************************************************************
909  *
910  * This is the start of the administrative functions when creating
911  * new books (ie, updating title and the like)
912  *
913  *****************************************************************************/
914 
926 static title *find_title(const object *book, int msgtype) {
927  title *t;
928  titlelist *tl;
929  size_t length;
930  int index;
931 
932  if (msgtype < 0)
933  return (title *)NULL;
934 
935  tl = get_titlelist(msgtype);
936  if (!tl)
937  return (title *)NULL;
938 
939  length = strlen(book->msg);
940  index = strtoint(book->msg);
941  for (t = tl->first_book; t; t = t->next)
942  if (t->size == length && t->msg_index == index) {
943 #ifdef ARCHIVE_DEBUG
944  LOG(llevDebug, "Found title match (list %d): %s %s (%d)\n", msgtype, t->name, t->authour, t->msg_index);
945 #endif
946  return t;
947  }
948 
949  return (title *)NULL;
950 }
951 
965 static void new_text_name(object *book, int msgtype) {
966  const char *name;
967 
968  if (book->type != BOOK)
969  return;
970 
971  switch (msgtype) {
972  case MSGTYPE_MONSTER:
974  break;
975 
976  case MSGTYPE_ARTIFACT:
978  break;
979 
980  case MSGTYPE_SPELLPATH:
982  break;
983 
984  case MSGTYPE_ALCHEMY:
986  break;
987 
988  case MSGTYPE_GODS:
990  break;
991 
992  case MSGTYPE_MSGFILE:
993  default:
994  if (book->weight > 2000) { /* based on weight */
996  } else {
998  }
999  break;
1000  }
1001  free_string(book->name);
1002  book->name = add_string(name);
1003 }
1004 
1014 static void add_author(object *op, int msgtype) {
1015  char title[MAX_BUF];
1016  const char *name;
1017 
1018  if (msgtype < 0 || strlen(op->msg) < 5)
1019  return;
1020 
1021  switch (msgtype) {
1022  case MSGTYPE_MONSTER:
1024  break;
1025 
1026  case MSGTYPE_ARTIFACT:
1028  break;
1029 
1030  case MSGTYPE_SPELLPATH:
1032  break;
1033 
1034  case MSGTYPE_ALCHEMY:
1036  break;
1037 
1038  case MSGTYPE_GODS:
1040  break;
1041 
1042  case MSGTYPE_MSGFILE:
1043  default:
1045  }
1046 
1047  snprintf(title, sizeof(title), "of %s", name);
1048  op->title = add_string(title);
1049 }
1050 
1062 static int unique_book(const object *book, int msgtype) {
1063  title *test;
1064 
1065  if (!booklist)
1066  return 1; /* No archival entries! Must be unique! */
1067 
1068  /* Go through the booklist. If the author and name match, not unique so
1069  * return 0.
1070  */
1071  for (test = get_titlelist(msgtype)->first_book; test; test = test->next) {
1072  if (!strcmp(test->name, book->name) && !strcmp(book->title, test->authour))
1073  return 0;
1074  }
1075  return 1;
1076 }
1077 
1086 static void add_book_to_list(const object *book, int msgtype) {
1087  titlelist *tl = get_titlelist(msgtype);
1088  title *t;
1089 
1090  if (!tl) {
1091  LOG(llevError, "add_book_to_list can't get booklist!\n");
1092  return;
1093  }
1094 
1095  t = get_empty_book();
1096  t->name = add_string(book->name);
1097  t->authour = add_string(book->title);
1098  t->size = strlen(book->msg);
1099  t->msg_index = strtoint(book->msg);
1100  t->archname = add_string(book->arch->name);
1101  t->level = book->level;
1102 
1103  t->next = tl->first_book;
1104  tl->first_book = t;
1105  tl->number++;
1106 
1107  /* We have stuff we need to write now */
1109 
1110 #ifdef ARCHIVE_DEBUG
1111  LOG(llevDebug, "Archiving new title: %s %s (%d)\n", book->name, book->title, msgtype);
1112 #endif
1113 }
1114 
1128 static void change_book(object *book, int msgtype) {
1129  titlelist *tl;
1130  title *t;
1131  int tries;
1132 
1133  if (book->type != BOOK) {
1134  LOG(llevError, "change_book_name() called w/ illegal obj type.\n");
1135  return;
1136  }
1137 
1138  tl = get_titlelist(msgtype);
1139  t = NULL;
1140  tries = 0;
1141 
1142  /* look to see if our msg already been archived. If so, alter
1143  * the book to match the archival text. If we fail to match,
1144  * then we archive the new title/name/msg combo if there is
1145  * room on the titlelist.
1146  */
1147 
1148  if (strlen(book->msg) > 5 && (t = find_title(book, msgtype))) {
1149  object *tmpbook;
1150  sstring marker = object_get_value(book, "knowledge_marker");
1151 
1152  /* alter book properties */
1153  tmpbook = create_archetype(t->archname);
1154  if (marker != NULL)
1155  /* need to copy the knowledge_marker */
1156  object_set_value(tmpbook, "knowledge_marker", marker, 1);
1157  object_set_msg(tmpbook, book->msg);
1158  object_copy(tmpbook, book);
1159  object_free_drop_inventory(tmpbook);
1160 
1161  book->title = add_string(t->authour);
1162  free_string(book->name);
1163  book->name = add_string(t->name);
1164  book->level = t->level;
1165  } else { /* Don't have any default title, so lets make up a new one */
1166  int numb, maxnames = max_titles[msgtype];
1167  const char *old_title;
1168  const char *old_name;
1169 
1170  old_title = book->title ? add_string(book->title) : NULL;
1171  old_name = add_string(book->name);
1172 
1173  /* some pre-generated books have title already set (from
1174  * maps), also don't bother looking for unique title if
1175  * we already used up all the available names! */
1176 
1177  if (!tl) {
1178  LOG(llevError, "change_book_name(): can't find title list\n");
1179  numb = 0;
1180  } else
1181  numb = tl->number;
1182 
1183  if (numb == maxnames) {
1184 #ifdef ARCHIVE_DEBUG
1185  LOG(llevDebug, "titles for list %d full (%d possible).\n", msgtype, maxnames);
1186 #endif
1187  // Set tries to maximum. That way we get the descriptors on books when the
1188  // title set is full, too.
1189  tries = MAX_TITLE_CHECK;
1190  }
1191  /* shouldnt change map-maker books */
1192  else if (!book->title)
1193  do {
1194  /* random book name */
1195  new_text_name(book, msgtype);
1196  add_author(book, msgtype); /* random author */
1197  tries++;
1198  } while (!unique_book(book, msgtype) && tries < MAX_TITLE_CHECK);
1199 
1200  /* Now deal with 2 cases.
1201  * 1) If no space for a new title exists lets just restore
1202  * the old book properties. Remember, if the book had
1203  * matchd an older entry on the titlelist, we shouldnt
1204  * have called this routine in the first place!
1205  * 2) If we got a unique title, we need to add it to
1206  * the list.
1207  */
1208 
1209  if (tries == MAX_TITLE_CHECK) {
1210 #ifdef ARCHIVE_DEBUG
1211  LOG(llevDebug, "Failed to obtain unique title for %s %s (names:%d/%d)\n", book->name, book->title, numb, maxnames);
1212 #endif
1213  /* restore old book properties here */
1214  free_string(book->name);
1215  if (book->title)
1216  free_string(book->title);
1217  book->title = old_title != NULL ? add_string(old_title) : NULL;
1218 
1219  if (RANDOM()%4) {
1220  /* Lets give the book a description to individualize it some */
1221  char new_name[MAX_BUF];
1222 
1223  snprintf(new_name, MAX_BUF, "%s %s", book_descrpt[RANDOM()%arraysize(book_descrpt)], old_name);
1224  book->name = add_string(new_name);
1225  } else {
1226  book->name = add_string(old_name);
1227  }
1228  } else if (book->title && strlen(book->msg) > 5) { /* archive if long msg texts */
1229  add_book_to_list(book, msgtype);
1230  }
1231 
1232  if (old_title != NULL)
1233  free_string(old_title);
1234  free_string(old_name);
1235  }
1236 }
1237 
1238 /*****************************************************************************
1239  *
1240  * This is the start of the area that generates the actual contents
1241  * of the book.
1242  *
1243  *****************************************************************************/
1244 
1245 /*****************************************************************************
1246  * Monster msg generation code.
1247  ****************************************************************************/
1248 
1260 object *get_random_mon(int level) {
1261 
1262  /* safety check. Problem w/ init_mon_info list? */
1263  if (monsters.empty())
1264  return (object *)NULL;
1265 
1266  if (!level) {
1267  return monsters[RANDOM() % monsters.size()];
1268  }
1269 
1270  std::vector<object *> select;
1271  std::copy_if(monsters.cbegin(), monsters.cend(), std::back_inserter(select), [&] (auto ob) { return ob->level >= level; });
1272 
1273  if (select.empty()) {
1274  LOG(llevError, "get_random_mon() couldn't return monster for level %d\n", level);
1275  return NULL;
1276  }
1277  return select[RANDOM() % select.size()];
1278 }
1279 
1289 static StringBuffer *mon_desc(const object *mon) {
1290  StringBuffer *desc = stringbuffer_new();
1291  stringbuffer_append_printf(desc, "\n---\n *** %s ***\n", mon->name);
1292  describe_item(mon, NULL, 0, desc);
1293  return desc;
1294 }
1295 
1307 static object *get_next_mon(const object *tmp) {
1308  auto it = std::find(monsters.begin(), monsters.end(), tmp);
1309  if (it == monsters.end())
1310  return nullptr;
1311  ++it;
1312  if (it == monsters.end())
1313  it = monsters.begin();
1314 
1315  return *it;
1316 }
1317 
1330 static StringBuffer *mon_info_msg(int level, size_t booksize, object *book) {
1331  object *tmp;
1332  StringBuffer *marker = stringbuffer_new(), *desc = stringbuffer_new(), *mon = NULL;
1333  int added = 0;
1334  sstring final;
1335  const char *sep = ":";
1336 
1337  /*preamble */
1338  stringbuffer_append_string(desc, "This beastiary contains:");
1339  stringbuffer_append_string(marker, "monster");
1340 
1341  /* lets print info on as many monsters as will fit in our
1342  * document.
1343  * 8-96 Had to change this a bit, otherwise there would
1344  * have been an impossibly large number of combinations
1345  * of text! (and flood out the available number of titles
1346  * in the archive in a snap!) -b.t.
1347  */
1348  for (tmp = get_random_mon(level*3); tmp; tmp = get_next_mon(tmp)) {
1349  /* monster description */
1350  mon = mon_desc(tmp);
1351 
1353  break;
1354  added++;
1355  stringbuffer_append_printf(marker, "%s%s", sep, tmp->arch->name);
1356  sep = "/";
1359  mon = NULL;
1360  }
1361 
1362  if (mon != NULL) {
1364  }
1365 
1366  final = stringbuffer_finish_shared(marker);
1367  if (added)
1368  object_set_value(book, "knowledge_marker", final, 1);
1369  free_string(final);
1370 
1371  return desc;
1372 }
1373 
1374 /*****************************************************************************
1375  * Artifact msg generation code.
1376  ****************************************************************************/
1377 
1387 static StringBuffer *artifact_describe(const artifact *art, const artifactlist *al, int message, int art_name, int separator) {
1388  object *tmp;
1389  int chance;
1390  StringBuffer *desc = stringbuffer_new(), *sbuf;
1391 
1392  if (separator)
1393  stringbuffer_append_string(desc, "---\n");
1394 
1395  /* Name */
1396  if (!art->allowed.empty()) {
1397  archetype *arch;
1398  auto name = art->allowed[RANDOM() % art->allowed.size()];
1399  int inv = 0;
1400 
1401  if (name[0] == '!')
1402  inv = 1;
1403 
1406  if (!arch)
1408 
1409  if (!arch)
1410  LOG(llevError, "artifact_msg: missing archetype %s for artifact %s (type %d)\n", name + inv, art->item->name, art->item->type);
1411  else {
1412  if (inv)
1413  stringbuffer_append_printf(desc, " A %s (excepted %s) of %s", art_name_array[art_name].name, arch->clone.name_pl, art->item->name);
1414  else
1415  stringbuffer_append_printf(desc, " A %s of %s", arch->clone.name, art->item->name);
1416  }
1417  } else { /* default name is used */
1418  /* use the base 'generic' name for our artifact */
1419  stringbuffer_append_printf(desc, " The %s of %s", art_name_array[art_name].name, art->item->name);
1420  }
1421 
1422  /* chance of finding */
1423  stringbuffer_append_string(desc, " is ");
1424  chance = 100*((float)art->chance/al->total_chance);
1425  if (chance >= 20)
1426  stringbuffer_append_string(desc, "an uncommon");
1427  else if (chance >= 10)
1428  stringbuffer_append_string(desc, "an unusual");
1429  else if (chance >= 5)
1430  stringbuffer_append_string(desc, "a rare");
1431  else
1432  stringbuffer_append_string(desc, "a very rare");
1433 
1434  /* value of artifact */
1435  stringbuffer_append_printf(desc, " item with a value that is %d times normal.\n", art->item->value);
1436 
1437  /* include the message about the artifact, if exists, and book
1438  * level is kinda high */
1439  if (message && !(strlen(art->item->msg) > BOOK_BUF))
1440  stringbuffer_append_string(desc, art->item->msg);
1441 
1442  /* properties of the artifact */
1443  tmp = object_new();
1444  add_abilities(tmp, art->item);
1445  tmp->type = al->type;
1447  sbuf = describe_item(tmp, NULL, 0, NULL);
1448  if (stringbuffer_length(sbuf) > 1) {
1449  stringbuffer_append_string(desc, " Properties of this artifact include:\n ");
1451  stringbuffer_append_string(desc, "\n");
1452  }
1453  free(stringbuffer_finish(sbuf));
1455 
1456  return desc;
1457 }
1458 
1470 static StringBuffer *artifact_msg(unsigned int level, size_t booksize) {
1471  const artifactlist *al;
1472  const artifact *art;
1473  int i, type, index;
1474  int book_entries = level > 5 ? RANDOM()%3+RANDOM()%3+2 : RANDOM()%level+1;
1476 
1477  /* values greater than 5 create msg buffers that are too big! */
1478  if (book_entries > 5)
1479  book_entries = 5;
1480 
1481  /* lets determine what kind of artifact type randomly.
1482  * Right now legal artifacts only come from those listed
1483  * in art_name_array. Also, we check to be sure an artifactlist
1484  * for that type exists!
1485  */
1486  i = 0;
1487  do {
1490  al = find_artifactlist(type);
1491  i++;
1492  } while (al == NULL && i < 10);
1493 
1494  if (i == 10) { /* Unable to find a message */
1496  return message;
1497  }
1498 
1499  /* There is no reason to start on the artifact list at the beginning. Lets
1500  * take our starting position randomly... */
1501  auto iart = al->items.cbegin();
1502  for (i = RANDOM()%level+RANDOM()%2+1; i > 0; i--) {
1503  if (iart == al->items.cend())
1504  iart = al->items.cbegin(); /* hmm, out of stuff, loop back around */
1505  ++iart;
1506  }
1507 
1508  /* Ok, lets print out the contents */
1509  stringbuffer_append_printf(message, "Herein %s detailed %s...\n", book_entries > 1 ? "are" : "is", book_entries > 1 ? "some artifacts" : "an artifact");
1510 
1511  i = 0;
1512  /* artifact msg attributes loop. Lets keep adding entries to the 'book'
1513  * as long as we have space up to the allowed max # (book_entires)
1514  */
1515  while (book_entries > 0) {
1516  int with_message;
1517  if (iart == al->items.cend())
1518  iart = al->items.cbegin();
1519  art = *iart;
1520  with_message = (art->item->msg && RANDOM()%4+1 < level) ? 1 : 0;
1521 
1522  desc = artifact_describe(art, al, with_message, index, i++);
1523 
1525  stringbuffer_delete(desc);
1526  break;
1527  }
1528 
1530  stringbuffer_delete(desc);
1531 
1532  ++iart;
1533  book_entries--;
1534  }
1535 
1536  return message;
1537 }
1538 
1539 /*****************************************************************************
1540  * Spellpath message generation
1541  *****************************************************************************/
1542 
1544 static struct {
1545  int prayers;
1546  int did_first_sp;
1547  uint32_t pnum;
1548  int level;
1549  size_t booksize;
1551  int done;
1555 static void do_spellpath_msg(archetype *at) {
1556  /* Determine if this is an appropriate spell. Must
1557  * be of matching path, must be of appropriate type (prayer
1558  * or not), and must be within the valid level range.
1559  */
1560  if (at->clone.type == SPELL
1561  && at->clone.path_attuned & sp_params.pnum
1562  && ((at->clone.stats.grace && sp_params.prayers) || (at->clone.stats.sp && !sp_params.prayers))
1563  && at->clone.level < sp_params.level*8) {
1564  if (strlen(at->clone.name) + stringbuffer_length(sp_params.buf) >= sp_params.booksize) {
1565  sp_params.done = 1;
1566  return;
1567  }
1568 
1569  if (sp_params.did_first_sp)
1571  sp_params.did_first_sp = 1;
1573  }
1574 }
1575 
1590  int path = RANDOM()%NRSPELLPATHS;
1591  sp_params.prayers = RANDOM()%2;
1592  sp_params.did_first_sp = 0;
1593  sp_params.pnum = spellpathdef[path];
1594  sp_params.done = 0;
1595 
1596  if (buf == NULL) {
1597  buf = stringbuffer_new();
1598  /* Preamble */
1599  stringbuffer_append_printf(buf, "Herein are detailed the names of %s", sp_params.prayers ? "prayers" : "incantations");
1600  stringbuffer_append_printf(buf, " belonging to the path of %s:\n ", spellpathnames[path]);
1601  }
1602  sp_params.level = level;
1603  sp_params.booksize = booksize;
1604  sp_params.buf = buf;
1605 
1607 
1608  /* Geez, no spells were generated. */
1609  if (!sp_params.did_first_sp) {
1610  if (RANDOM()%4) { /* usually, lets make a recursive call... */
1611  // If we do a recursive call, we reset the spell path, so we will want to reset our text as well.
1613  return spellpath_msg(level, booksize, NULL);
1614  }
1615  /* give up, cause knowing no spells exist for path is info too. need the header too. */
1616  stringbuffer_append_string(buf, "- no known spells exist -\n");
1617  }
1618  return buf;
1619 }
1620 
1629 static void make_formula_book(object *book, int level) {
1630  recipelist *fl;
1631  recipe *formula;
1632  int chance, count = 0;
1633  const char *op_name;
1634  archetype *at;
1635  StringBuffer *text, *title;
1636  char *final, km[MAX_BUF];
1637 
1638  /* the higher the book level, the more complex (ie number of
1639  * ingredients) the formula can be.
1640  */
1641  fl = get_formulalist((RANDOM()%level)/3+1);
1642  if (!fl)
1643  fl = get_formulalist(1); /* safety */
1644 
1645  if (fl->total_chance == 0) {
1646  object_set_msg(book, " <indecipherable text>\n");
1648  add_author(book, MSGTYPE_ALCHEMY);
1649  return;
1650  }
1651 
1652  /* get a random formula, weighted by its bookchance */
1653  chance = RANDOM()%fl->total_chance;
1654  for (formula = fl->items; formula != NULL; formula = formula->next) {
1655  chance -= formula->chance;
1656  if (chance <= 0 && formula->chance != 0 && !formula->is_combination)
1657  break;
1658  }
1659 
1660  if (!formula || formula->arch_names <= 0) {
1661  object_set_msg(book, " <indecipherable text>\n");
1663  add_author(book, MSGTYPE_ALCHEMY);
1664  return;
1665  }
1666 
1667  /* looks like a formula was found. Base the amount
1668  * of information on the booklevel and the spellevel
1669  * of the formula. */
1670 
1671  op_name = formula->arch_name[RANDOM()%formula->arch_names];
1672  at = find_archetype(op_name);
1673  if (at == (archetype *)NULL) {
1674  LOG(llevError, "formula_msg() can't find arch %s for formula.\n", op_name);
1675  object_set_msg(book, " <indecipherable text>\n");
1677  add_author(book, MSGTYPE_ALCHEMY);
1678  return;
1679  }
1680  op_name = at->clone.name;
1681 
1682  text = stringbuffer_new();
1683  title = stringbuffer_new();
1684 
1685  /* preamble */
1686  stringbuffer_append_printf(text, "Herein is described a project using %s:\n", formula->skill ? formula->skill : "an unknown skill");
1687 
1688  /* item name */
1689  if (strcmp(formula->title, "NONE")) {
1690  stringbuffer_append_printf(text, "The %s of %s", op_name, formula->title);
1691  /* This results in things like pile of philo. sulfur.
1692  * while philo. sulfur may look better, without this,
1693  * you get things like 'the wise' because its missing the
1694  * water of section.
1695  */
1697  } else {
1698  stringbuffer_append_printf(text, "The %s", op_name);
1700  if (at->clone.title) {
1703  }
1704  }
1705  /* Lets name the book something meaningful ! */
1706  if (book->name)
1707  free_string(book->name);
1709  if (book->title) {
1710  free_string(book->title);
1711  book->title = NULL;
1712  }
1713 
1714  /* ingredients to make it */
1715  if (formula->ingred != NULL) {
1716  linked_char *next;
1717  archetype *at;
1718  char name[MAX_BUF];
1719 
1720  at = find_archetype(formula->cauldron);
1721  if (at)
1722  query_name(&at->clone, name, MAX_BUF);
1723  else
1724  snprintf(name, sizeof(name), "an unknown place");
1725 
1726  stringbuffer_append_printf(text, " may be made at %s using the following ingredients:\n", name);
1727 
1728  for (next = formula->ingred; next != NULL; next = next->next) {
1729  count++;
1730  stringbuffer_append_printf(text, "%s\n", next->name);
1731  }
1732  } else {
1733  LOG(llevError, "formula_msg() no ingredient list for object %s of %s\n", op_name, formula->title);
1735  }
1736 
1737  final = stringbuffer_finish(text);
1738  object_set_msg(book, final);
1739  free(final);
1740 
1743  snprintf(km, sizeof(km), "alchemy:%d:%d:%s", count, formula->index, formula->title);
1744  object_set_value(book, "knowledge_marker", km, 1);
1745 }
1746 
1757 static StringBuffer *msgfile_msg(object *book, size_t booksize) {
1758  StringBuffer *ret = stringbuffer_new();
1759 
1761 
1762  if (msg && strlen(msg->message) <= booksize) {
1763  stringbuffer_append_string(ret, msg->message);
1764  if (msg->identifier != NULL) {
1765  char km[HUGE_BUF];
1768  snprintf(km, sizeof(km), "message:%s", msg->identifier);
1769  object_set_value(book, "knowledge_marker", km, 1);
1770  }
1771  if (msg->quest_code) {
1772  /* add a 'apply' hook to launch the quest */
1773  archetype *at = find_archetype("quest_advance_apply");
1774  if (at != NULL) {
1775  object *event = object_create_arch(at);
1776  FREE_AND_COPY(event->name, msg->quest_code);
1777  object_insert_in_ob(event, book);
1778  }
1779  }
1780  } else {
1781 #ifdef BOOK_MSG_DEBUG
1782  // If msg was defined when we got here, we overflowed a book.
1783  if (msg)
1784  LOG(llevDebug, "Could not fit message %s into %s (%ld > %ld)\n", msg->identifier, book->name, strlen(msg->message), booksize);
1785 #endif
1786  stringbuffer_append_string(ret, "\n <undecipherable text>");
1787  }
1788 
1789  return ret;
1790 }
1791 
1806 static StringBuffer *god_info_msg(int level, size_t booksize, object *book) {
1807  int what = 0;
1808  const object *god = get_rand_god();
1809  StringBuffer *desc = NULL;
1810 
1811  if (!god)
1812  return NULL; /* oops, problems... */
1813 
1814  if (booksize > BOOK_BUF) {
1815  LOG(llevError, "common/readable.c:god_info_msg() - passed in booksize (%lu) is larger than book buffer (%d)\n", (unsigned long)booksize, BOOK_BUF);
1816  booksize = BOOK_BUF;
1817  }
1818 
1819  if (level >= 2 && RANDOM()%2) {
1820  what |= GOD_ENEMY;
1821  }
1822  if (level >= 3 && RANDOM()%2) {
1823  what |= GOD_HOLYWORD;
1824  }
1825  if (level >= 4 && RANDOM()%2) {
1826  what |= GOD_RESISTANCES;
1827  }
1828  if (level >= 5 && RANDOM()%2) {
1829  what |= GOD_SACRED;
1830  }
1831  if (level >= 6 && RANDOM()%2) {
1832  what |= GOD_BLESSED;
1833  }
1834  if (level >= 8 && RANDOM()%2) {
1835  what |= GOD_IMMUNITIES;
1836  }
1837  if (level >= 12 && RANDOM()%2) {
1838  what |= GOD_PATHS;
1839  }
1840 
1841  desc = stringbuffer_new();
1842  what = describe_god(god, what, desc, booksize);
1843 
1844  /* check to be sure new buffer size dont exceed either
1845  * the maximum buffer size, or the 'natural' size of the
1846  * book... */
1847  if (stringbuffer_length(desc) > 1 && stringbuffer_length(desc) <= booksize) {
1848  char buf[BOOK_BUF];
1849  snprintf(buf, sizeof(buf), "god:%s:%d", god->name, what);
1850  object_set_value(book, "knowledge_marker", buf, 1);
1851  return desc;
1852  }
1853 
1854  stringbuffer_delete(desc);
1855  return NULL;
1856 }
1857 
1876 void tailor_readable_ob(object *book, int msg_type) {
1877  int level = book->level ? RANDOM()%book->level+1 : 1;
1878  size_t book_buf_size;
1879  StringBuffer *message = NULL;
1880 
1881  /* safety */
1882  if (book->type != BOOK)
1883  return;
1884 
1885  if (level <= 0)
1886  return; /* if no level no point in doing any more... */
1887 
1888  /* Max text length this book can have. */
1889  book_buf_size = BOOKSIZE(book);
1890  book_buf_size -= strlen("\n"); /* Keep enough for final \n. */
1891  assert(book_buf_size < BOOK_BUF);
1892 
1893  /* &&& The message switch &&& */
1894  /* Below all of the possible types of messages in the "book"s.
1895  */
1896  /*
1897  * IF you add a new type of book msg, you will have to do several things.
1898  * 1) make sure there is an entry in the msg switch below!
1899  * 2) make sure there is an entry in max_titles[] array.
1900  * 3) make sure there are entries for your case in new_text_title()
1901  * and add_authour().
1902  * 4) you may want separate authour/book name arrays in read.h
1903  */
1904 
1905  if (msg_type >= (int)arraysize(max_titles))
1906  msg_type = 0;
1907 
1908  msg_type = msg_type > 0 ? msg_type : (int)(RANDOM()%6);
1909  switch (msg_type) {
1910  case MSGTYPE_MONSTER:
1911  message = mon_info_msg(level, book_buf_size, book);
1912  break;
1913 
1914  case MSGTYPE_ARTIFACT:
1915  message = artifact_msg(level, book_buf_size);
1916  break;
1917 
1918  case MSGTYPE_SPELLPATH: /* grouping incantations/prayers by path */
1919  message = spellpath_msg(level, book_buf_size, NULL);
1920  break;
1921 
1922  case MSGTYPE_ALCHEMY: /* describe an alchemy formula */
1923  make_formula_book(book, level);
1924  /* make_formula_book already gives title */
1925  return;
1926  break;
1927 
1928  case MSGTYPE_GODS: /* bits of information about a god */
1929  message = god_info_msg(level, book_buf_size, book);
1930  break;
1931 
1932  case MSGTYPE_LIB: /* use info list in lib/ */
1933  default:
1934  message = msgfile_msg(book, book_buf_size);
1935  break;
1936  }
1937 
1938  if (message != NULL) {
1939  char *final;
1941  final = stringbuffer_finish(message);
1942  object_set_msg(book, final);
1943  free(final);
1944  /* lets give the "book" a new name, which may be a compound word */
1945  change_book(book, msg_type);
1946  }
1947 }
1948 
1949 /*****************************************************************************
1950  *
1951  * Cleanup routine for readable stuff.
1952  *
1953  *****************************************************************************/
1954 
1958 void free_all_readable(void) {
1959  titlelist *tlist, *tnext;
1960  title *title1, *titlenext;
1961 
1962  LOG(llevDebug, "freeing all book information\n");
1963 
1964  for (tlist = booklist; tlist != NULL; tlist = tnext) {
1965  tnext = tlist->next;
1966  for (title1 = tlist->first_book; title1; title1 = titlenext) {
1967  titlenext = title1->next;
1968  if (title1->name)
1969  free_string(title1->name);
1970  if (title1->authour)
1971  free_string(title1->authour);
1972  if (title1->archname)
1973  free_string(title1->archname);
1974  free(title1);
1975  }
1976  free(tlist);
1977  }
1978 }
1979 
1980 /*****************************************************************************
1981  *
1982  * Writeback routine for updating the bookarchive.
1983  *
1984  ****************************************************************************/
1985 
1990  FILE *fp;
1991  OutputFile of;
1992  int index;
1993  char fname[MAX_BUF];
1994  title *book;
1995  titlelist *bl;
1996 
1997  /* If nothing changed, don't write anything */
1999  return;
2000 
2001  snprintf(fname, sizeof(fname), "%s/bookarch", settings.localdir);
2002  LOG(llevDebug, "Updating book archive: %s...\n", fname);
2003 
2004  fp = of_open(&of, fname);
2005  if (fp == NULL)
2006  return;
2007 
2008  for (bl = get_titlelist(0), index = 0; bl; bl = bl->next, index++) {
2009  for (book = bl->first_book; book; book = book->next)
2010  if (book && book->authour) {
2011  fprintf(fp, "title %s\n", book->name);
2012  fprintf(fp, "authour %s\n", book->authour);
2013  fprintf(fp, "arch %s\n", book->archname);
2014  fprintf(fp, "level %d\n", book->level);
2015  fprintf(fp, "type %d\n", index);
2016  /* C89 doesn't have %zu... */
2017  fprintf(fp, "size %lu\n", (unsigned long)book->size);
2018  fprintf(fp, "index %d\n", book->msg_index);
2019  fprintf(fp, "end\n");
2020  }
2021  }
2022  if (!of_close(&of))
2023  return;
2024 
2025  if (chmod(fname, SAVE_MODE) != 0) {
2026  LOG(llevError, "Could not set permissions on '%s'\n", fname);
2027  }
2028 
2030 }
2031 
2040  uint8_t subtype = readable->subtype;
2041 
2042  if (subtype > last_readable_subtype)
2043  return &readable_message_types[0];
2044  return &readable_message_types[subtype];
2045 }
2046 
2053  return message->title;
2054 }
2055 
2062  return message->message;
2063 }
2064 
2071  return message->face;
2072 }
write_book_archive
void write_book_archive(void)
Definition: readable.cpp:1989
PATH_TURNING
#define PATH_TURNING
Definition: spells.h:29
MSG_TYPE_MONUMENT_WALL_2
#define MSG_TYPE_MONUMENT_WALL_2
Definition: newclient.h:472
give.next
def next
Definition: give.py:44
formula_book_name
static const char *const formula_book_name[]
Definition: readable.cpp:333
AssetsManager::messages
Messages * messages()
Definition: AssetsManager.h:59
MSG_TYPE_BOOK_SPELL_PRAYER
#define MSG_TYPE_BOOK_SPELL_PRAYER
Definition: newclient.h:419
output_file.h
global.h
GOD_IMMUNITIES
#define GOD_IMMUNITIES
Definition: god.h:18
change_book
static void change_book(object *book, int msgtype)
Definition: readable.cpp:1128
settings
struct Settings settings
Definition: init.cpp:139
path_book_name
static const char *const path_book_name[]
Definition: readable.cpp:173
stringbuffer_length
size_t stringbuffer_length(StringBuffer *sb)
Definition: stringbuffer.cpp:218
get_formulalist
recipelist * get_formulalist(int i)
Definition: recipe.cpp:98
SAVE_MODE
#define SAVE_MODE
Definition: config.h:563
title::authour
const char * authour
Definition: readable.cpp:110
get_empty_booklist
static titlelist * get_empty_booklist(void)
Definition: readable.cpp:567
do_spellpath_msg
static void do_spellpath_msg(archetype *at)
Definition: readable.cpp:1555
mon_author
static const char *const mon_author[]
Definition: readable.cpp:278
BOW
@ BOW
Definition: object.h:123
BRACERS
@ BRACERS
Definition: object.h:222
PATH_LIGHT
#define PATH_LIGHT
Definition: spells.h:32
llevError
@ llevError
Definition: logger.h:11
it
**Media tags please refer to the protocol file in doc Developers protocol Quick for your pleasure an example[/b][i] This is an old full of dirt and partially destroyed[hand] My dear as you two years i had to leave quickly Words have come to me of powerful magic scrolls discovered in an old temple by my uncle I have moved to study them I not forgot your knowledge in ancient languages I need your help for[print][b] Some parts of document are to damaged to be readable[/b][arcane] Arghis[color=Red] k h[color=dark slate blue] ark[color=#004000] fido[/color][hand] please come as fast as possible my friend[print][b] The bottom of letter seems deliberatly shredded What is it
Definition: media-tags.txt:28
init_mon_info
static void init_mon_info(void)
Definition: readable.cpp:877
GOD_PATHS
#define GOD_PATHS
Definition: god.h:19
PATH_RESTORE
#define PATH_RESTORE
Definition: spells.h:21
object::path_attuned
uint32_t path_attuned
Definition: object.h:353
get_empty_book
static title * get_empty_book(void)
Definition: readable.cpp:584
MSG_TYPE_PAPER_LETTER_OLD_2
#define MSG_TYPE_PAPER_LETTER_OLD_2
Definition: newclient.h:443
PATH_INFO
#define PATH_INFO
Definition: spells.h:26
LOG
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.cpp:51
arraysize
#define arraysize(arrayname)
Definition: readable.cpp:98
MSG_TYPE_BOOK_SPELL_SUMMONER
#define MSG_TYPE_BOOK_SPELL_SUMMONER
Definition: newclient.h:422
book_author
static const char *const book_author[]
Definition: readable.cpp:402
of_close
int of_close(OutputFile *of)
Definition: output_file.cpp:61
SET_FLAG
#define SET_FLAG(xyz, p)
Definition: define.h:224
MONSTER_EXCLUDE_FROM_READABLE_KEY
#define MONSTER_EXCLUDE_FROM_READABLE_KEY
Definition: object.h:587
free_all_readable
void free_all_readable(void)
Definition: readable.cpp:1958
PATH_ELEC
#define PATH_ELEC
Definition: spells.h:16
monsters
static std::vector< object * > monsters
Definition: readable.cpp:142
of_open
FILE * of_open(OutputFile *of, const char *fname)
Definition: output_file.cpp:30
GLOVES
@ GLOVES
Definition: object.h:218
arttypename::name
const char * name
Definition: readable.cpp:129
GIRDLE
@ GIRDLE
Definition: object.h:228
PATH_FIRE
#define PATH_FIRE
Definition: spells.h:14
QUERY_FLAG
#define QUERY_FLAG(xyz, p)
Definition: define.h:226
recipe::arch_names
size_t arch_names
Definition: recipe.h:12
archininventory.arch
arch
DIALOGCHECK MINARGS 1 MAXARGS 1
Definition: archininventory.py:16
archetypes_for_each
void archetypes_for_each(arch_op op)
Definition: assets.cpp:301
spellpathnames
const char *const spellpathnames[NRSPELLPATHS]
Definition: init.cpp:239
mon_book_name
static const char *const mon_book_name[]
Definition: readable.cpp:258
stringbuffer_append_printf
void stringbuffer_append_printf(StringBuffer *sb, const char *format,...)
Definition: stringbuffer.cpp:138
path_author
static const char *const path_author[]
Definition: readable.cpp:183
Messages::random
GeneralMessage * random()
Definition: Messages.cpp:50
artifact_msg
static StringBuffer * artifact_msg(unsigned int level, size_t booksize)
Definition: readable.cpp:1470
MSG_TYPE_PAPER
#define MSG_TYPE_PAPER
Definition: newclient.h:386
MSG_TYPE_PAPER_LETTER_NEW_2
#define MSG_TYPE_PAPER_LETTER_NEW_2
Definition: newclient.h:445
object::arch
struct archetype * arch
Definition: object.h:422
stringbuffer_new
StringBuffer * stringbuffer_new(void)
Definition: stringbuffer.cpp:57
MSG_TYPE_BOOK_QUARTO_1
#define MSG_TYPE_BOOK_QUARTO_1
Definition: newclient.h:416
PATH_SELF
#define PATH_SELF
Definition: spells.h:18
MSG_TYPE_SIGN_BASIC
#define MSG_TYPE_SIGN_BASIC
Definition: newclient.h:455
artifactlist::items
std::vector< artifact * > items
Definition: artifact.h:28
gods_book_name
static const char *const gods_book_name[]
Definition: readable.cpp:298
ARMOUR
@ ARMOUR
Definition: object.h:125
MSG_TYPE_PAPER_SCROLL_NEW_2
#define MSG_TYPE_PAPER_SCROLL_NEW_2
Definition: newclient.h:451
WEAPON
@ WEAPON
Definition: object.h:124
MSG_TYPE_PAPER_SCROLL_OLD_2
#define MSG_TYPE_PAPER_SCROLL_OLD_2
Definition: newclient.h:449
MSG_TYPE_PAPER_NOTE_2
#define MSG_TYPE_PAPER_NOTE_2
Definition: newclient.h:440
guildjoin.ob
ob
Definition: guildjoin.py:42
Settings::localdir
const char * localdir
Definition: global.h:249
MSG_TYPE_CARD_MONEY_2
#define MSG_TYPE_CARD_MONEY_2
Definition: newclient.h:435
MSG_TYPE_MONUMENT_STATUE_3
#define MSG_TYPE_MONUMENT_STATUE_3
Definition: newclient.h:467
artifact::item
object * item
Definition: artifact.h:15
commongive.inv
inv
Definition: commongive.py:29
god_info_msg
static StringBuffer * god_info_msg(int level, size_t booksize, object *book)
Definition: readable.cpp:1806
add_abilities
void add_abilities(object *op, const object *change)
Definition: artifact.cpp:320
AMULET
@ AMULET
Definition: object.h:144
recipelist::items
recipe * items
Definition: recipe.h:40
archetype::head
archetype * head
Definition: object.h:476
MSG_TYPE_PAPER_LETTER_OLD_1
#define MSG_TYPE_PAPER_LETTER_OLD_1
Definition: newclient.h:442
strtoktolin
char * strtoktolin(const char *buf1, const char *buf2, char *retbuf, size_t size)
Definition: readable.cpp:675
MSG_TYPE_SIGN_DIR_RIGHT
#define MSG_TYPE_SIGN_DIR_RIGHT
Definition: newclient.h:457
mon
object * mon
Definition: comet_perf.cpp:75
gods_author
static const char *const gods_author[]
Definition: readable.cpp:314
title::name
const char * name
Definition: readable.cpp:109
MSG_TYPE_CARD_SIMPLE_2
#define MSG_TYPE_CARD_SIMPLE_2
Definition: newclient.h:426
SKILL
@ SKILL
Definition: object.h:148
titlelist
Definition: readable.cpp:121
Ice.tmp
int tmp
Definition: Ice.py:207
object_copy
void object_copy(const object *src_ob, object *dest_ob)
Definition: object.cpp:1192
recipe::arch_name
char ** arch_name
Definition: recipe.h:13
artifact::allowed
std::vector< sstring > allowed
Definition: artifact.h:18
MSG_TYPE_BOOK_CLASP_2
#define MSG_TYPE_BOOK_CLASP_2
Definition: newclient.h:413
MSG_TYPE_CARD
#define MSG_TYPE_CARD
Definition: newclient.h:385
prayers
int prayers
Definition: readable.cpp:1547
booksize
size_t booksize
Definition: readable.cpp:1551
MSGTYPE_MONSTER
#define MSGTYPE_MONSTER
Definition: readable.cpp:81
MSG_TYPE_BOOK_ELEGANT_1
#define MSG_TYPE_BOOK_ELEGANT_1
Definition: newclient.h:414
object::title
sstring title
Definition: object.h:325
object_get_value
const char * object_get_value(const object *op, const char *const key)
Definition: object.cpp:4337
AssetsManager.h
GOD_BLESSED
#define GOD_BLESSED
Definition: god.h:17
object::level
int16_t level
Definition: object.h:361
buf
StringBuffer * buf
Definition: readable.cpp:1552
title::size
size_t size
Definition: readable.cpp:113
getManager
AssetsManager * getManager()
Definition: assets.cpp:305
object_insert_in_ob
object * object_insert_in_ob(object *op, object *where)
Definition: object.cpp:2848
recipelist::total_chance
int total_chance
Definition: recipe.h:38
HUGE_BUF
#define HUGE_BUF
Definition: define.h:37
PATH_ABJURE
#define PATH_ABJURE
Definition: spells.h:20
readable_message_types
static const readable_message_type readable_message_types[]
Definition: readable.cpp:476
GOD_HOLYWORD
#define GOD_HOLYWORD
Definition: god.h:14
titlelist::first_book
title * first_book
Definition: readable.cpp:123
name
Plugin animator file specs[Config] name
Definition: animfiles.txt:4
linked_char
Definition: global.h:96
recipe::is_combination
int is_combination
Definition: recipe.h:31
find_title
static title * find_title(const object *book, int msgtype)
Definition: readable.cpp:926
get_next_mon
static object * get_next_mon(const object *tmp)
Definition: readable.cpp:1307
MSG_TYPE_PAPER_SCROLL_OLD_1
#define MSG_TYPE_PAPER_SCROLL_OLD_1
Definition: newclient.h:448
need_to_write_bookarchive
static int need_to_write_bookarchive
Definition: readable.cpp:144
MSG_TYPE_CARD_STRANGE_1
#define MSG_TYPE_CARD_STRANGE_1
Definition: newclient.h:431
PATH_MISSILE
#define PATH_MISSILE
Definition: spells.h:17
CLOAK
@ CLOAK
Definition: object.h:209
stringbuffer_finish
char * stringbuffer_finish(StringBuffer *sb)
Definition: stringbuffer.cpp:76
MSGTYPE_MSGFILE
#define MSGTYPE_MSGFILE
Definition: readable.cpp:91
GeneralMessage
Definition: book.h:44
object_free_drop_inventory
void object_free_drop_inventory(object *ob)
Definition: object.cpp:1555
add_author
static void add_author(object *op, int msgtype)
Definition: readable.cpp:1014
HELMET
@ HELMET
Definition: object.h:141
get_rand_god
const object * get_rand_god(void)
Definition: holy.cpp:73
object::subtype
uint8_t subtype
Definition: object.h:349
recipelist
Definition: recipe.h:37
MSG_TYPE_MONUMENT_GRAVESTONE_1
#define MSG_TYPE_MONUMENT_GRAVESTONE_1
Definition: newclient.h:468
PATH_WOUNDING
#define PATH_WOUNDING
Definition: spells.h:30
MSG_TYPE_MONUMENT
#define MSG_TYPE_MONUMENT
Definition: newclient.h:388
PATH_CREATE
#define PATH_CREATE
Definition: spells.h:24
MSG_TYPE_CARD_STRANGE_2
#define MSG_TYPE_CARD_STRANGE_2
Definition: newclient.h:432
query_name
void query_name(const object *op, char *buf, size_t size)
Definition: item.cpp:592
MSG_TYPE_SIGN_DIR_BOTH
#define MSG_TYPE_SIGN_DIR_BOTH
Definition: newclient.h:458
MSG_TYPE_MONUMENT_GRAVESTONE_2
#define MSG_TYPE_MONUMENT_GRAVESTONE_2
Definition: newclient.h:469
get_titlelist
static titlelist * get_titlelist(int i)
Definition: readable.cpp:602
PATH_DETONATE
#define PATH_DETONATE
Definition: spells.h:22
stringbuffer_finish_shared
sstring stringbuffer_finish_shared(StringBuffer *sb)
Definition: stringbuffer.cpp:85
MSG_TYPE_SIGN_DIR_LEFT
#define MSG_TYPE_SIGN_DIR_LEFT
Definition: newclient.h:456
archetype::clone
object clone
Definition: object.h:478
of
a copper bar weighs and has a value of
Definition: ore.txt:3
add_string
sstring add_string(const char *str)
Definition: shstr.cpp:124
MSG_TYPE_BOOK_CLASP_1
#define MSG_TYPE_BOOK_CLASP_1
Definition: newclient.h:412
book_descrpt
static const char *const book_descrpt[]
Definition: readable.cpp:449
ROD
@ ROD
Definition: object.h:114
init_readable
void init_readable(void)
Definition: readable.cpp:895
titlelist::next
titlelist * next
Definition: readable.cpp:124
done
int done
Definition: readable.cpp:1553
init_book_archive
static void init_book_archive(void)
Definition: readable.cpp:729
tailor_readable_ob
void tailor_readable_ob(object *book, int msg_type)
Definition: readable.cpp:1876
spellpath_msg
static StringBuffer * spellpath_msg(int level, size_t booksize, StringBuffer *buf)
Definition: readable.cpp:1589
navar-midane_pickup.msg
list msg
Definition: navar-midane_pickup.py:13
object::value
int32_t value
Definition: object.h:360
FREE_AND_COPY
#define FREE_AND_COPY(sv, nv)
Definition: global.h:204
MSG_TYPE_MONUMENT_STATUE_2
#define MSG_TYPE_MONUMENT_STATUE_2
Definition: newclient.h:466
add_book_to_list
static void add_book_to_list(const object *book, int msgtype)
Definition: readable.cpp:1086
object::type
uint8_t type
Definition: object.h:348
message
TIPS on SURVIVING Crossfire is populated with a wealth of different monsters These monsters can have varying immunities and attack types In some of them can be quite a bit smarter than others It will be important for new players to learn the abilities of different monsters and learn just how much it will take to kill them This section discusses how monsters can interact with players Most monsters in the game are out to mindlessly kill and destroy the players These monsters will help boost a player s after he kills them When fighting a large amount of monsters in a single attempt to find a narrower hallway so that you are not being attacked from all sides Charging into a room full of Beholders for instance would not be open the door and fight them one at a time For there are several maps designed for them Find these areas and clear them out All throughout these a player can find signs and books which they can read by stepping onto them and hitting A to apply the book sign These messages will help the player to learn the system One more always keep an eye on your food If your food drops to your character will soon so BE CAREFUL ! NPCs Non Player Character are special monsters which have intelligence Players may be able to interact with these monsters to help solve puzzles and find items of interest To speak with a monster you suspect to be a simply move to an adjacent square to them and push the double ie Enter your message
Definition: survival-guide.txt:34
PATH_SUMMON
#define PATH_SUMMON
Definition: spells.h:19
MSG_TYPE_CARD_ELEGANT_1
#define MSG_TYPE_CARD_ELEGANT_1
Definition: newclient.h:428
recipe::index
int index
Definition: recipe.h:18
object_create_arch
object * object_create_arch(archetype *at)
Definition: arch.cpp:298
book.h
MAX_TITLE_CHECK
#define MAX_TITLE_CHECK
Definition: readable.cpp:76
title
Definition: readable.cpp:108
artifactlist
Definition: artifact.h:24
arttypename
Definition: readable.cpp:128
PATH_TRANSFER
#define PATH_TRANSFER
Definition: spells.h:28
disinfect.count
int count
Definition: disinfect.py:7
FLAG_UNAGGRESSIVE
#define FLAG_UNAGGRESSIVE
Definition: define.h:272
MSG_TYPE_CARD_ELEGANT_3
#define MSG_TYPE_CARD_ELEGANT_3
Definition: newclient.h:430
artifact_describe
static StringBuffer * artifact_describe(const artifact *art, const artifactlist *al, int message, int art_name, int separator)
Definition: readable.cpp:1387
PATH_FROST
#define PATH_FROST
Definition: spells.h:15
archetype
Definition: object.h:474
MSG_TYPE_CARD_MONEY_3
#define MSG_TYPE_CARD_MONEY_3
Definition: newclient.h:436
MSG_TYPE_BOOK_SPELL_EVOKER
#define MSG_TYPE_BOOK_SPELL_EVOKER
Definition: newclient.h:418
ARROW
@ ARROW
Definition: object.h:122
MSG_TYPE_MONUMENT_GRAVESTONE_3
#define MSG_TYPE_MONUMENT_GRAVESTONE_3
Definition: newclient.h:470
living::sp
int16_t sp
Definition: living.h:42
get_message_body
sstring get_message_body(const GeneralMessage *message)
Definition: readable.cpp:2061
stringbuffer_append_string
void stringbuffer_append_string(StringBuffer *sb, const char *str)
Definition: stringbuffer.cpp:95
MSG_TYPE_MONUMENT_STONE_3
#define MSG_TYPE_MONUMENT_STONE_3
Definition: newclient.h:464
MSG_TYPE_MONUMENT_WALL_3
#define MSG_TYPE_MONUMENT_WALL_3
Definition: newclient.h:473
titlelist::number
int number
Definition: readable.cpp:122
BOOK
@ BOOK
Definition: object.h:119
trim
const char * trim(const char *buf)
Definition: readable.cpp:652
MSG_TYPE_PAPER_ENVELOPE_2
#define MSG_TYPE_PAPER_ENVELOPE_2
Definition: newclient.h:447
MSG_TYPE_CARD_ELEGANT_2
#define MSG_TYPE_CARD_ELEGANT_2
Definition: newclient.h:429
MSG_TYPE_PAPER_NOTE_1
#define MSG_TYPE_PAPER_NOTE_1
Definition: newclient.h:439
RING
@ RING
Definition: object.h:190
MSG_TYPE_BOOK_SPELL_SORCERER
#define MSG_TYPE_BOOK_SPELL_SORCERER
Definition: newclient.h:421
MSG_TYPE_BOOK_SPELL_PYRO
#define MSG_TYPE_BOOK_SPELL_PYRO
Definition: newclient.h:420
PATH_TRANSMUTE
#define PATH_TRANSMUTE
Definition: spells.h:27
title::archname
const char * archname
Definition: readable.cpp:111
MSGTYPE_GODS
#define MSGTYPE_GODS
Definition: readable.cpp:89
MSG_TYPE_SIGN_MAGIC_MOUTH
#define MSG_TYPE_SIGN_MAGIC_MOUTH
Definition: newclient.h:459
FLAG_MONSTER
#define FLAG_MONSTER
Definition: define.h:245
mon_desc
static StringBuffer * mon_desc(const object *mon)
Definition: readable.cpp:1289
MAX_BUF
#define MAX_BUF
Definition: define.h:35
strlcpy
size_t strlcpy(char *dst, const char *src, size_t size)
Definition: porting.cpp:222
object_new
object * object_new(void)
Definition: object.cpp:1268
describe_item
StringBuffer * describe_item(const object *op, const object *owner, int use_media_tags, StringBuffer *buf)
Definition: item.cpp:955
create_archetype
object * create_archetype(const char *name)
Definition: arch.cpp:278
path
pluglist shows those as well as a short text describing each the list will simply appear empty The keyword for the Python plugin is Python plugout< keyword > Unloads a given identified by its _keyword_ So if you want to unload the Python you need to do plugout Python plugin< libname > Loads a given whose _filename_ is libname So in the case of you d have to do a plugin cfpython so Note that all filenames are relative to the default plugin path(SHARE/plugins). Console messages. ----------------- When Crossfire starts
object::weight
int32_t weight
Definition: object.h:375
free_string
void free_string(sstring str)
Definition: shstr.cpp:280
RANDOM
#define RANDOM()
Definition: define.h:644
arttypename::type
int type
Definition: readable.cpp:130
PATH_DEATH
#define PATH_DEATH
Definition: spells.h:31
recipe
Definition: recipe.h:10
art_book_name
static const char *const art_book_name[]
Definition: readable.cpp:227
GOD_RESISTANCES
#define GOD_RESISTANCES
Definition: god.h:15
MSG_TYPE_CARD_MONEY_1
#define MSG_TYPE_CARD_MONEY_1
Definition: newclient.h:434
PATH_TELE
#define PATH_TELE
Definition: spells.h:25
llevInfo
@ llevInfo
Definition: logger.h:12
fatal
void fatal(enum fatal_error err)
Definition: utils.cpp:570
find_archetype_by_object_name
archetype * find_archetype_by_object_name(const char *name)
Definition: arch.cpp:53
MSG_TYPE_PAPER_ENVELOPE_1
#define MSG_TYPE_PAPER_ENVELOPE_1
Definition: newclient.h:446
spells.h
object::name
sstring name
Definition: object.h:319
get_message_title
sstring get_message_title(const GeneralMessage *message)
Definition: readable.cpp:2052
did_first_sp
int did_first_sp
Definition: readable.cpp:1548
MSGTYPE_LIB
#define MSGTYPE_LIB
Definition: readable.cpp:79
MSG_TYPE_CARD_SIMPLE_3
#define MSG_TYPE_CARD_SIMPLE_3
Definition: newclient.h:427
spellpathdef
static const uint32_t spellpathdef[NRSPELLPATHS]
Definition: readable.cpp:149
stringbuffer_delete
void stringbuffer_delete(StringBuffer *sb)
Definition: stringbuffer.cpp:71
msgfile_msg
static StringBuffer * msgfile_msg(object *book, size_t booksize)
Definition: readable.cpp:1757
PATH_PROT
#define PATH_PROT
Definition: spells.h:13
recipe::chance
int chance
Definition: recipe.h:14
artifact::chance
uint16_t chance
Definition: artifact.h:16
MSGTYPE_ALCHEMY
#define MSGTYPE_ALCHEMY
Definition: readable.cpp:87
sstring
const typedef char * sstring
Definition: sstring.h:2
MSG_TYPE_PAPER_LETTER_NEW_1
#define MSG_TYPE_PAPER_LETTER_NEW_1
Definition: newclient.h:444
give.op
op
Definition: give.py:33
autojail.value
value
Definition: autojail.py:6
Floor.t
t
Definition: Floor.py:62
find_archetype
archetype * find_archetype(const char *name)
Definition: assets.cpp:266
MSG_TYPE_BOOK_ELEGANT_2
#define MSG_TYPE_BOOK_ELEGANT_2
Definition: newclient.h:415
strtoint
int strtoint(const char *buf)
Definition: recipe.cpp:709
GOD_ENEMY
#define GOD_ENEMY
Definition: god.h:13
PATH_MIND
#define PATH_MIND
Definition: spells.h:23
object_set_msg
void object_set_msg(object *op, const char *msg)
Definition: object.cpp:4802
object::msg
sstring msg
Definition: object.h:330
BOOK_BUF
#define BOOK_BUF
Definition: book.h:16
title::next
title * next
Definition: readable.cpp:115
assets.h
artifactlist::total_chance
uint16_t total_chance
Definition: artifact.h:26
MSGTYPE_ARTIFACT
#define MSGTYPE_ARTIFACT
Definition: readable.cpp:83
npc_dialog.index
int index
Definition: npc_dialog.py:102
do_monster
static void do_monster(archetype *at)
Definition: readable.cpp:865
art_name_array
static const arttypename art_name_array[]
Definition: readable.cpp:207
OutputFile
Definition: output_file.h:41
MSG_TYPE_MONUMENT_STONE_2
#define MSG_TYPE_MONUMENT_STONE_2
Definition: newclient.h:463
stringbuffer_append_stringbuffer
void stringbuffer_append_stringbuffer(StringBuffer *sb, const StringBuffer *sb2)
Definition: stringbuffer.cpp:165
new_text_name
static void new_text_name(object *book, int msgtype)
Definition: readable.cpp:965
heavy_book_name
static const char *const heavy_book_name[]
Definition: readable.cpp:388
level
int level
Definition: readable.cpp:1550
MSGTYPE_SPELLPATH
#define MSGTYPE_SPELLPATH
Definition: readable.cpp:85
readable_message_type
Definition: book.h:36
make_face_from_files.int
int
Definition: make_face_from_files.py:32
formula_author
static const char *const formula_author[]
Definition: readable.cpp:347
booklist
static titlelist * booklist
Definition: readable.cpp:139
Face
Definition: face.h:14
MSG_TYPE_MONUMENT_STATUE_1
#define MSG_TYPE_MONUMENT_STATUE_1
Definition: newclient.h:465
title::level
unsigned int level
Definition: readable.cpp:112
MSG_TYPE_MONUMENT_WALL_1
#define MSG_TYPE_MONUMENT_WALL_1
Definition: newclient.h:471
get_readable_message_type
const readable_message_type * get_readable_message_type(object *readable)
Definition: readable.cpp:2039
art_author
static const char *const art_author[]
Definition: readable.cpp:243
last_readable_subtype
static const int last_readable_subtype
Definition: readable.cpp:540
get_random_mon
object * get_random_mon(int level)
Definition: readable.cpp:1260
book_overflow
int book_overflow(const char *buf1, const char *buf2, size_t booksize)
Definition: readable.cpp:710
FOOD
@ FOOD
Definition: object.h:117
MSG_TYPE_PAPER_NOTE_3
#define MSG_TYPE_PAPER_NOTE_3
Definition: newclient.h:441
MSG_TYPE_BOOK_QUARTO_2
#define MSG_TYPE_BOOK_QUARTO_2
Definition: newclient.h:417
try_find_archetype
archetype * try_find_archetype(const char *name)
Definition: assets.cpp:270
animate.event
event
DIALOGCHECK MINARGS 1 MAXARGS 2
Definition: animate.py:17
mon_info_msg
static StringBuffer * mon_info_msg(int level, size_t booksize, object *book)
Definition: readable.cpp:1330
unique_book
static int unique_book(const object *book, int msgtype)
Definition: readable.cpp:1062
recipe::ingred
linked_char * ingred
Definition: recipe.h:22
MSG_TYPE_CARD_SIMPLE_1
#define MSG_TYPE_CARD_SIMPLE_1
Definition: newclient.h:425
light_book_name
static const char *const light_book_name[]
Definition: readable.cpp:369
MSG_TYPE_MONUMENT_STONE_1
#define MSG_TYPE_MONUMENT_STONE_1
Definition: newclient.h:462
archetype::name
sstring name
Definition: object.h:475
recipe::next
recipe * next
Definition: recipe.h:24
recipe::skill
sstring skill
Definition: recipe.h:26
living::grace
int16_t grace
Definition: living.h:44
object::stats
living stats
Definition: object.h:378
MSG_TYPE_SIGN
#define MSG_TYPE_SIGN
Definition: newclient.h:387
MSG_TYPE_BOOK
#define MSG_TYPE_BOOK
Definition: newclient.h:384
artifact
Definition: artifact.h:14
object_set_value
int object_set_value(object *op, const char *key, const char *value, int add_key)
Definition: object.cpp:4490
player::title
char title[BIG_NAME]
Definition: player.h:184
buf_overflow
int buf_overflow(const char *buf1, const char *buf2, size_t bufsize)
Definition: shstr.cpp:398
describe_god
int describe_god(const object *god, int what, StringBuffer *buf, size_t maxlen)
Definition: holy.cpp:109
add_book
static void add_book(title *book, int type, const char *fname, int lineno)
Definition: readable.cpp:851
MSG_TYPE_PAPER_SCROLL_MAGIC
#define MSG_TYPE_PAPER_SCROLL_MAGIC
Definition: newclient.h:452
BOOTS
@ BOOTS
Definition: object.h:217
make_formula_book
static void make_formula_book(object *book, int level)
Definition: readable.cpp:1629
StringBuffer
Definition: stringbuffer.cpp:25
FLAG_CHANGING
#define FLAG_CHANGING
Definition: define.h:263
title::msg_index
int msg_index
Definition: readable.cpp:114
SPELL
@ SPELL
Definition: object.h:219
MSG_TYPE_PAPER_SCROLL_NEW_1
#define MSG_TYPE_PAPER_SCROLL_NEW_1
Definition: newclient.h:450
sp_params
static struct @0 sp_params
SHIELD
@ SHIELD
Definition: object.h:140
text
Crossfire Protocol most of the time after the actual code was already omit certain important and possibly make life miserable any new developer or curious player should be able to find most of the relevant information here If inconsistencies are found or this documentation proves to be consider the latest server side protocol code in the public source code repository as the authoritative reference Introduction If you were ever curious enough to telnet or netcat to a Crossfire chances are you were sorely disappointed While the protocol may seem to use plain text at it actually uses a mix of ASCII and binary data This handbook attempts to document various aspects of the Crossfire protocol As consult the README file to find out how to get in touch with helpful people via mailing and more History the communications plan was set to be a text based system It was up to the server and client to parse these messages and determine what to do These messages were assumed to be line per message At a reasonably early stage of Eric Anderson wrote a then the data itself you could send many data and after the other end could decode these commands This works fairly but I think the creation of numerous sub packets has some performance hit the eutl was not especially well so writing a client for a different platform became more Eric left to work on other products shortly after writing his which didn t really leave anyone with a full understanding of the socket code I have decided to remove the eutl dependency At least one advantage is that having this network related code directly in the client and server makes error handling a bit easier cleaner Packet Format the outside packet method the byte size for the size information is not included here Eutl originally used bytes for the size to bytes seems it makes a least some sense The actual data is something of the nature of the commands listed below It is a text followed by possible other data The remaining data can be binary it is up to the client and server to decode what it sent The commands as described below is just the data portion of the packet If writing a new remember that you must take into account the size of the packet There is no termination of other than knowing how long it should be For most everything that is sent is text This is more or less how things worked under except it packed the ints into bytes in a known order In some we handle ints as in they are sent as binary information How any command handles it is detailed below in the command description The S and C represent the direction of the we use MSB as well as any ints or shorts that get sent inside the packets All packets are defined to have at least one word of text
Definition: protocol.txt:84
OUT_OF_MEMORY
@ OUT_OF_MEMORY
Definition: define.h:48
BOOKSIZE
#define BOOKSIZE(xyz)
Definition: book.h:31
max_titles
static const int max_titles[6]
Definition: readable.cpp:543
nstrtok
int nstrtok(const char *buf1, const char *buf2)
Definition: readable.cpp:633
living.h
find_artifactlist
artifactlist * find_artifactlist(int type)
Definition: artifact.cpp:574
artifactlist::type
uint8_t type
Definition: artifact.h:25
MSG_TYPE_CARD_STRANGE_3
#define MSG_TYPE_CARD_STRANGE_3
Definition: newclient.h:433
get_message_face
const Face * get_message_face(const GeneralMessage *message)
Definition: readable.cpp:2070
pnum
uint32_t pnum
Definition: readable.cpp:1549
llevDebug
@ llevDebug
Definition: logger.h:13
NRSPELLPATHS
#define NRSPELLPATHS
Definition: spells.h:40
is_valid_types_gen.type
list type
Definition: is_valid_types_gen.py:25
GOD_SACRED
#define GOD_SACRED
Definition: god.h:16
FLAG_IDENTIFIED
#define FLAG_IDENTIFIED
Definition: define.h:261
recipe::cauldron
sstring cauldron
Definition: recipe.h:27
recipe::title
sstring title
Definition: recipe.h:11
level
Definition: level.py:1