Crossfire Server, Trunk  1.75.0
quest.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 
28 #include "global.h"
29 
30 #include <assert.h>
31 #include <stdlib.h>
32 #include <string.h>
33 
34 #include "output_file.h"
35 #include "sproto.h"
36 
37 #include "quest.h"
38 #include "assets.h"
39 
40 /* Media tags for information messages prefix. */
41 #define TAG_START "[color=#aa55ff]"
42 #define TAG_END "[/color]"
43 
45 #define QC_CAN_RESTART -1
46 
48 struct quest_state {
50  int state;
55 };
56 
58 struct quest_player {
62 };
63 
65 static quest_player *player_states = NULL;
66 
74  for (const auto qs : quest->steps) {
75  if (qs->step == step)
76  return qs;
77  }
78 
79  LOG(llevError, "quest %s has no required step %d\n", quest->quest_code, step);
80  return NULL;
81 }
82 
88  quest_state *qs = static_cast<quest_state *>(calloc(1, sizeof(quest_state)));
89  if (qs == NULL)
91  return qs;
92 }
93 
99  FILE *file;
100  char final[MAX_BUF], read[MAX_BUF], data[MAX_BUF];
101  quest_state *qs = NULL, *prev = NULL;
102  int warned = 0, state;
103  quest_definition *quest = NULL;
104 
105  snprintf(final, sizeof(final), "%s/%s/%s/%s.quest", settings.localdir, settings.playerdir, pq->player_name, pq->player_name);
106 
107  file = fopen(final, "r");
108  if (!file) {
109  /* no quest yet, no big deal */
110  return;
111  }
112 
113  while (fgets(read, sizeof(read), file) != NULL) {
114  if (sscanf(read, "quest %s\n", data)) {
115  qs = get_new_quest_state();
116  qs->code = add_string(data);
117  quest = quest_get_by_code(qs->code);
118  continue;
119  }
120 
121  if (!qs) {
122  if (!warned)
123  LOG(llevError, "quest: invalid file format for %s\n", final);
124  warned = 1;
125  continue;
126  }
127 
128  if (sscanf(read, "state %d\n", &state)) {
129  qs->state = state;
130  if (quest != NULL && state != -1) {
131  quest_step_definition *step = quest_get_step(quest, state);
132  if (step == NULL) {
133  LOG(llevError, "invalid quest step %d for %s in %s\n", state, quest->quest_code, final);
134  }
135  else if (step->is_completion_step)
136  qs->is_complete = 1;
137  }
138  continue;
139  }
140  if (strcmp(read, "end_quest\n") == 0) {
141  if (quest == NULL) {
142  LOG(llevDebug, "Unknown quest %s in quest file %s\n", qs->code, final);
143  free(qs);
144  } else {
145  if (prev == NULL) {
146  pq->quests = qs;
147  } else {
148  prev->next = qs;
149  }
150  prev = qs;
151  }
152  qs = NULL;
153  continue;
154  }
155  if (sscanf(read, "completed %d\n", &state)) {
156  qs->was_completed = state ? 1 : 0;
157  continue;
158  }
159 
160  LOG(llevError, "quest: invalid line in %s: %s\n", final, read);
161  }
162 
163  if (qs)
164  LOG(llevError, "quest: missing end_quest in %s\n", final);
165 
166  fclose(file);
167 }
168 
173 static void quest_write_player_data(const quest_player *pq) {
174  FILE *file;
175  OutputFile of;
176  char fname[MAX_BUF];
177  const quest_state *state;
178 
179  snprintf(fname, sizeof(fname), "%s/%s/%s/%s.quest", settings.localdir, settings.playerdir, pq->player_name, pq->player_name);
180 
181  file = of_open(&of, fname);
182  if (file == NULL) {
183  draw_ext_info(NDI_UNIQUE | NDI_ALL_DMS, 0, NULL, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_LOADSAVE, "File write error on server!");
184  return;
185  }
186 
187  state = pq->quests;
188 
189  while (state) {
190  fprintf(file, "quest %s\n", state->code);
191  fprintf(file, "state %d\n", state->state);
192  fprintf(file, "completed %d\n", state->was_completed);
193  fprintf(file, "end_quest\n");
194  state = state->next;
195  }
196 
197  if (!of_close(&of)) {
198  draw_ext_info(NDI_UNIQUE | NDI_ALL_DMS, 0, NULL, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_LOADSAVE, "File write error on server!");
199  return;
200  }
202 }
203 
211  quest_state *qs = pq->quests;
212 
213  while (qs) {
214  if (qs->code == name)
215  return qs;
216  qs = qs->next;
217  }
218 
219  return NULL;
220 }
221 
229  quest_state *qs = get_state(pq, name);
230 
231  if (!qs) {
232  qs = static_cast<quest_state *>(calloc(1, sizeof(quest_state)));
233  if (!qs)
235  qs->code = add_refcount(name);
236  if (pq->quests != NULL) {
237  quest_state *last;
238  for (last = pq->quests ; last->next != NULL; last = last->next)
239  ;
240  last->next = qs;
241  } else {
242  pq->quests = qs;
243  }
244  }
245 
246  return qs;
247 }
248 
256 
257  while (pq) {
258  if (pq->player_name == pl->ob->name)
259  return pq;
260  pq = pq->next;
261  }
262 
263  return NULL;
264 }
265 
273  quest_player *pq = get_quest(pl);
274 
275  if (!pq) {
276  pq = static_cast<quest_player *>(calloc(1, sizeof(quest_player)));
277  if (!pq)
279  pq->player_name = add_refcount(pl->ob->name);
280  pq->next = player_states;
281  player_states = pq;
283  }
284 
285  return pq;
286 }
287 
288 /* quest_set_state can call itself through the function update_quests, so it needs to be declared here */
289 static void quest_set_state(player* dm, player *pl, sstring quest_code, int state, int started);
290 
297 static int evaluate_quest_conditions(const std::vector<quest_condition *> conditions, player *pl) {
298  int current_step;
299 
300  if (conditions.empty())
301  return 0;
302  for (const auto cond : conditions) {
303  current_step = quest_get_player_state(pl, cond->quest_code);
304  if (cond->minstep < 0 && cond->maxstep < 0) {
305  /* we are checking for the quest to have been completed. */
306  if (!quest_was_completed(pl, cond->quest_code))
307  return 0;
308  } else {
309  if (current_step < cond->minstep || current_step > cond->maxstep)
310  return 0;
311  }
312  }
313  return 1;
314 }
315 
316 static void do_update(const quest_definition *quest, void *user) {
317  player *pl = (player *)user;
318  int new_step = 0;
319  for (const quest_step_definition *step : quest->steps) {
320  if (evaluate_quest_conditions(step->conditions, pl)) {
321  new_step=new_step<step->step?step->step:new_step;
322  }
323  }
324  if (new_step > 0) {
325  int current_step = quest_get_player_state(pl, quest->quest_code);
326  if (new_step > current_step) {
327  quest_set_state(NULL, pl, quest->quest_code, new_step, 0);
328  }
329  }
330 }
331 
336 static void update_quests(player *pl) {
338 }
339 
348 static void quest_set_state(player* dm, player *pl, sstring quest_code, int state, int started) {
350  quest_state *qs = get_or_create_state(pq, quest_code);
351  quest_definition *quest = quest_find_by_code(quest_code);
353 
354  if (!quest) {
355  if (dm) {
356  draw_ext_info_format(NDI_UNIQUE, 0, dm->ob, MSG_TYPE_ADMIN_DM, MSG_TYPE_COMMAND_FAILURE, "Unknown quest %s!", quest_code);
357  } else {
358  LOG(llevError, "quest: asking for set_state of unknown quest %s!\n", quest_code);
359  }
360  return;
361  }
362 
363  if (!dm && state <= 0) {
364  LOG(llevDebug, "quest_set_player_state: warning: called with invalid state %d for quest %s, player %s\n", state, pl->ob->name, quest_code);
365  state = 100;
366  }
367 
368  if (started && qs->state == 0) {
369  if (!dm) {
370  LOG(llevDebug, "quest_set_player_state: warning: called for player %s not having started quest %s\n", pl->ob->name, quest_code);
371  }
372  }
373 
374  qs->state = state;
375  if (state == 0) {
376  qs->is_complete = 0;
377  return;
378  }
379 
380  step = quest_get_step(quest, state);
381  if (!step) {
382  if (dm) {
383  draw_ext_info_format(NDI_UNIQUE, 0, dm->ob, MSG_TYPE_ADMIN_DM, MSG_TYPE_COMMAND_FAILURE, "Couldn't find state definition %d for quest %s", state, quest_code);
384  } else {
385  LOG(llevError, "quest_set_player_state: couldn't find state definition %d for quest %s, player %s\n", state, quest_code, pl->ob->name);
386  }
387  return;
388  }
389 
390  if (step->is_completion_step) {
391  /* don't send an update note if the quest was already completed, this is just to show the outcome afterwards. */
392  if (!qs->is_complete)
394  qs->was_completed = 1;
395  if (quest->quest_restart)
396  qs->state = QC_CAN_RESTART;
397  else
398  qs->is_complete = 1;
399 
400  } else {
401  draw_ext_info_format(NDI_UNIQUE|NDI_DELAYED, 0, pl->ob, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_QUESTS, "New objective for the quest '%s':", quest->quest_title);
403  }
404 
405  if (pl->socket->notifications > 0) {
407 
408  if (qs->sent_to_client) {
409  SockList_AddString(sl, "updquest ");
410  } else {
411  SockList_AddString(sl, "addquest ");
412  }
413 
414  SockList_AddInt(sl, quest->client_code);
415  if (qs->sent_to_client == 0) {
416  SockList_AddLen16Data(sl, quest->quest_title, strlen(quest->quest_title));
417  if (quest->face && !(pl->socket->faces_sent[quest->face->number]&NS_FACESENT_FACE))
418  esrv_send_face(pl->socket, quest->face, 0);
419  SockList_AddInt(sl, quest->face ? quest->face->number : 0);
420  SockList_AddChar(sl, quest->quest_restart ? 1 : 0);
421  SockList_AddInt(sl, quest->parent ? quest->parent->client_code : 0);
422  }
423 
424  SockList_AddChar(sl, (step == NULL || step->is_completion_step) ? 1 : 0);
425  assert(step != NULL);
426  SockList_AddLen16Data(sl, step->step_description,
427  strlen(step->step_description));
428 
429  qs->sent_to_client = 1;
430  }
431 
432  if (pl->has_directory)
434  update_quests(pl);
435  LOG(llevDebug, "quest_set_player_state %s %s %d\n", pl->ob->name, quest_code, state);
436 
437 }
438 
446 static void quest_display(player *pl, quest_player *pq, int showall, const char* name) {
447  quest_state *state;
448  quest_definition *quest;
449  const char *restart;
450  int completed_count = 0, restart_count = 0, total_count = 0, current_count = 0;
451 
452  state = pq->quests;
453  while (state) {
454  quest = quest_find_by_code(state->code);
455  if (quest->parent == NULL) {
456  total_count++;
457  /* count up the number of completed quests first */
458  if (state->state == QC_CAN_RESTART) {
459  restart_count++;
460  completed_count++;
461  } else if (state->is_complete) {
462  completed_count++;
463  }
464  }
465  state = state->next;
466  }
467  if (completed_count > 0) {
468  if (!showall) {
469  if (restart_count > 0)
471  "%s completed %d out of %zu quests, of which %d may be restarted.", name, completed_count, quests_count(false), restart_count);
472  else
474  "%s completed %d quests", name, completed_count);
475  current_count = completed_count;
476  } else {
478  "%s completed the following quests:", name);
479  state = pq->quests;
480  while (state) {
481  quest = quest_find_by_code(state->code);
482  if (quest->parent == NULL) {
483  if (state->state == QC_CAN_RESTART || state->is_complete) {
484 
485  restart = state->state == QC_CAN_RESTART ? i18n(pl->ob, " (can be replayed)") : "";
487  "(%3d) %s%s", ++current_count, quest->quest_title, restart);
488  }
489  }
490  state = state->next;
491  }
492  }
493  }
494  if (total_count > completed_count) {
496  "%s started the following quests:", name);
497  state = pq->quests;
498  while (state) {
499  quest = quest_find_by_code(state->code);
500  if (quest->parent == NULL) {
501  if (state->state != QC_CAN_RESTART && state->is_complete == 0) {
502  quest = quest_find_by_code(state->code);
504  "(%3d) %s", ++current_count, quest->quest_title);
505  }
506  }
507  state = state->next;
508  }
509  }
510 }
511 
519 static void quest_list(player *pl, player *who, int showall, const char* name) {
520  quest_player *pq;
521 
522  /* ensure we load data if not loaded yet */
523  pq = get_or_create_quest(who);
524  if (!pq->quests) {
525  draw_ext_info_format(NDI_UNIQUE, 0, pl->ob, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_QUESTS, "%s didn't start any quest.", name);
526  return;
527  }
528 
529  quest_display(pl, pq, showall, name);
530 }
531 
539 static quest_state *get_quest_by_number(player *pl, int number) {
540  quest_state *state;
542  int questnum = 0;
543 
544  if (number <= 0 || !pq) {
545  return NULL;
546  }
547  /* count through completed quests first */
548  state = pq->quests;
549  while (state) {
550  /* count up the number of completed quests first */
551  if (!(quest_find_by_code(state->code)->parent) && (state->state == QC_CAN_RESTART || state->is_complete))
552  if (++questnum == number) return state;
553  state = state->next;
554  }
555  /* then active ones */
556  state = pq->quests;
557  while (state) {
558  /* count up the number of completed quests first */
559  if (!(quest_find_by_code(state->code)->parent) && state->state != QC_CAN_RESTART && state->is_complete ==0)
560  if (++questnum == number) return state;
561  state = state->next;
562  }
563  /* Ok, we didn't find our quest, return NULL*/
564  return NULL;
565 }
566 
574 static void quest_info(player *pl, player* who, quest_state *qs, int level) {
575  quest_definition *quest, *child;
576  quest_state *state;
578  const char *prefix;
579 
580  if (!qs) {
581  draw_ext_info(NDI_UNIQUE, 0, pl->ob, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_QUESTS, "Invalid quest number");
582  return;
583  }
584  quest = quest_find_by_code(qs->code);
585  if (!quest) {
586  /* already warned by quest_get */
587  draw_ext_info(NDI_UNIQUE, 0, pl->ob, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_QUESTS, "Quest: (internal error)");
588  return;
589  }
590 
592  if (QUERY_FLAG(pl->ob, FLAG_WIZ)) {
594  std::for_each(quest->steps.cbegin(), quest->steps.cend(), [&pl] (const auto &step) {
595  draw_ext_info_format(NDI_UNIQUE, 0, pl->ob, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_QUESTS, " " TAG_START "Step:" TAG_END " %d (%s)", step->step, step->step_description);
596  });
597  }
599 
601  if (qs->state == QC_CAN_RESTART || qs->is_complete) {
602  const char *restart = "";
603  if (quest->quest_restart)
604  restart = i18n(pl->ob, " (can be replayed)");
605  draw_ext_info_format(NDI_UNIQUE, 0, pl->ob, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_QUESTS, TAG_START "This quest has been completed%s.[/color]", restart);
606  }
607  prefix = "";
608  if (qs->state != QC_CAN_RESTART) {
609  /* ie, if we are in progress or completed for a non-restartable quest */
610  if (!step) {
611  /* already warned by quest_get_step */
612  draw_ext_info_format(NDI_UNIQUE, 0, pl->ob, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_QUESTS, " \nOutcome: (invalid quest)");
613  return;
614  }
615  if (level > 0) {
616  prefix = " * ";
617  } else if (qs->is_complete)
618  prefix = "Outcome";
619  else
620  prefix = "Current Status";
621  draw_ext_info_format(NDI_UNIQUE, 0, pl->ob, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_QUESTS, " \n" TAG_START "%s:" TAG_END " %s", prefix, step->step_description);
622  }
623 
624  /* ok, now check all of the player's other quests for any children, and print those in order */
625  state = pq->quests;
626  while (state) {
627  child = quest_find_by_code(state->code);
628  if (child->parent == quest)
629  quest_info(pl, who, state, level+1);
630  state = state->next;
631  }
632  return;
633 }
634 
639 static void free_state(quest_player *pq) {
640  quest_state *qs = pq->quests, *next;
641 
642  while (qs) {
643  next = qs->next;
644  free_string(qs->code);
645  free(qs);
646  qs = next;
647  }
648  pq->quests = NULL;
649 }
650 
651 
652 /* public functions */
653 
660 int quest_get_player_state(player *pl, sstring quest_code) {
662  quest_state *s = get_state(q, quest_code);
663  quest_definition *quest = quest_find_by_code(quest_code);
664 
665  if (!s)
666  return 0;
667 
668  if (s->state == QC_CAN_RESTART && quest && quest->quest_restart)
669  return 0;
670 
671  return s->state;
672 }
673 
680 void quest_start(player *pl, sstring quest_code, int state) {
681  quest_player *pq;
682  quest_state *q;
683  quest_definition *quest;
684 
685  quest = quest_find_by_code(quest_code);
686  if (!quest) {
687  LOG(llevError, "quest_start: requested unknown quest %s\n", quest_code);
688  return;
689  }
690  pq = get_or_create_quest(pl);
691  q = get_or_create_state(pq, quest_code);
692 
693  if (state <= 0) {
694  state = 100;
695  LOG(llevDebug, "quest_start: negative state %d for %s quest %s\n", state, pl->ob->name, quest_code);
696  }
697 
698  /* if completed already, assume the player can redo it */
699  if (q->state > 0) {
700  LOG(llevDebug, "quest_start: warning: player %s has already started quest %s\n", pl->ob->name, quest_code);
701  }
702 
704 
705  quest_set_state(NULL, pl, quest_code, state, 0);
706 
707  /* saving state will be done in quest_set_state(). */
708 }
709 
716 void quest_set_player_state(player *pl, sstring quest_code, int state) {
717  quest_set_state(NULL, pl, quest_code, state, 1);
718 }
719 
726 int quest_was_completed(player *pl, sstring quest_code) {
728  quest_state *state = get_state(qp, quest_code);
729 
730  return (state && state->was_completed);
731 }
732 
738 void command_quest(object *op, const char *params) {
739  /* who to display information about, used when called in DM mode */
740  object *who;
741  const char *name;
742 
743  if (!op->contr) {
744  LOG(llevError, "command_quest called for a non player!\n");
745  return;
746  }
747 
748  if (!params || *params == '\0') {
749  command_help(op, "quest");
750  return;
751  }
752 
753  if (QUERY_FLAG(op, FLAG_WIZ)) {
754  char* dup = strdup(params);
755  char* space = strchr(dup, ' ');
756  player* other;
757  draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ADMIN_DM, MSG_TYPE_COMMAND_QUESTS, "Command 'quest' called in DM mode.");
758  if (space == NULL) {
759  free(dup);
760  draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ADMIN_DM, MSG_TYPE_COMMAND_FAILURE, "Please specify a player name.");
761  return;
762  }
763  params = params + (space - dup) + 1;
764  *space = '\0';
765  other = find_player_partial_name(dup);
766  if (other == NULL) {
767  draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_ADMIN_DM, MSG_TYPE_COMMAND_FAILURE, "%s is not online, or ambiguous name.", dup);
768  free(dup);
769  return;
770  }
771  free(dup);
772  who = other->ob;
773  name = who->name;
774  } else {
775  who = op;
776  name = i18n(op, "You");
777  }
778 
779  if (strcmp(params, "list all") == 0) {
780  quest_list(op->contr, who->contr, 1, name);
781  return;
782  }
783 
784  if (strcmp(params, "list") == 0) {
785  quest_list(op->contr, who->contr, 0, name);
786  return;
787  }
788 
789  if (strncmp(params, "info ", 5) == 0) {
790  int number = atoi(params+5);
791  quest_info(op->contr, who->contr, get_quest_by_number(who->contr, number), 0);
792  return;
793  }
794 
795  /*
796  * Quest display for clients using the quest system, similar to 'info' above
797  * but using the (shared) quest's client_code instead of the (player unique) index.
798  */
799  if (strncmp(params, "info_c ", 7) == 0) {
800  int number = atoi(params+7);
801  quest_player *qp = get_quest(who->contr);
802  quest_state *qs = qp ? qp->quests : NULL;
803  while (qs) {
805  if (q && q->client_code == (uint32_t)number) {
806  break;
807  }
808  qs = qs->next;
809  }
810  if (qs) {
811  quest_info(op->contr, who->contr, qs, 0);
812  return;
813  }
814  draw_ext_info(NDI_UNIQUE, 0, who, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_FAILURE, "Invalid quest number");
815  return;
816  }
817 
818  if (QUERY_FLAG(op, FLAG_WIZ) && strncmp(params, "set ", 4) == 0) {
819  char *dup = strdup(params + 4);
820  char *space = strrchr(dup, ' ');
821  int state, number;
822  quest_state* q;
823  if (space == NULL) {
824  draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ADMIN_DM, MSG_TYPE_COMMAND_FAILURE, "Syntax is: quest (player name) (quest number) (state).");
825  free(dup);
826  return;
827  }
828  *space = '\0';
829  number = atoi(dup);
830  q = get_quest_by_number(who->contr, number);
831  if (q == NULL) {
832  draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_ADMIN_DM, MSG_TYPE_COMMAND_FAILURE, "Invalid quest number %d.", number);
833  free(dup);
834  return;
835  }
836  state = atoi(space + 1);
837  quest_set_state(op->contr, who->contr, q->code, state, 0);
838  free(dup);
840  return;
841  }
842 
843  command_help(op, "quest");
844 }
845 
847 struct dump {
849  int level;
850 };
856 static void output_quests(const quest_definition *quest, void *user) {
857  dump *d = (dump *)user;
858  if (d->parent != quest->parent)
859  return;
860 
861  char prefix[MAX_BUF];
862  prefix[0] = '\0';
863  for (int i = 0; i < d->level; i++) {
864  strncat(prefix, "-", MAX_BUF - 1);
865  }
866  prefix[MAX_BUF - 1] = '\0';
867 
868  fprintf(logfile, "%s%s - %s - %zu steps (%srestartable)\n", prefix, quest->quest_code, quest->quest_title, quest->steps.size(), quest->quest_restart ? "" : "not ");
869 
870  dump r;
871  r.parent = quest;
872  r.level = d->level + 1;
874 }
875 
880 void dump_quests(void) {
881  dump d;
882  d.parent = NULL;
883  d.level = 0;
885  exit(0);
886 }
887 
891 void free_quest(void) {
892  quest_player *pq = player_states, *next;
893 
894  while (pq) {
895  next = pq->next;
896  free_state(pq);
898  free(pq);
899  pq = next;
900  }
901  player_states = NULL;
902 }
903 
910  quest_player *states = NULL;
911  quest_state *state = NULL;
912  SockList sl;
913  size_t size;
914  quest_definition *quest;
916 
917  if (pl->socket->notifications < 1)
918  return;
919 
920  states = get_or_create_quest(pl);
921 
922  SockList_Init(&sl);
923  SockList_AddString(&sl, "addquest ");
924  for (state = states->quests; state != NULL; state = state->next) {
925 
926  quest = quest_get_by_code(state->code);
927  if (state->state == -1)
928  step = NULL;
929  else
930  step = quest_get_step(quest, state->state);
931 
932  size = 2 + (2 + strlen(quest->quest_title)) + 4 + 1 + (2 + (step != NULL ? strlen(step->step_description) : 0));
933 
934  if (SockList_Avail(&sl) < size) {
935  Send_With_Handling(pl->socket, &sl);
936  SockList_Reset(&sl);
937  SockList_AddString(&sl, "addquest ");
938  }
939 
940  SockList_AddInt(&sl, quest->client_code);
941  SockList_AddLen16Data(&sl, quest->quest_title, strlen(quest->quest_title));
942  if (quest->face && !(pl->socket->faces_sent[quest->face->number]&NS_FACESENT_FACE))
943  esrv_send_face(pl->socket, quest->face, 0);
944  SockList_AddInt(&sl, quest->face ? quest->face->number : 0);
945  SockList_AddChar(&sl, quest->quest_restart ? 1 : 0);
946  SockList_AddInt(&sl, quest->parent ? quest->parent->client_code : 0);
947  SockList_AddChar(&sl, (step == NULL || step->is_completion_step) ? 1 : 0);
948  if (step != NULL)
949  SockList_AddLen16Data(&sl, step->step_description, strlen(step->step_description));
950  else
951  SockList_AddShort(&sl, 0);
952 
953  state->sent_to_client = 1;
954  }
955 
956  Send_With_Handling(pl->socket, &sl);
957  SockList_Term(&sl);
958 }
959 
968  quest_player *qp = get_quest(pl);
969  if (qp != NULL && qp->quests != NULL) {
971  }
972 }
output_file.h
SockList_AddInt
void SockList_AddInt(SockList *sl, uint32_t data)
Adds a 32 bit value.
Definition: lowlevel.cpp:127
global.h
NS_FACESENT_FACE
#define NS_FACESENT_FACE
Bitmask for the faces_sent[] array - what portion of the face have we sent?
Definition: newserver.h:137
settings
struct Settings settings
Server settings.
Definition: init.cpp:139
output_quests
static void output_quests(const quest_definition *quest, void *user)
Dump one quest on the logfile, then its children.
Definition: quest.cpp:856
MSG_TYPE_COMMAND_SUCCESS
#define MSG_TYPE_COMMAND_SUCCESS
Successful result from command.
Definition: newclient.h:533
llevError
@ llevError
Error, serious thing.
Definition: logger.h:11
quest_for_each
void quest_for_each(quest_op op, void *user)
Iterate over all quests.
Definition: assets.cpp:538
LOG
void LOG(LogLevel logLevel, const char *format,...)
Logs a message to stderr, or to file.
Definition: logger.cpp:58
of_close
int of_close(OutputFile *of)
Closes an output file.
Definition: output_file.cpp:61
of_open
FILE * of_open(OutputFile *of, const char *fname)
Opens an output file.
Definition: output_file.cpp:30
player
One player.
Definition: player.h:105
get_or_create_quest
static quest_player * get_or_create_quest(player *pl)
Get quest status for a player, creating it if it doesn't exist yet.
Definition: quest.cpp:272
QUERY_FLAG
#define QUERY_FLAG(xyz, p)
Definition: define.h:226
quest_write_player_data
static void quest_write_player_data(const quest_player *pq)
Write quest-data information for a player.
Definition: quest.cpp:173
get_new_quest_state
static quest_state * get_new_quest_state(void)
Return a new quest_state*, calling fatal() if memory shortage.
Definition: quest.cpp:87
quest_first_player_save
void quest_first_player_save(player *pl)
Ensure the quest state is correctly saved for a player.
Definition: quest.cpp:967
quests_count
static int quests_count
Count of quests.
Definition: mapper.cpp:945
SockList_AddString
void SockList_AddString(SockList *sl, const char *data)
Adds a string without length.
Definition: lowlevel.cpp:157
quest_set_state
static void quest_set_state(player *dm, player *pl, sstring quest_code, int state, int started)
Set the state of a quest for a player.
Definition: quest.cpp:348
player::ob
object * ob
The object representing the player.
Definition: player.h:177
quest_definition::quest_description
sstring quest_description
Quest longer description.
Definition: quest.h:40
quest_info
static void quest_info(player *pl, player *who, quest_state *qs, int level)
Give details about a quest.
Definition: quest.cpp:574
draw_ext_info_format
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...) PRINTF_ARGS(6
NDI_ALL_DMS
#define NDI_ALL_DMS
Inform all logged in DMs.
Definition: newclient.h:267
MSG_TYPE_COMMAND_QUESTS
#define MSG_TYPE_COMMAND_QUESTS
Quest info.
Definition: newclient.h:530
get_quest
static quest_player * get_quest(player *pl)
Get quest status for a player, not creating it if it doesn't exist.
Definition: quest.cpp:254
quest_state::was_completed
int was_completed
Whether the quest was completed once or not, indepandently of the state.
Definition: quest.cpp:51
dump::parent
const quest_definition * parent
Parent to dump from.
Definition: quest.cpp:848
get_quest_by_number
static quest_state * get_quest_by_number(player *pl, int number)
returns the quest state which corresponds to a certain number for the given player.
Definition: quest.cpp:539
SockList_AddLen16Data
void SockList_AddLen16Data(SockList *sl, const void *data, size_t len)
Adds a data block prepended with an 16 bit length field.
Definition: lowlevel.cpp:191
SockList_Reset
void SockList_Reset(SockList *sl)
Resets the length of the stored data for writing.
Definition: lowlevel.cpp:74
free_state
static void free_state(quest_player *pq)
Free quests structures.
Definition: quest.cpp:639
quest_step_definition
One step of a quest.
Definition: quest.h:29
MSG_TYPE_COMMAND
#define MSG_TYPE_COMMAND
Responses to commands, eg, who.
Definition: newclient.h:407
name
Plugin animator file specs[Config] name
Definition: animfiles.txt:4
draw_ext_info
vs only yadda is in because all tags get reset on the next draw_ext_info In the second since it is all in one draw_ext_info
Definition: media-tags.txt:61
quest_state::sent_to_client
int sent_to_client
Whether this state was sent to the client or not.
Definition: quest.cpp:53
SockList_Avail
size_t SockList_Avail(const SockList *sl)
Returns the available bytes in a SockList instance.
Definition: lowlevel.cpp:246
quest_definition::quest_title
sstring quest_title
Quest title for player.
Definition: quest.h:39
MSG_TYPE_ADMIN_DM
#define MSG_TYPE_ADMIN_DM
DM related admin actions.
Definition: newclient.h:500
object::contr
struct player * contr
Pointer to the player which control this object.
Definition: object.h:284
add_refcount
sstring add_refcount(sstring str)
This will increase the refcount of the string str.
Definition: shstr.cpp:210
esrv_send_face
void esrv_send_face(socket_struct *ns, const Face *face, int nocache)
Sends a face to a client if they are in pixmap mode, nothing gets sent in bitmap mode.
Definition: image.cpp:72
quest_definition::face
const Face * face
Face associated with this quest.
Definition: quest.h:43
Face::number
uint16_t number
This is the image unique identifier.
Definition: face.h:15
do_update
static void do_update(const quest_definition *quest, void *user)
Definition: quest.cpp:316
add_string
sstring add_string(const char *str)
This will add 'str' to the hash table.
Definition: shstr.cpp:124
quest_definition::quest_code
sstring quest_code
Quest internal code.
Definition: quest.h:38
SockList_AddShort
void SockList_AddShort(SockList *sl, uint16_t data)
Adds a 16 bit value.
Definition: lowlevel.cpp:116
NDI_DELAYED
#define NDI_DELAYED
If set, then message is sent only after the player's tick completes.
Definition: newclient.h:272
SockList_AddChar
void SockList_AddChar(SockList *sl, unsigned char c)
Adds an 8 bit value.
Definition: lowlevel.cpp:106
dump_quests
void dump_quests(void)
Dump all of the quests, then calls exit() - useful in terms of debugging to make sure that quests are...
Definition: quest.cpp:880
dump
Structure used when dumping quests to stdout.
Definition: quest.cpp:847
quest_get_step
static quest_step_definition * quest_get_step(quest_definition *quest, int step)
Get a step for the specified quest.
Definition: quest.cpp:73
quest_definition::parent
struct quest_definition * parent
Parent for this quest, NULL if it is a 'top-level' quest.
Definition: quest.h:47
update_quests
static void update_quests(player *pl)
Look through all of the quests for the given player, and see if any need to be updated.
Definition: quest.cpp:336
quest_list
static void quest_list(player *pl, player *who, int showall, const char *name)
Display current and completed player quests.
Definition: quest.cpp:519
command_help
void command_help(object *op, const char *params)
Player is asking for some help.
Definition: c_misc.cpp:1772
step
How to Install a Crossfire Server on you must install a python script engine on your computer Python is the default script engine of Crossfire You can find the python engine you have only to install them The VisualC Crossfire settings are for but you habe then to change the pathes in the VC settings Go in Settings C and Settings Link and change the optional include and libs path to the new python installation path o step
Definition: INSTALL_WIN32.txt:20
sproto.h
quest_state::is_complete
int is_complete
Whether the quest is complete in the current playthrough.
Definition: quest.cpp:52
logfile
FILE * logfile
Used by server/daemon.c.
Definition: init.cpp:114
quest_player::quests
quest_state * quests
Quests done or in progress.
Definition: quest.cpp:60
quest_get_by_code
quest_definition * quest_get_by_code(sstring code)
Find a quest from its code if it exists.
Definition: assets.cpp:529
SockList_Init
void SockList_Init(SockList *sl)
Initializes the SockList instance.
Definition: lowlevel.cpp:55
quest_display
static void quest_display(player *pl, quest_player *pq, int showall, const char *name)
Utility function to display a quest list.
Definition: quest.cpp:446
get_or_create_state
static quest_state * get_or_create_state(quest_player *pq, sstring name)
Get the state of a quest for a player, creating it if not existing yet.
Definition: quest.cpp:228
quest_definition::client_code
uint32_t client_code
The code used to communicate with the client, merely a unique index.
Definition: quest.h:44
fatal
void fatal(enum fatal_error err)
fatal() is meant to be called whenever a fatal signal is intercepted.
Definition: utils.cpp:590
quest_set_player_state
void quest_set_player_state(player *pl, sstring quest_code, int state)
Set the state of a quest for a player.
Definition: quest.cpp:716
MAX_BUF
#define MAX_BUF
Used for all kinds of things.
Definition: define.h:35
MSG_TYPE_ADMIN_LOADSAVE
#define MSG_TYPE_ADMIN_LOADSAVE
load/save operations
Definition: newclient.h:502
d
How to Install a Crossfire Server on you must install a python script engine on your computer Python is the default script engine of Crossfire You can find the python engine you have only to install them The VisualC Crossfire settings are for d
Definition: INSTALL_WIN32.txt:13
free_string
void free_string(sstring str)
This will reduce the refcount, and if it has reached 0, str will be freed.
Definition: shstr.cpp:280
quest_send_initial_states
void quest_send_initial_states(player *pl)
Send the current quest states for the specified player, if the client supports those notifications.
Definition: quest.cpp:909
SockList_Term
void SockList_Term(SockList *sl)
Frees all resources allocated by a SockList instance.
Definition: lowlevel.cpp:65
Settings::playerdir
const char * playerdir
Where the player files are.
Definition: global.h:252
quest_get_player_state
int quest_get_player_state(player *pl, sstring quest_code)
Get the quest state for a player.
Definition: quest.cpp:660
TAG_END
#define TAG_END
Definition: quest.cpp:42
MSG_TYPE_COMMAND_FAILURE
#define MSG_TYPE_COMMAND_FAILURE
Failed result from command.
Definition: newclient.h:534
quest_find_by_code
quest_definition * quest_find_by_code(sstring code)
Find a quest from its code, logging if no matching quest.
Definition: assets.cpp:518
FLAG_WIZ
#define FLAG_WIZ
Object has special privilegies.
Definition: define.h:231
quest.h
NDI_UNIQUE
#define NDI_UNIQUE
Print immediately, don't buffer.
Definition: newclient.h:265
object::name
sstring name
The name of the object, obviously...
Definition: object.h:319
quest_start
void quest_start(player *pl, sstring quest_code, int state)
Start a quest for a player.
Definition: quest.cpp:680
quest_state
Information about a quest for a player.
Definition: quest.cpp:48
QC_CAN_RESTART
#define QC_CAN_RESTART
Quest status that indicates a quest was completed and may be restarted.
Definition: quest.cpp:45
quest_read_player_data
static void quest_read_player_data(quest_player *pq)
Read quest-data information for a player.
Definition: quest.cpp:98
sstring
const typedef char * sstring
Definition: sstring.h:2
quest_player::player_name
sstring player_name
Player's name.
Definition: quest.cpp:59
quest_definition
Definition of an in-game quest.
Definition: quest.h:37
quest_state::code
sstring code
Quest internal code.
Definition: quest.cpp:49
get_state
static quest_state * get_state(quest_player *pq, sstring name)
Get the state of a quest for a player, not creating if not existing yet.
Definition: quest.cpp:210
quest_player::next
quest_player * next
Next player on the list.
Definition: quest.cpp:61
quest_was_completed
int quest_was_completed(player *pl, sstring quest_code)
Check if a quest was completed once for a player, without taking account the current state.
Definition: quest.cpp:726
assets.h
quest_state::state
int state
State for the player.
Definition: quest.cpp:50
find_player_partial_name
player * find_player_partial_name(const char *plname)
Find a player by a partial name.
Definition: player.cpp:114
socket_struct::faces_sent
uint8_t * faces_sent
This is a bitmap on sent face status.
Definition: newserver.h:96
level
int level
Definition: readable.cpp:1563
data
====Textual A command containing textual data has data fields separated by one ASCII space character. word::A sequence of ASCII characters that does not contain the space or nul character. This is to distinguish it from the _string_, which may contain space characters. Not to be confused with a machine word. int::A _word_ containing the textual representation of an integer. Not to be confused with any of the binary integers in the following section. Otherwise known as the "string value of integer data". Must be parsed, e.g. using `atoi()` to get the actual integer value. string::A sequence of ASCII characters. This must only appear at the end of a command, since spaces are used to separate fields of a textual message.=====Binary All multi-byte integers are transmitted in network byte order(MSB first). int8::1-byte(8-bit) integer int16::2-byte(16-bit) integer int32::4-byte(32-bit) integer lstring::A length-prefixed string, which consists of an `int8` followed by that many bytes of the actual string. This is used to transmit a string(that may contain spaces) in the middle of binary data. l2string::Like _lstring_, but is prefixed with an `int16` to support longer strings Implementation Notes ~~~~~~~~~~~~~~~~~~~~ - Typical implementations read two bytes to determine the length of the subsequent read for the actual message, then read and parse the data from each message according to the commands described below. To send a message, the sender builds the message in a buffer, counts the length of the message, sends the length, and finally sends the actual message. TIP:Incorrectly transmitting or receiving the `length` field can lead to apparent "no response" issues as the client or server blocks to read the entire length of the message. - Since the protocol is highly interactive, it may be useful to set `TCP_NODELAY` on both the client and server. - If you are using a language with a buffered output stream, remember to flush the stream after a complete message. - If the connection is lost(which will also happen if the output buffer overflowing), the player is saved and the server cleans up. This does open up some abuses, but there is no perfect solution here. - The server only reads data from the socket if the player has an action. This isn 't really good, since many of the commands below might not be actual commands for the player. The alternative is to look at the data, and if it is a player command and there isn 't time, store it away to be processed later. But this increases complexity, in that the server must start buffering the commands. Fortunately, for now, there are few such client commands. Commands -------- In the documentation below, `S->C` represents a message to the client from the server, and `C->S` represents a message to the server from the client. Commands are documented in a brief format like:C->S:version< csval >[scval[vinfo]] Fields are enclosed like `< this >`. Optional fields are denoted like `[this]`. Spaces that appear in the command are literal, i.e. the<< _version > > command above uses spaces to separate its fields, but the command below does not:C->S:accountlogin< name >< password > As described in<< _messages > >, if a command contains data, then the command is separated from the data by a literal space. Many of the commands below refer to 'object tags'. Whenever the server creates an object, it creates a unique tag for that object(starting at 1 when the server is first run, and ever increasing.) Tags are unique, but are not consistent between runs. Thus, the client can not store tags when it exits and hope to re-use them when it joins the server at a later time - tags are only valid for the current connection. The protocol commands are broken into various sections which based somewhat on what the commands are for(ie, item related commands, map commands, image commands, etc.) In this way, all the commands related to similar functionality is in the same place. Initialization ~~~~~~~~~~~~~~ version ^^^^^^^ C->S:version< csval >[scval[vinfo]] S->C:version< csval >[scval[vinfo]] Used by the client and server to exchange which version of the Crossfire protocol they understand. Neither send this in response to the other - they should both send this shortly after a connection is established. csval::int, version level of C->S communications scval::int, version level of S->C communications vinfo::string, that is purely for informative that general client/server info(ie, javaclient, x11client, winclient, sinix server, etc). It is purely of interest of server admins who can see what type of clients people are using.=====Version ID If a new command is added to the protocol in the C->S direction, then the version number in csval will get increased. Likewise, the same is true for the scval. The version are currently integers, in the form ABCD. A=1, and will likely for quite a while. This will only really change if needed from rollover of B. B represents major protocol changes - if B mismatches, the clients will be totally unusable. Such an example would be change of map or item sending commands(either new commands or new format.) C represents more minor but still significant changes - clients might still work together, but some features that used to work may now fail due to the mismatch. An example may be a change in the meaning of some field in some command - providing the field is the same size, it still should be decoded properly, but the meaning won 't be processed properly. D represents very minor changes or new commands. Things should work no worse if D does not match, however if they do match, some new features might be included. An example of the would be the C->S mark command to mark items. Server not understanding this just means that the server can not process it, and will ignore it.=====Handling As far as the client is concerned, its _scval_ must be at least equal to the server, and its _csval_ should not be newer than the server. The server does not care about the version command it receives right now - all it currently does is log mismatches. In theory, the server should keep track of what the client has, and adjust the commands it sends respectively in the S->C direction. The server is resilant enough that it won 't crash with a version mismatch(however, client may end up sending commands that the server just ignores). It is really up to the client to enforce versioning and quit if the versions don 't match. NOTE:Since all packets have the length as the first 2 bytes, all that either the client or server needs to be able to do is look at the first string and see if it understands it. If not, it knows how many bytes it can skip. As such, exact version matches should not be necessary for proper operation - however, both the client and server needs to be coded to handle such cases.=====History _scval_ and _vinfo_ were added in version 1020. Before then, there was only one version sent in the version command. NOTE:For the most part, this has been obsoleted by the setup command which always return status and whether it understood the command or not. However there are still some cases where using this versioning is useful - an example it the addition of the requestinfo/replyinfo commands - the client wants to wait for acknowledge of all the replyinfo commands it has issued before sending the addme command. However, if the server doesn 't understand these options, the client will never get a response. With the versioning, the client can look at the version and know if it should wait for a response or if the server will never send back. setup ^^^^^ C->S, S->C:setup< option1 >< value1 >< option2 >< value2 > ... Sent by the client to request protocol option changes. This can be at any point during the life of a connection, but usually sent at least once right after the<< _version > > command. The server responds with a message in the same format confirming what configuration options were set. The server only sends a setup command in response to one from the client. The sc_version should be updated in the server if commands have been obsoleted such that old clients may not be able to play. option::word, name of configuration option value::word, value of configuration option. May need further parsing according to the setup options below=====Setup Options There are really 2 set of setup commands here:. Those that control preferences of the client(how big is the map, what faceset to use, etc). . Those that describe capabilities of the client(client supports this protocol command or that) .Setup Options[options="autowidth,header"]|===========================|Command|Description|beat|Ask the server to enable heartbeat support. When heartbeat is enabled, the client must send the server a command every three seconds. If no commands need to be sent, use the `beat` no-op command. Clients that do not contact the server within the interval are assumed to have a temporary connection failure.|bot(0/1 value)|If set to 1, the client will not be considered a player when updating information to the metaserver. This is to avoid having a server with many bots appear more crowded than others.|darkness(0/1 value)|If set to 1(default), the server will send darkness information in the map protocol commands. If 0, the server will not include darkness, thus saving a minor amount of bandwidth. Since the client is free to ignore the darkness information, this does not allow the client to cheat. In the case of the old 'map' protocol command, turning darkness off will result in the masking faces not getting sent to the client.|extended_stats(0/1 value)|If set to 1, the server will send the CS_STAT_RACE_xxx and CS_STAT_BASE_xxx values too, so the client can display various status related to statistics. Default is 0.|facecache(0/1)|Determines if the client is caching images(1) or wants the images sent to it without caching them(0). Default is 0. This replaces the setfacemode command.|faceset(8 bit)|Faceset the client wishes to use. If the faceset is not valid, the server returns the faceset the client will be using(default 0).|loginmethod(8 bit)|Client sends this to server to note login support. This is basically used as a subset of the csversion/scversion to find out what level of login support the server and client support. Current defined values:0:no advanced support - only legacy login method 1:account based login(described more below) 2:new character creation support This list may grow - for example, advanced character creation could become a feature.|map2cmd:(1)|This indicates client support for the map2 protocol command. See the map2 protocol details above for the main differences. Obsolete:This is the only supported mode now, but many clients use it as a sanity check for protocol versions, so the server still replies. It doesn 't do anything with the data|mapsize(int x) X(int y)|Sets the map size to x X y. Note the spaces here are only for clarity - there should be no spaces when actually sent(it should be 11x11 or 25x25). The default map size unless changed is 11x11. The minimum map size the server will allow is 9x9(no technical reason this could be smaller, but I don 't think the game would be smaller). The maximum map size supported in the current protocol is 63x63. However, each server can have its maximum map size sent to most any value. If the client sends an invalid mapsize command or a mapsize of 0x0, the server will respond with a mapsize that is the maximum size the server supports. Thus, if the client wants to know the maximum map size, it can just do a 'mapsize 0x0' or 'mapsize' and it will get the maximum size back. The server will constrain the provided mapsize x &y to the configured minumum and maximums. For example, if the maximum map size is 25x25, the minimum map size is 9x9, and the client sends a 31x7 mapsize request, the mapsize will be set to 25x9 and the server will send back a mapsize 25x9 setup command. When the values are valid, the server will send back a mapsize XxY setup command. Note that this is from its parsed values, so it may not match stringwise with what the client sent, but will match 0 wise. For example, the client may send a 'mapsize 025X025' command, in which case the server will respond with a 'mapsize 25x25' command - the data is functionally the same. The server will send an updated map view when this command is sent.|notifications(int value)|Value indicating what notifications the client accepts. It is incremental, a value means "all notifications till this level". The following levels are supported:1:quest-related notifications("addquest" and "updquest") 2:knowledge-related notifications("addknowledge") 3:character status flags(overloaded, blind,...)|num_look_objects(int value)|The maximum number of objects shown in the ground view. If more objects are present, fake objects are created for selecting the previous/next group of items. Defaults to 50 if not set. The server may adjust the given value to a suitable one data
Definition: protocol.txt:379
quest_player
Information about a player.
Definition: quest.cpp:58
command_quest
void command_quest(object *op, const char *params)
Command handler for 'quest'.
Definition: quest.cpp:738
player_get_delayed_buffer
SockList * player_get_delayed_buffer(player *pl)
Get a delayed socket buffer, that will be sent after the player's tick is complete.
Definition: player.cpp:4501
player::has_directory
uint32_t has_directory
If 0, the player was not yet saved, its directory doesn't exist.
Definition: player.h:149
socket_struct::notifications
uint16_t notifications
Notifications this client wants to get.
Definition: newserver.h:129
player::socket
socket_struct * socket
Socket information for this player.
Definition: player.h:107
quest_definition::quest_restart
int quest_restart
If non zero, can be restarted.
Definition: quest.h:42
dump::level
int level
Indentation level.
Definition: quest.cpp:849
TAG_START
#define TAG_START
Definition: quest.cpp:41
OUT_OF_MEMORY
@ OUT_OF_MEMORY
Definition: define.h:48
Send_With_Handling
void Send_With_Handling(socket_struct *ns, SockList *sl)
Calls Write_To_Socket to send data to the client.
Definition: lowlevel.cpp:447
MSG_TYPE_ADMIN
#define MSG_TYPE_ADMIN
Definition: newclient.h:405
SockList
Contains the base information we use to make up a packet we want to send.
Definition: newclient.h:684
free_quest
void free_quest(void)
Free all quest status structures.
Definition: quest.cpp:891
quest_state::next
quest_state * next
Next quest on the list.
Definition: quest.cpp:54
player_states
static quest_player * player_states
Player quest state.
Definition: quest.cpp:65
llevDebug
@ llevDebug
Only for debugging purposes.
Definition: logger.h:13
evaluate_quest_conditions
static int evaluate_quest_conditions(const std::vector< quest_condition * > conditions, player *pl)
Checks whether the conditions for a given step are met.
Definition: quest.cpp:297
i18n
const char * i18n(const object *who, const char *code)
Translate a message in the appropriate language.
Definition: languages.cpp:42
OutputFile
Definition: output_file.h:41
quest_definition::steps
std::vector< quest_step_definition * > steps
Quest steps.
Definition: quest.h:46
Settings::localdir
const char * localdir
Read/write data files.
Definition: global.h:251