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  if (step == 0) {
75  // No error message, because 0 means not started.
76  return NULL;
77  }
78 
79  for (const auto qs : quest->steps) {
80  if (qs->step == step)
81  return qs;
82  }
83 
84  LOG(llevError, "quest %s has no required step %d\n", quest->quest_code, step);
85  return NULL;
86 }
87 
93  quest_state *qs = static_cast<quest_state *>(calloc(1, sizeof(quest_state)));
94  if (qs == NULL)
96  return qs;
97 }
98 
104  FILE *file;
105  char final[MAX_BUF], read[MAX_BUF], data[MAX_BUF];
106  quest_state *qs = NULL, *prev = NULL;
107  int warned = 0, state;
108  quest_definition *quest = NULL;
109 
110  snprintf(final, sizeof(final), "%s/%s/%s/%s.quest", settings.localdir, settings.playerdir, pq->player_name, pq->player_name);
111 
112  file = fopen(final, "r");
113  if (!file) {
114  /* no quest yet, no big deal */
115  return;
116  }
117 
118  while (fgets(read, sizeof(read), file) != NULL) {
119  if (sscanf(read, "quest %s\n", data)) {
120  qs = get_new_quest_state();
121  qs->code = add_string(data);
122  quest = quest_get_by_code(qs->code);
123  continue;
124  }
125 
126  if (!qs) {
127  if (!warned)
128  LOG(llevError, "quest: invalid file format for %s\n", final);
129  warned = 1;
130  continue;
131  }
132 
133  if (sscanf(read, "state %d\n", &state)) {
134  qs->state = state;
135  if (quest != NULL && state != -1 && state != 0) {
136  quest_step_definition *step = quest_get_step(quest, state);
137  if (step == NULL) {
138  LOG(llevError, "invalid quest step %d for %s in %s\n", state, quest->quest_code, final);
139  }
140  else if (step->is_completion_step)
141  qs->is_complete = 1;
142  }
143  continue;
144  }
145  if (strcmp(read, "end_quest\n") == 0) {
146  if (quest == NULL) {
147  LOG(llevDebug, "Unknown quest %s in quest file %s\n", qs->code, final);
148  free(qs);
149  } else {
150  if (prev == NULL) {
151  pq->quests = qs;
152  } else {
153  prev->next = qs;
154  }
155  prev = qs;
156  }
157  qs = NULL;
158  continue;
159  }
160  if (sscanf(read, "completed %d\n", &state)) {
161  qs->was_completed = state ? 1 : 0;
162  continue;
163  }
164 
165  LOG(llevError, "quest: invalid line in %s: %s\n", final, read);
166  }
167 
168  if (qs)
169  LOG(llevError, "quest: missing end_quest in %s\n", final);
170 
171  fclose(file);
172 }
173 
178 static void quest_write_player_data(const quest_player *pq) {
179  FILE *file;
180  OutputFile of;
181  char fname[MAX_BUF];
182  const quest_state *state;
183 
184  snprintf(fname, sizeof(fname), "%s/%s/%s/%s.quest", settings.localdir, settings.playerdir, pq->player_name, pq->player_name);
185 
186  file = of_open(&of, fname);
187  if (file == NULL) {
188  draw_ext_info(NDI_UNIQUE | NDI_ALL_DMS, 0, NULL, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_LOADSAVE, "File write error on server!");
189  return;
190  }
191 
192  state = pq->quests;
193 
194  while (state) {
195  fprintf(file, "quest %s\n", state->code);
196  fprintf(file, "state %d\n", state->state);
197  fprintf(file, "completed %d\n", state->was_completed);
198  fprintf(file, "end_quest\n");
199  state = state->next;
200  }
201 
202  if (!of_close(&of)) {
203  draw_ext_info(NDI_UNIQUE | NDI_ALL_DMS, 0, NULL, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_LOADSAVE, "File write error on server!");
204  return;
205  }
207 }
208 
216  quest_state *qs = pq->quests;
217 
218  while (qs) {
219  if (qs->code == name)
220  return qs;
221  qs = qs->next;
222  }
223 
224  return NULL;
225 }
226 
234  quest_state *qs = get_state(pq, name);
235 
236  if (!qs) {
237  qs = static_cast<quest_state *>(calloc(1, sizeof(quest_state)));
238  if (!qs)
240  qs->code = add_refcount(name);
241  if (pq->quests != NULL) {
242  quest_state *last;
243  for (last = pq->quests ; last->next != NULL; last = last->next)
244  ;
245  last->next = qs;
246  } else {
247  pq->quests = qs;
248  }
249  }
250 
251  return qs;
252 }
253 
261 
262  while (pq) {
263  if (pq->player_name == pl->ob->name)
264  return pq;
265  pq = pq->next;
266  }
267 
268  return NULL;
269 }
270 
278  quest_player *pq = get_quest(pl);
279 
280  if (!pq) {
281  pq = static_cast<quest_player *>(calloc(1, sizeof(quest_player)));
282  if (!pq)
284  pq->player_name = add_refcount(pl->ob->name);
285  pq->next = player_states;
286  player_states = pq;
288  }
289 
290  return pq;
291 }
292 
293 /* quest_set_state can call itself through the function update_quests, so it needs to be declared here */
294 static void quest_set_state(player* dm, player *pl, sstring quest_code, int state, int started);
295 
302 static int evaluate_quest_conditions(const std::vector<quest_condition *> conditions, player *pl) {
303  int current_step;
304 
305  if (conditions.empty())
306  return 0;
307  for (const auto cond : conditions) {
308  current_step = quest_get_player_state(pl, cond->quest_code);
309  if (cond->minstep < 0 && cond->maxstep < 0) {
310  /* we are checking for the quest to have been completed. */
311  if (!quest_was_completed(pl, cond->quest_code))
312  return 0;
313  } else {
314  if (current_step < cond->minstep || current_step > cond->maxstep)
315  return 0;
316  }
317  }
318  return 1;
319 }
320 
321 static void do_update(const quest_definition *quest, void *user) {
322  player *pl = (player *)user;
323  int new_step = 0;
324  for (const quest_step_definition *step : quest->steps) {
325  if (evaluate_quest_conditions(step->conditions, pl)) {
326  new_step=new_step<step->step?step->step:new_step;
327  }
328  }
329  if (new_step > 0) {
330  int current_step = quest_get_player_state(pl, quest->quest_code);
331  if (new_step > current_step) {
332  quest_set_state(NULL, pl, quest->quest_code, new_step, 0);
333  }
334  }
335 }
336 
341 static void update_quests(player *pl) {
343 }
344 
353 static void quest_set_state(player* dm, player *pl, sstring quest_code, int state, int started) {
355  quest_state *qs = get_or_create_state(pq, quest_code);
356  quest_definition *quest = quest_find_by_code(quest_code);
358 
359  if (!quest) {
360  if (dm) {
361  draw_ext_info_format(NDI_UNIQUE, 0, dm->ob, MSG_TYPE_ADMIN_DM, MSG_TYPE_COMMAND_FAILURE, "Unknown quest %s!", quest_code);
362  } else {
363  LOG(llevError, "quest: asking for set_state of unknown quest %s!\n", quest_code);
364  }
365  return;
366  }
367 
368  if (!dm && state < 0) {
369  LOG(llevDebug, "quest_set_player_state: warning: called with invalid state %d for quest %s, player %s\n", state, pl->ob->name, quest_code);
370  state = 0;
371  }
372 
373  if (started && qs->state == 0) {
374  if (!dm) {
375  LOG(llevDebug, "quest_set_player_state: warning: called for player %s not having started quest %s\n", pl->ob->name, quest_code);
376  }
377  }
378 
379  qs->state = state;
380  if (state == 0) {
381  qs->is_complete = 0;
382  }
383 
384  step = quest_get_step(quest, state);
385  if (!step && state != 0) {
386  if (dm) {
387  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);
388  } else {
389  LOG(llevError, "quest_set_player_state: couldn't find state definition %d for quest %s, player %s\n", state, quest_code, pl->ob->name);
390  }
391  }
392 
393  if (step && step->is_completion_step) {
394  /* don't send an update note if the quest was already completed, this is just to show the outcome afterwards. */
395  if (!qs->is_complete)
397  qs->was_completed = 1;
398  if (quest->quest_restart)
399  qs->state = QC_CAN_RESTART;
400  else
401  qs->is_complete = 1;
402 
403  } else if (step != 0) {
404  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);
406  }
407 
408  if (pl->socket->notifications > 0) {
410 
411  if (qs->sent_to_client) {
412  SockList_AddString(sl, "updquest ");
413  } else {
414  SockList_AddString(sl, "addquest ");
415  }
416 
417  SockList_AddInt(sl, quest->client_code);
418  if (qs->sent_to_client == 0) {
419  SockList_AddLen16Data(sl, quest->quest_title, strlen(quest->quest_title));
420  if (quest->face && !(pl->socket->faces_sent[quest->face->number]&NS_FACESENT_FACE))
421  esrv_send_face(pl->socket, quest->face, 0);
422  SockList_AddInt(sl, quest->face ? quest->face->number : 0);
423  SockList_AddChar(sl, quest->quest_restart ? 1 : 0);
424  SockList_AddInt(sl, quest->parent ? quest->parent->client_code : 0);
425  }
426 
427  SockList_AddChar(sl, (step == NULL || step->is_completion_step) ? 1 : 0);
428  if (step) {
429  SockList_AddLen16Data(sl, step->step_description, strlen(step->step_description));
430  } else {
431  const char *not_started = "Not started.";
432  SockList_AddLen16Data(sl, not_started, strlen(not_started));
433  }
434 
435  qs->sent_to_client = 1;
436  }
437 
438  if (pl->has_directory)
440  update_quests(pl);
441  LOG(llevDebug, "quest_set_player_state %s %s %d\n", pl->ob->name, quest_code, state);
442 
443 }
444 
452 static void quest_display(player *pl, quest_player *pq, int showall, const char* name) {
453  quest_state *state;
454  quest_definition *quest;
455  const char *restart;
456  int completed_count = 0, restart_count = 0, total_count = 0, current_count = 0;
457 
458  state = pq->quests;
459  while (state) {
460  quest = quest_find_by_code(state->code);
461  if (quest->parent == NULL) {
462  total_count++;
463  /* count up the number of completed quests first */
464  if (state->state == QC_CAN_RESTART) {
465  restart_count++;
466  completed_count++;
467  } else if (state->is_complete) {
468  completed_count++;
469  }
470  }
471  state = state->next;
472  }
473  if (completed_count > 0) {
474  if (!showall) {
475  if (restart_count > 0)
477  "%s completed %d out of %zu quests, of which %d may be restarted.", name, completed_count, quests_count(false), restart_count);
478  else
480  "%s completed %d quests", name, completed_count);
481  current_count = completed_count;
482  } else {
484  "%s completed the following quests:", name);
485  state = pq->quests;
486  while (state) {
487  quest = quest_find_by_code(state->code);
488  if (quest->parent == NULL) {
489  if (state->state == QC_CAN_RESTART || state->is_complete) {
490 
491  restart = state->state == QC_CAN_RESTART ? i18n(pl->ob, " (can be replayed)") : "";
493  "(%3d) %s%s", ++current_count, quest->quest_title, restart);
494  }
495  }
496  state = state->next;
497  }
498  }
499  }
500  if (total_count > completed_count) {
502  "%s started the following quests:", name);
503  state = pq->quests;
504  while (state) {
505  quest = quest_find_by_code(state->code);
506  if (quest->parent == NULL) {
507  if (state->state != QC_CAN_RESTART && state->is_complete == 0) {
508  quest = quest_find_by_code(state->code);
510  "(%3d) %s", ++current_count, quest->quest_title);
511  }
512  }
513  state = state->next;
514  }
515  }
516 }
517 
525 static void quest_list(player *pl, player *who, int showall, const char* name) {
526  quest_player *pq;
527 
528  /* ensure we load data if not loaded yet */
529  pq = get_or_create_quest(who);
530  if (!pq->quests) {
531  draw_ext_info_format(NDI_UNIQUE, 0, pl->ob, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_QUESTS, "%s didn't start any quest.", name);
532  return;
533  }
534 
535  quest_display(pl, pq, showall, name);
536 }
537 
545 static quest_state *get_quest_by_number(player *pl, int number) {
546  quest_state *state;
548  int questnum = 0;
549 
550  if (number <= 0 || !pq) {
551  return NULL;
552  }
553  /* count through completed quests first */
554  state = pq->quests;
555  while (state) {
556  /* count up the number of completed quests first */
557  if (!(quest_find_by_code(state->code)->parent) && (state->state == QC_CAN_RESTART || state->is_complete))
558  if (++questnum == number) return state;
559  state = state->next;
560  }
561  /* then active ones */
562  state = pq->quests;
563  while (state) {
564  /* count up the number of completed quests first */
565  if (!(quest_find_by_code(state->code)->parent) && state->state != QC_CAN_RESTART && state->is_complete ==0)
566  if (++questnum == number) return state;
567  state = state->next;
568  }
569  /* Ok, we didn't find our quest, return NULL*/
570  return NULL;
571 }
572 
580 static void quest_info(player *pl, player* who, quest_state *qs, int level) {
581  quest_definition *quest, *child;
582  quest_state *state;
584  const char *prefix;
585 
586  if (!qs) {
587  draw_ext_info(NDI_UNIQUE, 0, pl->ob, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_QUESTS, "Invalid quest number");
588  return;
589  }
590  quest = quest_find_by_code(qs->code);
591  if (!quest) {
592  /* already warned by quest_get */
593  draw_ext_info(NDI_UNIQUE, 0, pl->ob, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_QUESTS, "Quest: (internal error)");
594  return;
595  }
596 
598  if (QUERY_FLAG(pl->ob, FLAG_WIZ)) {
600  std::for_each(quest->steps.cbegin(), quest->steps.cend(), [&pl] (const auto &step) {
601  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);
602  });
603  }
605 
607  if (qs->state == QC_CAN_RESTART || qs->is_complete) {
608  const char *restart = "";
609  if (quest->quest_restart)
610  restart = i18n(pl->ob, " (can be replayed)");
611  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);
612  }
613  prefix = "";
614  if (qs->state != QC_CAN_RESTART) {
615  /* ie, if we are in progress or completed for a non-restartable quest */
616  if (!step) {
617  /* already warned by quest_get_step */
619  return;
620  }
621  if (level > 0) {
622  prefix = " * ";
623  } else if (qs->is_complete)
624  prefix = "Outcome";
625  else
626  prefix = "Current Status";
627  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);
628  }
629 
630  /* ok, now check all of the player's other quests for any children, and print those in order */
631  state = pq->quests;
632  while (state) {
633  child = quest_find_by_code(state->code);
634  if (child->parent == quest)
635  quest_info(pl, who, state, level+1);
636  state = state->next;
637  }
638  return;
639 }
640 
645 static void free_state(quest_player *pq) {
646  quest_state *qs = pq->quests, *next;
647 
648  while (qs) {
649  next = qs->next;
650  free_string(qs->code);
651  free(qs);
652  qs = next;
653  }
654  pq->quests = NULL;
655 }
656 
657 
658 /* public functions */
659 
666 int quest_get_player_state(player *pl, sstring quest_code) {
668  quest_state *s = get_state(q, quest_code);
669  quest_definition *quest = quest_find_by_code(quest_code);
670 
671  if (!s)
672  return 0;
673 
674  if (s->state == QC_CAN_RESTART && quest && quest->quest_restart)
675  return 0;
676 
677  return s->state;
678 }
679 
686 void quest_start(player *pl, sstring quest_code, int state) {
687  quest_player *pq;
688  quest_state *q;
689  quest_definition *quest;
690 
691  quest = quest_find_by_code(quest_code);
692  if (!quest) {
693  LOG(llevError, "quest_start: requested unknown quest %s\n", quest_code);
694  return;
695  }
696  pq = get_or_create_quest(pl);
697  q = get_or_create_state(pq, quest_code);
698 
699  if (state <= 0) {
700  state = 100;
701  LOG(llevDebug, "quest_start: negative state %d for %s quest %s\n", state, pl->ob->name, quest_code);
702  }
703 
704  /* if completed already, assume the player can redo it */
705  if (q->state > 0) {
706  LOG(llevDebug, "quest_start: warning: player %s has already started quest %s\n", pl->ob->name, quest_code);
707  }
708 
710 
711  quest_set_state(NULL, pl, quest_code, state, 0);
712 
713  /* saving state will be done in quest_set_state(). */
714 }
715 
722 void quest_set_player_state(player *pl, sstring quest_code, int state) {
723  quest_set_state(NULL, pl, quest_code, state, 1);
724 }
725 
732 int quest_was_completed(player *pl, sstring quest_code) {
734  quest_state *state = get_state(qp, quest_code);
735 
736  return (state && state->was_completed);
737 }
738 
744 void command_quest(object *op, const char *params) {
745  /* who to display information about, used when called in DM mode */
746  object *who;
747  const char *name;
748 
749  if (!op->contr) {
750  LOG(llevError, "command_quest called for a non player!\n");
751  return;
752  }
753 
754  if (!params || *params == '\0') {
755  command_help(op, "quest");
756  return;
757  }
758 
759  if (QUERY_FLAG(op, FLAG_WIZ)) {
760  char* dup = strdup(params);
761  char* space = strchr(dup, ' ');
762  player* other;
763  draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ADMIN_DM, MSG_TYPE_COMMAND_QUESTS, "Command 'quest' called in DM mode.");
764  if (space == NULL) {
765  free(dup);
766  draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ADMIN_DM, MSG_TYPE_COMMAND_FAILURE, "Please specify a player name.");
767  return;
768  }
769  params = params + (space - dup) + 1;
770  *space = '\0';
771  other = find_player_partial_name(dup);
772  if (other == NULL) {
773  draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_ADMIN_DM, MSG_TYPE_COMMAND_FAILURE, "%s is not online, or ambiguous name.", dup);
774  free(dup);
775  return;
776  }
777  free(dup);
778  who = other->ob;
779  name = who->name;
780  } else {
781  who = op;
782  name = i18n(op, "You");
783  }
784 
785  if (strcmp(params, "list all") == 0) {
786  quest_list(op->contr, who->contr, 1, name);
787  return;
788  }
789 
790  if (strcmp(params, "list") == 0) {
791  quest_list(op->contr, who->contr, 0, name);
792  return;
793  }
794 
795  if (strncmp(params, "info ", 5) == 0) {
796  int number = atoi(params+5);
797  quest_info(op->contr, who->contr, get_quest_by_number(who->contr, number), 0);
798  return;
799  }
800 
801  /*
802  * Quest display for clients using the quest system, similar to 'info' above
803  * but using the (shared) quest's client_code instead of the (player unique) index.
804  */
805  if (strncmp(params, "info_c ", 7) == 0) {
806  int number = atoi(params+7);
807  quest_player *qp = get_quest(who->contr);
808  quest_state *qs = qp ? qp->quests : NULL;
809  while (qs) {
811  if (q && q->client_code == (uint32_t)number) {
812  break;
813  }
814  qs = qs->next;
815  }
816  if (qs) {
817  quest_info(op->contr, who->contr, qs, 0);
818  return;
819  }
820  draw_ext_info(NDI_UNIQUE, 0, who, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_FAILURE, "Invalid quest number");
821  return;
822  }
823 
824  if (strncmp(params, "reset ", 6) == 0) {
825  int number = atoi(params+6);
826  quest_state *q = get_quest_by_number(who->contr, number);
827  if (q == NULL) {
829  "You cannot reset a quest that you haven't started.");
830  return;
831  }
832  quest_set_state(NULL, who->contr, q->code, 0, 0);
834  "Quest progress reset.");
835  return;
836  }
837 
838  if (QUERY_FLAG(op, FLAG_WIZ) && strncmp(params, "set ", 4) == 0) {
839  char *dup = strdup(params + 4);
840  char *space = strrchr(dup, ' ');
841  int state, number;
842  quest_state* q;
843  if (space == NULL) {
844  draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ADMIN_DM, MSG_TYPE_COMMAND_FAILURE, "Syntax is: quest (player name) (quest number) (state).");
845  free(dup);
846  return;
847  }
848  *space = '\0';
849  number = atoi(dup);
850  q = get_quest_by_number(who->contr, number);
851  if (q == NULL) {
852  draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_ADMIN_DM, MSG_TYPE_COMMAND_FAILURE, "Invalid quest number %d.", number);
853  free(dup);
854  return;
855  }
856  state = atoi(space + 1);
857  quest_set_state(op->contr, who->contr, q->code, state, 0);
858  free(dup);
860  return;
861  }
862 
863  command_help(op, "quest");
864 }
865 
867 struct dump {
869  int level;
870 };
876 static void output_quests(const quest_definition *quest, void *user) {
877  dump *d = (dump *)user;
878  if (d->parent != quest->parent)
879  return;
880 
881  char prefix[MAX_BUF];
882  prefix[0] = '\0';
883  for (int i = 0; i < d->level; i++) {
884  strncat(prefix, "-", MAX_BUF - 1);
885  }
886  prefix[MAX_BUF - 1] = '\0';
887 
888  fprintf(logfile, "%s%s - %s - %zu steps (%srestartable)\n", prefix, quest->quest_code, quest->quest_title, quest->steps.size(), quest->quest_restart ? "" : "not ");
889 
890  dump r;
891  r.parent = quest;
892  r.level = d->level + 1;
894 }
895 
900 void dump_quests(void) {
901  dump d;
902  d.parent = NULL;
903  d.level = 0;
905  exit(0);
906 }
907 
911 void free_quest(void) {
912  quest_player *pq = player_states, *next;
913 
914  while (pq) {
915  next = pq->next;
916  free_state(pq);
918  free(pq);
919  pq = next;
920  }
921  player_states = NULL;
922 }
923 
930  quest_player *states = NULL;
931  quest_state *state = NULL;
932  SockList sl;
933  size_t size;
934  quest_definition *quest;
936 
937  if (pl->socket->notifications < 1)
938  return;
939 
940  states = get_or_create_quest(pl);
941 
942  SockList_Init(&sl);
943  SockList_AddString(&sl, "addquest ");
944  for (state = states->quests; state != NULL; state = state->next) {
945 
946  quest = quest_get_by_code(state->code);
947  if (state->state == -1 || state->state == 0)
948  step = NULL;
949  else
950  step = quest_get_step(quest, state->state);
951 
952  size = 2 + (2 + strlen(quest->quest_title)) + 4 + 1 + (2 + (step != NULL ? strlen(step->step_description) : 0));
953 
954  if (SockList_Avail(&sl) < size) {
955  Send_With_Handling(pl->socket, &sl);
956  SockList_Reset(&sl);
957  SockList_AddString(&sl, "addquest ");
958  }
959 
960  SockList_AddInt(&sl, quest->client_code);
961  SockList_AddLen16Data(&sl, quest->quest_title, strlen(quest->quest_title));
962  if (quest->face && !(pl->socket->faces_sent[quest->face->number]&NS_FACESENT_FACE))
963  esrv_send_face(pl->socket, quest->face, 0);
964  SockList_AddInt(&sl, quest->face ? quest->face->number : 0);
965  SockList_AddChar(&sl, quest->quest_restart ? 1 : 0);
966  SockList_AddInt(&sl, quest->parent ? quest->parent->client_code : 0);
967  SockList_AddChar(&sl, (step == NULL || step->is_completion_step) ? 1 : 0);
968  if (step != NULL)
969  SockList_AddLen16Data(&sl, step->step_description, strlen(step->step_description));
970  else
971  SockList_AddShort(&sl, 0);
972 
973  state->sent_to_client = 1;
974  }
975 
976  Send_With_Handling(pl->socket, &sl);
977  SockList_Term(&sl);
978 }
979 
988  quest_player *qp = get_quest(pl);
989  if (qp != NULL && qp->quests != NULL) {
991  }
992 }
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:141
settings
struct Settings settings
Global 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:876
MSG_TYPE_COMMAND_SUCCESS
#define MSG_TYPE_COMMAND_SUCCESS
Successful result from command.
Definition: newclient.h:534
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:543
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:107
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:277
QUERY_FLAG
#define QUERY_FLAG(xyz, p)
Definition: define.h:371
quest_write_player_data
static void quest_write_player_data(const quest_player *pq)
Write quest-data information for a player.
Definition: quest.cpp:178
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:92
quest_first_player_save
void quest_first_player_save(player *pl)
Ensure the quest state is correctly saved for a player.
Definition: quest.cpp:987
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:353
player::ob
object * ob
The object representing the player.
Definition: player.h:179
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:580
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
FLAG_WIZ
#define FLAG_WIZ
Object has special privilegies.
Definition: define.h:218
NDI_ALL_DMS
#define NDI_ALL_DMS
Inform all logged in DMs.
Definition: newclient.h:268
MSG_TYPE_COMMAND_QUESTS
#define MSG_TYPE_COMMAND_QUESTS
Quest info.
Definition: newclient.h:531
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:259
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:868
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:545
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:645
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:408
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:501
object::contr
struct player * contr
Pointer to the player which control this object.
Definition: object.h:284
add_refcount
sstring add_refcount(sstring str)
Like add_string(), but the string is already a shared string.
Definition: shstr.cpp:224
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:321
add_string
sstring add_string(const char *str)
Share a string.
Definition: shstr.cpp:137
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:273
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:900
dump
Structure used when dumping quests to stdout.
Definition: quest.cpp:867
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:341
quest_list
static void quest_list(player *pl, player *who, int showall, const char *name)
Display current and completed player quests.
Definition: quest.cpp:525
command_help
void command_help(object *op, const char *params)
Player is asking for some help.
Definition: c_misc.cpp:1770
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:534
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:452
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:233
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:722
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:503
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:294
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:929
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:251
quest_get_player_state
int quest_get_player_state(player *pl, sstring quest_code)
Get the quest state for a player.
Definition: quest.cpp:666
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:535
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:523
quest.h
NDI_UNIQUE
#define NDI_UNIQUE
Print immediately, don't buffer.
Definition: newclient.h:266
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:686
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:103
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:215
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:732
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:100
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:744
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:4506
player::has_directory
uint32_t has_directory
If 0, the player was not yet saved, its directory doesn't exist.
Definition: player.h:151
socket_struct::notifications
uint16_t notifications
Notifications this client wants to get.
Definition: newserver.h:133
player::socket
socket_struct * socket
Socket information for this player.
Definition: player.h:109
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:869
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:406
SockList
Contains the base information we use to make up a packet we want to send.
Definition: newclient.h:685
free_quest
void free_quest(void)
Free all quest status structures.
Definition: quest.cpp:911
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:302
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:250