Crossfire Server, Trunk
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);
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) {
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);
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 
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)
393  draw_ext_info_format(NDI_UNIQUE|NDI_DELAYED, 0, pl->ob, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_QUESTS, "Quest %s completed.", quest->quest_title);
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) {
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) {
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;
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  }
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  }
598  draw_ext_info_format(NDI_UNIQUE, 0, pl->ob, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_QUESTS, TAG_START "Description:" TAG_END " %s", quest->quest_description);
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 
662  quest_state *s = get_state(q, 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;
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 
703  draw_ext_info_format(NDI_UNIQUE | NDI_DELAYED, 0, pl->ob, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_QUESTS, "New quest started: %s", quest->quest_title);
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 
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  }
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) {
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;
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 }
give.next
def next
Definition: give.py:44
output_file.h
SockList_AddInt
void SockList_AddInt(SockList *sl, uint32_t data)
Definition: lowlevel.cpp:124
global.h
NS_FACESENT_FACE
#define NS_FACESENT_FACE
Definition: newserver.h:137
castle_read.prefix
string prefix
Definition: castle_read.py:31
settings
struct Settings settings
Definition: init.cpp:139
output_quests
static void output_quests(const quest_definition *quest, void *user)
Definition: quest.cpp:856
MSG_TYPE_COMMAND_SUCCESS
#define MSG_TYPE_COMMAND_SUCCESS
Definition: newclient.h:519
llevError
@ llevError
Definition: logger.h:11
quest_for_each
void quest_for_each(quest_op op, void *user)
Definition: assets.cpp:539
LOG
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.cpp:51
of_close
int of_close(OutputFile *of)
Definition: output_file.cpp:61
of_open
FILE * of_open(OutputFile *of, const char *fname)
Definition: output_file.cpp:30
player
Definition: player.h:105
get_or_create_quest
static quest_player * get_or_create_quest(player *pl)
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)
Definition: quest.cpp:173
get_new_quest_state
static quest_state * get_new_quest_state(void)
Definition: quest.cpp:87
quest_first_player_save
void quest_first_player_save(player *pl)
Definition: quest.cpp:967
quests_count
static int quests_count
Definition: mapper.cpp:896
SockList_AddString
void SockList_AddString(SockList *sl, const char *data)
Definition: lowlevel.cpp:154
quest_set_state
static void quest_set_state(player *dm, player *pl, sstring quest_code, int state, int started)
Definition: quest.cpp:348
player::ob
object * ob
Definition: player.h:177
Settings::localdir
const char * localdir
Definition: global.h:249
quest_info
static void quest_info(player *pl, player *who, quest_state *qs, int level)
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
mad_mage_user.file
file
Definition: mad_mage_user.py:15
NDI_ALL_DMS
#define NDI_ALL_DMS
Definition: newclient.h:253
MSG_TYPE_COMMAND_QUESTS
#define MSG_TYPE_COMMAND_QUESTS
Definition: newclient.h:516
get_quest
static quest_player * get_quest(player *pl)
Definition: quest.cpp:254
quest_state::was_completed
int was_completed
Definition: quest.cpp:51
dump::parent
const quest_definition * parent
Definition: quest.cpp:848
get_quest_by_number
static quest_state * get_quest_by_number(player *pl, int number)
Definition: quest.cpp:539
SockList_AddLen16Data
void SockList_AddLen16Data(SockList *sl, const void *data, size_t len)
Definition: lowlevel.cpp:188
SockList_Reset
void SockList_Reset(SockList *sl)
Definition: lowlevel.cpp:71
free_state
static void free_state(quest_player *pq)
Definition: quest.cpp:639
quest_step_definition
Definition: quest.h:29
MSG_TYPE_COMMAND
#define MSG_TYPE_COMMAND
Definition: newclient.h:393
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
Definition: quest.cpp:53
SockList
Definition: newclient.h:670
SockList_Avail
size_t SockList_Avail(const SockList *sl)
Definition: lowlevel.cpp:243
MSG_TYPE_ADMIN_DM
#define MSG_TYPE_ADMIN_DM
Definition: newclient.h:486
quest
Definition: quest.py:1
autojail.who
who
Definition: autojail.py:3
space
Crossfire Protocol most of the time after the actual code was already omit certain important and possibly make life miserable any new developer or curious player should be able to find most of the relevant information here If inconsistencies are found or this documentation proves to be consider the latest server side protocol code in the public source code repository as the authoritative reference Introduction If you were ever curious enough to telnet or netcat to a Crossfire chances are you were sorely disappointed While the protocol may seem to use plain text at it actually uses a mix of ASCII and binary data This handbook attempts to document various aspects of the Crossfire protocol As consult the README file to find out how to get in touch with helpful people via mailing and more History the communications plan was set to be a text based system It was up to the server and client to parse these messages and determine what to do These messages were assumed to be line per message At a reasonably early stage of Eric Anderson wrote a then the data itself you could send many data and after the other end could decode these commands This works fairly but I think the creation of numerous sub packets has some performance hit the eutl was not especially well so writing a client for a different platform became more Eric left to work on other products shortly after writing his which didn t really leave anyone with a full understanding of the socket code I have decided to remove the eutl dependency At least one advantage is that having this network related code directly in the client and server makes error handling a bit easier cleaner Packet Format the outside packet method the byte size for the size information is not included here Eutl originally used bytes for the size to bytes seems it makes a least some sense The actual data is something of the nature of the commands listed below It is a text followed by possible other data The remaining data can be binary it is up to the client and server to decode what it sent The commands as described below is just the data portion of the packet If writing a new remember that you must take into account the size of the packet There is no termination of other than knowing how long it should be For most everything that is sent is text This is more or less how things worked under except it packed the ints into bytes in a known order In some we handle ints as in they are sent as binary information How any command handles it is detailed below in the command description The S and C represent the direction of the we use MSB as well as any ints or shorts that get sent inside the packets All packets are defined to have at least one word of followed by a space
Definition: protocol.txt:85
item.q
q
Definition: item.py:32
add_refcount
sstring add_refcount(sstring str)
Definition: shstr.cpp:210
esrv_send_face
void esrv_send_face(socket_struct *ns, const Face *face, int nocache)
Definition: image.cpp:72
of
a copper bar weighs and has a value of
Definition: ore.txt:3
do_update
static void do_update(const quest_definition *quest, void *user)
Definition: quest.cpp:316
add_string
sstring add_string(const char *str)
Definition: shstr.cpp:124
SockList_AddShort
void SockList_AddShort(SockList *sl, uint16_t data)
Definition: lowlevel.cpp:113
NDI_DELAYED
#define NDI_DELAYED
Definition: newclient.h:258
SockList_AddChar
void SockList_AddChar(SockList *sl, unsigned char c)
Definition: lowlevel.cpp:103
dump_quests
void dump_quests(void)
Definition: quest.cpp:880
dump
Definition: quest.cpp:847
quest_get_step
static quest_step_definition * quest_get_step(quest_definition *quest, int step)
Definition: quest.cpp:73
quest_definition::parent
struct quest_definition * parent
Definition: quest.h:47
update_quests
static void update_quests(player *pl)
Definition: quest.cpp:336
quest_list
static void quest_list(player *pl, player *who, int showall, const char *name)
Definition: quest.cpp:519
command_help
void command_help(object *op, const char *params)
Definition: c_misc.cpp:1772
navar-midane_time.data
data
Definition: navar-midane_time.py:11
Settings::playerdir
const char * playerdir
Definition: global.h:250
sproto.h
quest_state::is_complete
int is_complete
Definition: quest.cpp:52
logfile
FILE * logfile
Definition: init.cpp:114
quest_player::quests
quest_state * quests
Definition: quest.cpp:60
quest_get_by_code
quest_definition * quest_get_by_code(sstring code)
Definition: assets.cpp:530
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
SockList_Init
void SockList_Init(SockList *sl)
Definition: lowlevel.cpp:52
quest_display
static void quest_display(player *pl, quest_player *pq, int showall, const char *name)
Definition: quest.cpp:446
get_or_create_state
static quest_state * get_or_create_state(quest_player *pq, sstring name)
Definition: quest.cpp:228
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
quest_set_player_state
void quest_set_player_state(player *pl, sstring quest_code, int state)
Definition: quest.cpp:716
MAX_BUF
#define MAX_BUF
Definition: define.h:35
MSG_TYPE_ADMIN_LOADSAVE
#define MSG_TYPE_ADMIN_LOADSAVE
Definition: newclient.h:488
free_string
void free_string(sstring str)
Definition: shstr.cpp:280
quest_send_initial_states
void quest_send_initial_states(player *pl)
Definition: quest.cpp:909
SockList_Term
void SockList_Term(SockList *sl)
Definition: lowlevel.cpp:62
quest_get_player_state
int quest_get_player_state(player *pl, sstring quest_code)
Definition: quest.cpp:660
TAG_END
#define TAG_END
Definition: quest.cpp:42
MSG_TYPE_COMMAND_FAILURE
#define MSG_TYPE_COMMAND_FAILURE
Definition: newclient.h:520
quest_find_by_code
quest_definition * quest_find_by_code(sstring code)
Definition: assets.cpp:519
FLAG_WIZ
#define FLAG_WIZ
Definition: define.h:231
quest.h
NDI_UNIQUE
#define NDI_UNIQUE
Definition: newclient.h:251
fatal
void fatal(enum fatal_error err)
Definition: utils.cpp:570
quest_start
void quest_start(player *pl, sstring quest_code, int state)
Definition: quest.cpp:680
quest_state
Definition: quest.cpp:48
QC_CAN_RESTART
#define QC_CAN_RESTART
Definition: quest.cpp:45
quest_read_player_data
static void quest_read_player_data(quest_player *pq)
Definition: quest.cpp:98
sstring
const typedef char * sstring
Definition: sstring.h:2
quest_player::player_name
sstring player_name
Definition: quest.cpp:59
give.op
op
Definition: give.py:33
quest_definition
Definition: quest.h:37
quest_state::code
sstring code
Definition: quest.cpp:49
get_state
static quest_state * get_state(quest_player *pq, sstring name)
Definition: quest.cpp:210
quest_player::next
quest_player * next
Definition: quest.cpp:61
quest_was_completed
int quest_was_completed(player *pl, sstring quest_code)
Definition: quest.cpp:726
roll-o-matic.params
params
Definition: roll-o-matic.py:193
exit
Install Bug reporting Credits but rather whatever guild name you are using *With the current map and server there are three they and GreenGoblin *Whatever name you give the folder should but it will still use GUILD_TEMPLATE *You can change what guild it uses by editing the map files Modify Map or objects if you want to use the optional Python based Guild Storage hall The first three are on the main the next two are in the guild_hq and the final one is in hallofjoining Withe the Storage three objects are found on the main floor and the last two are in the basement It s not that but you will need a map editor You find the object that has the click edit and change the line script options(which currently is "GUILD_TEMPALTE") to the guild you wish to use. And make sure you use the same one for all of them or it won 't work. Here 's a quick HOWTO for using the map editor to make these changes edit the mainfloor map exit(x15, y29 - set to/Edit/This/Exit/Path in the template) back to the world map as well. If you are using the Storage Hall map(storage_hall)
assets.h
OutputFile
Definition: output_file.h:41
quest_state::state
int state
Definition: quest.cpp:50
find_player_partial_name
player * find_player_partial_name(const char *plname)
Definition: player.cpp:114
quest_player
Definition: quest.cpp:58
command_quest
void command_quest(object *op, const char *params)
Definition: quest.cpp:738
player_get_delayed_buffer
SockList * player_get_delayed_buffer(player *pl)
Definition: player.cpp:4488
quest.state
state
Definition: quest.py:13
dump::level
int level
Definition: quest.cpp:849
TAG_START
#define TAG_START
Definition: quest.cpp:41
OUT_OF_MEMORY
@ OUT_OF_MEMORY
Definition: define.h:48
altar_valkyrie.pl
pl
Definition: altar_valkyrie.py:28
Send_With_Handling
void Send_With_Handling(socket_struct *ns, SockList *sl)
Definition: lowlevel.cpp:444
MSG_TYPE_ADMIN
#define MSG_TYPE_ADMIN
Definition: newclient.h:391
free_quest
void free_quest(void)
Definition: quest.cpp:891
quest_state::next
quest_state * next
Definition: quest.cpp:54
player_states
static quest_player * player_states
Definition: quest.cpp:65
ring_occidental_mages.r
r
Definition: ring_occidental_mages.py:6
llevDebug
@ llevDebug
Definition: logger.h:13
evaluate_quest_conditions
static int evaluate_quest_conditions(const std::vector< quest_condition * > conditions, player *pl)
Definition: quest.cpp:297
i18n
const char * i18n(const object *who, const char *code)
Definition: languages.cpp:42
level
Definition: level.py:1