Crossfire Server, Trunk
item.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 
25 #include "global.h"
26 
27 #include <stdlib.h>
28 #include <string.h>
29 
30 #include "newserver.h"
31 #include "object.h"
32 #include "shared/newclient.h"
33 #include "sproto.h"
34 
36 #define MAXITEMLEN 300
37 
38 /*************************************************************************
39  *
40  * Functions related to sending object data to the client.
41  *
42  *************************************************************************
43  */
44 
49 static unsigned int query_flags(const object *op) {
50  unsigned int flags = 0;
51 
52  if (QUERY_FLAG(op, FLAG_APPLIED)) {
53  switch (op->type) {
54  case BOW:
55  case WAND:
56  case ROD:
57  flags = a_readied;
58  break;
59 
60  case WEAPON:
61  flags = a_wielded;
62  break;
63 
64  case SKILL:
65  case ARMOUR:
66  case HELMET:
67  case SHIELD:
68  case RING:
69  case BOOTS:
70  case GLOVES:
71  case AMULET:
72  case GIRDLE:
73  case BRACERS:
74  case CLOAK:
75  flags = a_worn;
76  break;
77 
78  case CONTAINER:
79  flags = a_active;
80  break;
81 
82  default:
83  flags = a_applied;
84  break;
85  }
86  }
87  if (op->type == CONTAINER
88  && ((op->env && op->env->container == op) || (!op->env && QUERY_FLAG(op, FLAG_APPLIED))))
89  flags |= F_OPEN;
90 
91  if (!is_identified(op))
93 
96  flags |= F_DAMNED;
97  else if (QUERY_FLAG(op, FLAG_CURSED))
98  flags |= F_CURSED;
99  }
101  flags |= F_MAGIC;
102  if (QUERY_FLAG(op, FLAG_UNPAID))
103  flags |= F_UNPAID;
105  flags |= F_LOCKED;
107  flags |= F_BLESSED;
108  // Denote when a book has been read. This gives GUI feedback in the inventory window if the client handles the flag.
109  // NO_SKILL_IDENT is set when identification fails or when the book is read. So the book is read only when it is
110  // both identified and NO_SKILL_IDENT.
112  flags |= F_READ;
113 
114  return flags;
115 }
116 
122 static void add_object_to_socklist(socket_struct *ns, SockList *sl, object *head) {
123  int flags, len, anim_speed;
124  char item_n[MAX_BUF], item_p[MAX_BUF];
125  sstring custom_name = object_get_value(head, CUSTOM_NAME_FIELD);
126 
127  flags = query_flags(head);
128  if (QUERY_FLAG(head, FLAG_NO_PICK))
129  flags |= F_NOPICK;
130 
131  if (!(ns->faces_sent[head->face->number]&NS_FACESENT_FACE))
132  esrv_send_face(ns, head->face, 0);
133 
134  if (QUERY_FLAG(head, FLAG_ANIMATE)) {
135  if (head->animation == NULL) {
136  LOG(llevError, "Item %s in %s (%d,%d) has FLAG_ANIMATE but animation_id 0\n", head->name, (head->env ? head->env->name : (head->map ? head->map->path : "???")), head->x, head->y);
137  CLEAR_FLAG(head, FLAG_ANIMATE);
138  } else if (!ns->anims_sent[head->animation->num])
139  esrv_send_animation(ns, head->animation);
140  }
141 
142  SockList_AddInt(sl, head->count);
143  SockList_AddInt(sl, flags);
144  SockList_AddInt(sl, QUERY_FLAG(head, FLAG_NO_PICK) ? -1 : WEIGHT(head));
145  SockList_AddInt(sl, head->face->number);
146 
147  if (!custom_name) {
148  query_base_name(head, 0, item_n, 126);
149  item_n[127] = 0;
150  len = strlen(item_n);
151  query_base_name(head, 1, item_p, MAX_BUF);
152  } else {
153  safe_strncpy(item_n, custom_name, 127);
154  item_n[127] = 0;
155  len = strlen(item_n);
156  safe_strncpy(item_p, custom_name, sizeof(item_p));
157  }
158  strncpy(item_n+len+1, item_p, 127);
159  /* This is needed because strncpy may not add a ending \0 if the string is long enough. */
160  item_n[len+1+127] = 0;
161  len += strlen(item_n+1+len)+1;
162  SockList_AddLen8Data(sl, item_n, len);
163 
164  SockList_AddShort(sl, (QUERY_FLAG(head, FLAG_ANIMATE) && head->animation) ? head->animation->num : 0);
165  anim_speed = 0;
166  if (QUERY_FLAG(head, FLAG_ANIMATE)) {
167  if (head->anim_speed)
168  anim_speed = head->anim_speed;
169  else {
170  if (FABS(head->speed) < 0.001)
171  anim_speed = 255;
172  else if (FABS(head->speed) >= 1.0)
173  anim_speed = 1;
174  else
175  anim_speed = (int)(1.0/FABS(head->speed));
176  }
177  if (anim_speed > 255)
178  anim_speed = 255;
179  }
180  SockList_AddChar(sl, (char)anim_speed);
181  SockList_AddInt(sl, head->nrof);
182 
183  SockList_AddShort(sl, head->client_type);
184 
185  SET_FLAG(head, FLAG_CLIENT_SENT);
186 }
187 
193 void esrv_draw_look(object *pl) {
194  object *tmp, *last;
195  int got_one = 0, start_look = 0, end_look = 0, objects_sent = 0;
196  SockList sl;
197  char buf[MAX_BUF];
198 
199  if (!pl->contr->socket->update_look) {
200  LOG(llevDebug, "esrv_draw_look called when update_look was not set\n");
201  return;
202  } else {
203  pl->contr->socket->update_look = 0;
204  }
205 
207  || pl->map == NULL
208  || pl->map->in_memory != MAP_IN_MEMORY
209  || out_of_map(pl->map, pl->x, pl->y))
210  return;
211 
212  if (pl->contr->transport)
213  for (tmp = pl->contr->transport->inv; tmp && tmp->above; tmp = tmp->above)
214  ;
215  else
216  for (tmp = GET_MAP_OB(pl->map, pl->x, pl->y); tmp && tmp->above; tmp = tmp->above)
217  ;
218 
219  SockList_Init(&sl);
220  SockList_AddString(&sl, "delinv 0");
221  Send_With_Handling(pl->contr->socket, &sl);
222 
223  SockList_Reset(&sl);
224  SockList_AddPrintf(&sl, "item2 ");
225  SockList_AddInt(&sl, 0);
226 
227  if (!(pl->contr->socket->faces_sent[empty_face->number]&NS_FACESENT_FACE))
228  esrv_send_face(pl->contr->socket, empty_face, 0);
229 
230  if (pl->contr->socket->look_position) {
231  int overhead = 1+(pl->contr->transport != NULL);
232  int prev_len = pl->contr->socket->num_look_objects-overhead-(pl->contr->socket->look_position > pl->contr->socket->num_look_objects-overhead);
233  SockList_AddInt(&sl, 0x80000000|MAX(0, pl->contr->socket->look_position-prev_len));
234  SockList_AddInt(&sl, 0);
235  SockList_AddInt(&sl, -1);
237  snprintf(buf, sizeof(buf), "Click here to see previous group of items");
238  SockList_AddLen8Data(&sl, buf, MIN(strlen(buf), 255));
239  SockList_AddShort(&sl, 0);
240  SockList_AddChar(&sl, 0);
241  SockList_AddInt(&sl, 0);
242  SockList_AddShort(&sl, 0);
243  objects_sent++;
244  got_one++;
245  }
246 
247  if (pl->contr->transport) {
248  add_object_to_socklist(pl->contr->socket, &sl, pl->contr->transport);
249  objects_sent++;
250  got_one++;
251  }
252 
253  last = NULL;
255  object *head;
256 
257  if (tmp == last) {
258  break;
259  }
260 
261  if (!QUERY_FLAG(pl, FLAG_WIZ)) {
262  // Unless DM, stop at the first floor or two consecutive floor objects.
263  if (QUERY_FLAG(tmp, FLAG_IS_FLOOR) && !last) {
264  last = tmp->below; /* assumes double floor mode */
265  if (last && QUERY_FLAG(last, FLAG_IS_FLOOR))
266  last = last->below;
267  }
268  }
269  if (QUERY_FLAG(pl, FLAG_WIZ) || LOOK_OBJ(tmp)) {
270  if (start_look++ < pl->contr->socket->look_position)
271  continue;
272  end_look++;
273  objects_sent++;
274  if (objects_sent >= pl->contr->socket->num_look_objects) {
275  /* What we basically do is make a 'fake' object -
276  * when the user applies it, we notice the special
277  * tag the object has, and act accordingly.
278  */
279  SockList_AddInt(&sl, 0x80000000|(pl->contr->socket->look_position+end_look-1));
280  SockList_AddInt(&sl, 0);
281  SockList_AddInt(&sl, -1);
283  snprintf(buf, sizeof(buf), "Click here to see next group of items");
284  SockList_AddLen8Data(&sl, buf, MIN(strlen(buf), 255));
285  SockList_AddShort(&sl, 0);
286  SockList_AddChar(&sl, 0);
287  SockList_AddInt(&sl, 0);
288  SockList_AddShort(&sl, 0);
289  break;
290  }
291  head = HEAD(tmp);
292  add_object_to_socklist(pl->contr->socket, &sl, head);
293  got_one++;
294 
295  if (SockList_Avail(&sl) < MAXITEMLEN) {
296  Send_With_Handling(pl->contr->socket, &sl);
297  SockList_Reset(&sl);
298  SockList_AddPrintf(&sl, "item2 ");
299  SockList_AddInt(&sl, 0);
300  got_one = 0;
301  }
302  } /* If LOOK_OBJ() */
304  if (got_one)
305  Send_With_Handling(pl->contr->socket, &sl);
306 
307  SockList_Term(&sl);
308 }
309 
316 void esrv_send_inventory(object *pl, object *op) {
317  int got_one = 0, start_look = 0, end_look = 0, objects_sent = 0;
318  SockList sl;
319  char buf[MAX_BUF];
320  int prev_len = pl->contr->socket->num_look_objects - 2 - (((pl->contr->socket->container_position > pl->contr->socket->num_look_objects - 1)) ? 1 : 0);
321 
322  SockList_Init(&sl);
323  SockList_AddPrintf(&sl, "delinv %u", op->count);
324  Send_With_Handling(pl->contr->socket, &sl);
325 
326  SockList_Reset(&sl);
327  SockList_AddString(&sl, "item2 ");
328  SockList_AddInt(&sl, op->count);
329  objects_sent++;
330 
331  if (pl != op && pl->contr->socket->container_position) {
332  SockList_AddInt(&sl, 0x80000000|MAX(0, pl->contr->socket->container_position-prev_len));
333  SockList_AddInt(&sl, 0);
334  SockList_AddInt(&sl, -1);
336  snprintf(buf, sizeof(buf), "Click here to see previous group of items");
337  SockList_AddLen8Data(&sl, buf, MIN(strlen(buf), 255));
338  SockList_AddShort(&sl, 0);
339  SockList_AddChar(&sl, 0);
340  SockList_AddInt(&sl, 0);
341  SockList_AddShort(&sl, 0);
342  objects_sent++;
343  got_one++;
344  }
345 
347  object *head;
348 
349  head = HEAD(tmp);
350  if (LOOK_OBJ(head)) {
351  if (start_look++ < pl->contr->socket->container_position && pl != op)
352  continue;
353  end_look++;
354  objects_sent++;
355  if (pl != op && objects_sent >= pl->contr->socket->num_look_objects) {
356  /* What we basically do is make a 'fake' object -
357  * when the user applies it, we notice the special
358  * tag the object has, and act accordingly.
359  */
360  SockList_AddInt(&sl, 0x80000000|(pl->contr->socket->container_position + end_look - 1));
361  SockList_AddInt(&sl, 0);
362  SockList_AddInt(&sl, -1);
364  snprintf(buf, sizeof(buf), "Click here to see next group of items");
365  SockList_AddLen8Data(&sl, buf, MIN(strlen(buf), 255));
366  SockList_AddShort(&sl, 0);
367  SockList_AddChar(&sl, 0);
368  SockList_AddInt(&sl, 0);
369  SockList_AddShort(&sl, 0);
370  break;
371  }
372 
373  add_object_to_socklist(pl->contr->socket, &sl, head);
374 
375  got_one++;
376 
377  /* It is possible for players to accumulate a huge amount of
378  * items (especially with some of the bags out there) to
379  * overflow the buffer. IF so, send multiple item commands.
380  */
381  if (SockList_Avail(&sl) < MAXITEMLEN) {
382  Send_With_Handling(pl->contr->socket, &sl);
383  SockList_Reset(&sl);
384  SockList_AddString(&sl, "item2 ");
385  SockList_AddInt(&sl, op->count);
386  got_one = 0;
387  }
388  } /* If LOOK_OBJ() */
389  } FOR_INV_FINISH();
390  if (got_one) {
391  /* special case: only one item, the "prev group" arrow */
392  if (pl != op && pl->contr->socket->container_position) {
393  if (got_one > 1)
394  Send_With_Handling(pl->contr->socket, &sl);
395  else {
396  /* view shifted, get to previous page and resend */
397  pl->contr->socket->container_position = MAX(0, pl->contr->socket->container_position - prev_len);
399  }
400  } else
401  Send_With_Handling(pl->contr->socket, &sl);
402  }
403  SockList_Term(&sl);
404 }
405 
414 void esrv_update_item(int flags, object *pl, object *op) {
415  SockList sl;
416 
417  if (!pl->contr)
418  return;
419 
420  /* If we have a request to send the player item, skip a few checks. */
421  if (op != pl) {
422  if (!LOOK_OBJ(op))
423  return;
424  /* we remove the check for op->env, because in theory, the object
425  * is hopefully in the same place, so the client should preserve
426  * order.
427  */
428  }
429  if (!QUERY_FLAG(op, FLAG_CLIENT_SENT)) {
430  // Sometimes, we try to update an item that we haven't sent to the
431  // client. Don't! This can happen, for example, when a button under the
432  // floor gets toggled, but objects under floor tiles are generally not
433  // sent. There are some other places where this happens that we haven't
434  // tracked down, but in general, just don't.
435  //LOG(llevDebug, "We have not sent item %s (%d)\n", op->name, op->count);
436  return;
437  }
438 
439  SockList_Init(&sl);
440  SockList_AddString(&sl, "upditem ");
441  SockList_AddChar(&sl, (char)flags);
442 
443  op = HEAD(op);
444  SockList_AddInt(&sl, op->count);
446 
447  if (flags&UPD_LOCATION)
448  SockList_AddInt(&sl, op->env ? op->env->count : 0);
449 
450  if (flags&UPD_FLAGS)
452 
453  if (flags&UPD_WEIGHT) {
454  int32_t weight = WEIGHT(op);
455 
456  /* TRANSPORTS are odd - they sort of look like containers,
457  * yet can't be picked up. So we don't to send the weight,
458  * as it is odd that you see weight sometimes and not other
459  * (the draw_look won't send it for example.
460  */
462  if (pl == op) {
463  op->contr->last_weight = weight;
464  }
465  }
466 
467  if (flags&UPD_FACE) {
468  if (!(pl->contr->socket->faces_sent[op->face->number]&NS_FACESENT_FACE))
469  esrv_send_face(pl->contr->socket, op->face, 0);
470  SockList_AddInt(&sl, op->face->number);
471  }
472  if (flags&UPD_NAME) {
473  int len;
474  char item_p[MAX_BUF];
475  char item_n[MAX_BUF];
476 
477  if (!custom_name) {
478  query_base_name(op, 0, item_n, sizeof(item_n)-1);
479  query_base_name(op, 1, item_p, sizeof(item_p));
480  } else {
481  strlcpy(item_n, custom_name, sizeof(item_n)-1);
482  strlcpy(item_p, custom_name, sizeof(item_p));
483  }
484 
485  len = strlen(item_n)+1;
486  snprintf(item_n+len, sizeof(item_n)-len, "%s", item_p);
487  len += strlen(item_n+len);
488  SockList_AddLen8Data(&sl, item_n, len);
489  }
490  if (flags&UPD_ANIM)
491  SockList_AddShort(&sl, op->animation ? op->animation->num : 0);
492 
493  if (flags&UPD_ANIMSPEED) {
494  int anim_speed = 0;
495 
496  if (QUERY_FLAG(op, FLAG_ANIMATE)) {
497  if (op->anim_speed)
498  anim_speed = op->anim_speed;
499  else {
500  if (FABS(op->speed) < 0.001)
501  anim_speed = 255;
502  else if (FABS(op->speed) >= 1.0)
503  anim_speed = 1;
504  else
505  anim_speed = (int)(1.0/FABS(op->speed));
506  }
507  if (anim_speed > 255)
508  anim_speed = 255;
509  }
510  SockList_AddChar(&sl, (char)anim_speed);
511  }
512  if (flags&UPD_NROF)
513  SockList_AddInt(&sl, op->nrof);
514 
515  Send_With_Handling(pl->contr->socket, &sl);
516  SockList_Term(&sl);
517 }
518 
522 void esrv_send_item(object *pl, object*op) {
523  SockList sl;
524 
525  /* If this is not the player object, do some more checks */
526  if (op != pl) {
527  /* We only send 'visibile' objects to the client */
528  if (!LOOK_OBJ(op))
529  return;
530  /* if the item is on the ground, mark that the look needs to
531  * be updated.
532  */
533  if (!op->env) {
534  pl->contr->socket->update_look = 1;
535  return;
536  }
537  }
538 
539  SockList_Init(&sl);
540  SockList_AddString(&sl, "item2 ");
541 
542  op = HEAD(op);
543  SockList_AddInt(&sl, op->env ? op->env->count : 0);
544 
545  add_object_to_socklist(pl->contr->socket, &sl, op);
546 
547  Send_With_Handling(pl->contr->socket, &sl);
549  SockList_Term(&sl);
550 
551  /* if the object is in an opened container, then it may shift the contents,
552  * so resend everything */
553  if (pl->contr != NULL && pl->container != NULL && op->env == pl->container)
554  pl->contr->socket->update_inventory = 1;
555 }
556 
562 void esrv_del_item(player *pl, object *ob) {
563  SockList sl;
564 
565  SockList_Init(&sl);
566  SockList_AddString(&sl, "delitem ");
567  SockList_AddInt(&sl, ob->count);
568  Send_With_Handling(pl->socket, &sl);
569  SockList_Term(&sl);
570  /* if the object is in an opened container, then it may shift the contents,
571  * so resend everything */
572  if (pl->ob->container != NULL && ob->env == pl->ob->container)
573  pl->socket->update_inventory = 1;
574 }
575 
576 /**************************************************************************
577  *
578  * Client has requested us to do something with an object.
579  *
580  **************************************************************************
581  */
582 
587 bool player_can_find(object *op, object *ob) {
588  return QUERY_FLAG(op, FLAG_WIZ) || !ob->invisible;
589 }
590 
594 static object *ob_if_can_find(object *op, object *ob) {
595  if (player_can_find(op, ob)) {
596  return ob;
597  } else {
598  return NULL;
599  }
600 }
601 
607 static object *esrv_get_ob_from_count(object *pl, tag_t count) {
608  if (pl->count == count)
609  return pl;
610 
612  if (op->count == count)
613  return ob_if_can_find(pl, op);
614  else if (op->type == CONTAINER && pl->container == op) {
616  if (tmp->count == count)
617  return ob_if_can_find(pl, tmp);
618  FOR_INV_FINISH();
619  }
620  FOR_INV_FINISH();
621 
622  FOR_MAP_PREPARE(pl->map, pl->x, pl->y, op)
623  if (HEAD(op)->count == count)
624  return ob_if_can_find(pl, op);
625  else if (op->type == CONTAINER && pl->container == op) {
627  if (tmp->count == count)
628  return ob_if_can_find(pl, tmp);
629  FOR_INV_FINISH();
630  }
631  FOR_MAP_FINISH();
632 
633  if (pl->contr->transport) {
634  FOR_INV_PREPARE(pl->contr->transport, tmp)
635  if (tmp->count == count)
636  return ob_if_can_find(pl, tmp);
637  FOR_INV_FINISH();
638  }
639  return NULL;
640 }
641 
643 void examine_cmd(char *buf, int len, player *pl) {
644  long tag;
645  object *op;
646 
647  if (len <= 0 || !buf) {
648  LOG(llevDebug, "Player '%s' sent bogus examine_cmd information\n", pl->ob->name);
649  return;
650  }
651 
652  tag = atoi(buf);
654  if (!op) {
655  LOG(llevDebug, "Player '%s' tried to examine the unknown object (%ld)\n", pl->ob->name, tag);
656  return;
657  }
658  examine(pl->ob, op);
659  if (QUERY_FLAG(pl->ob, FLAG_WIZ)) {
660  do_dump(pl->ob, op);
661  }
662 }
663 
665 void apply_cmd(char *buf, int len, player *pl) {
666  uint32_t tag;
667  object *op;
668 
669  if (!buf || len <= 0) {
670  LOG(llevDebug, "Player '%s' sent bogus apply_cmd information\n", pl->ob->name);
671  return;
672  }
673 
674  tag = atoi(buf);
676 
677  /* sort of a hack, but if the player saves and the player then
678  * manually applies a savebed (or otherwise tries to do stuff),
679  * we run into trouble.
680  */
681  if (QUERY_FLAG(pl->ob, FLAG_REMOVED))
682  return;
683 
684  /* If the high bit is set, player applied a pseudo object. */
685  if (tag&0x80000000) {
686  if (pl->ob->container != NULL) {
687  pl->socket->container_position = tag&0x7fffffff;
688  esrv_send_inventory(pl->ob, pl->ob->container);
689  pl->socket->update_inventory = 0;
690  } else {
691  pl->socket->look_position = tag&0x7fffffff;
692  pl->socket->update_look = 1;
693  }
694  return;
695  }
696 
697  if (!op) {
698  LOG(llevDebug, "Player '%s' tried to apply the unknown object (%d)\n", pl->ob->name, tag);
699  return;
700  }
701  apply_by_living(pl->ob, op, 0, 0);
702 }
703 
705 void lock_item_cmd(uint8_t *data, int len, player *pl) {
706  int flag, tag;
707  object *op;
708  object *tmp;
709  char name[HUGE_BUF];
710 
711  if (len != 5) {
712  LOG(llevDebug, "Player '%s' sent bogus lock_item_cmd information\n", pl->ob->name);
713  return;
714  }
715  flag = data[0];
716  tag = GetInt_String(data+1);
718 
719  if (!op) {
721  "Could not find object to lock/unlock");
722  return;
723  }
724 
725  if (op->map) {
727  "Can't lock/unlock an item on the ground");
728  return;
729  }
730  if (op->env != pl->ob) {
732  "Can't lock/unlock an item not directly in your inventory");
733  return;
734  }
735 
737  if (!flag) {
740  "Unlocked %s.", name);
741  } else {
744  "Locked %s.", name);
745  }
746 
747  tmp = object_merge(op, NULL);
748  if (tmp == NULL) {
749  /* object was not merged - if it was, object_merge() sent updates for us. */
751  }
752 }
753 
764 void mark_item_cmd(uint8_t *data, int len, player *pl) {
765  int tag;
766  object *op;
767  char name[MAX_BUF];
768 
769  if (len != 4) {
770  LOG(llevDebug, "Player '%s' sent bogus mark_item_cmd information\n", pl->ob->name);
771  return;
772  }
773 
776  if (!op) {
778  "Could not find object to mark");
779  return;
780  }
781  pl->mark = op;
782  pl->mark_count = op->count;
785  "Marked item %s",
786  name);
787 }
788 
795 void look_at(object *op, int dx, int dy) {
796  object *tmp;
797  int flag = 0;
798  int16_t x, y;
799  mapstruct *m;
800  char name[MAX_BUF];
801 
802  if (out_of_map(op->map, op->x+dx, op->y+dy))
803  return;
804 
805  x = op->x+dx;
806  y = op->y+dy;
807 
808  m = get_map_from_coord(op->map, &x, &y);
809  if (!m)
810  return;
811 
812  for (tmp = GET_MAP_OB(m, x, y); tmp != NULL && tmp->above != NULL; tmp = tmp->above)
813  ;
814 
816  if (tmp->invisible && !QUERY_FLAG(op, FLAG_WIZ))
817  continue;
818 
819  if (!flag) {
820  if (dx || dy)
822  "There you see:");
823  else {
825  "You see:");
826  }
827  flag = 1;
828  }
829 
831  if (QUERY_FLAG(op, FLAG_WIZ))
833  "- %s (%d).",
834  name, tmp->count);
835  else
837  "- %s.",
838  name);
839 
840  if ((HEAD(tmp)->inv != NULL && (tmp->type != CONTAINER && tmp->type != FLESH))
841  || QUERY_FLAG(op, FLAG_WIZ))
842  inventory(op, HEAD(tmp));
843 
844  /* don't continue under the floor */
846  break;
848 
849  if (!flag) {
850  if (dx || dy)
852  "You see nothing there.");
853  else
855  "You see nothing.");
856  }
857 }
858 
860 void look_at_cmd(char *buf, int len, player *pl) {
861  int dx, dy;
862  char *cp;
863 
864  if (len <= 0 || !buf) {
865  LOG(llevDebug, "Player '%s' sent bogus look_at_cmd information\n", pl->ob->name);
866  return;
867  }
868 
869  dx = atoi(buf);
870  if (!(cp = strchr(buf, ' '))) {
871  return;
872  }
873  dy = atoi(cp);
874 
875  if (FABS(dx) > MAP_CLIENT_X/2 || FABS(dy) > MAP_CLIENT_Y/2) {
877  "You can't see there from where you're standing.");
878  return;
879  }
880 
881  if (pl->blocked_los[dx+(pl->socket->mapx/2)][dy+(pl->socket->mapy/2)]) {
883  "You can't see there from where you're standing.");
884  return;
885  }
886  look_at(pl->ob, dx, dy);
887 }
888 
890 void esrv_move_object(object *pl, tag_t to, tag_t tag, long nrof) {
891  object *op, *env;
892 
894  if (!op) {
895  LOG(llevDebug, "Player '%s' tried to move an unknown object (%lu)\n", pl->name, (unsigned long)tag);
896  return;
897  }
898 
899  /* If on a transport, you don't drop to the ground - you drop to the
900  * transport.
901  */
902  if (!to && !pl->contr->transport) { /* drop it to the ground */
903  /* LOG(llevDebug, "Drop it on the ground.\n");*/
904 
905  if (op->map && !op->env) {
906 /* LOG(llevDebug, "Dropping object to ground that is already on ground\n");*/
907  return;
908  }
909  /* If it is an active container, then we should drop all objects
910  * in the container and not the container itself.
911  */
912  if (op->inv && QUERY_FLAG(op, FLAG_APPLIED)) {
914  drop_object(pl, current, 0);
915  FOR_INV_FINISH();
917  } else {
918  drop_object(pl, op, nrof);
919  }
920  return;
921  } else if (to == pl->count) { /* pick it up to the inventory */
922  /* return if player has already picked it up */
923  if (op->env == pl)
924  return;
925 
926  pl->contr->count = nrof;
927  pick_up(pl, op);
928  return;
929  }
930  /* If not dropped or picked up, we are putting it into a sack */
931  if (pl->contr->transport) {
932  if (object_can_pick(pl, op)
933  && transport_can_hold(pl->contr->transport, op, nrof)) {
934  put_object_in_sack(pl, pl->contr->transport, op, nrof);
935  }
936  } else {
938  if (!env) {
939  LOG(llevDebug, "Player '%s' tried to move object to the unknown location (%d)\n", pl->name, to);
940  return;
941  }
942  /* put_object_in_sack presumes that necessary sanity checking
943  * has already been done (eg, it can be picked up and fits in
944  * in a sack, so check for those things. We should also check
945  * an make sure env is in fact a container for that matter.
946  */
947  if (env->type == CONTAINER
948  && object_can_pick(pl, op)
949  && sack_can_hold(pl, env, op, nrof)) {
950  put_object_in_sack(pl, env, op, nrof);
951  }
952  }
953 }
954 
955 void inscribe_scroll_cmd(char *buf, int len, player *pl) {
956  object *scroll, *spell, *marked, *inscription, *currentspell;
957  tag_t tscroll, tspell, tmarked;
958  char type;
959 
960  if (len < 1) {
961  LOG(llevDebug, "Player %s sent an invalid inscribe command.\n", pl->ob->name);
962  return;
963  }
964 
965  type = buf[0];
966 
967  inscription = find_skill_by_name(pl->ob, "inscription");
968  if (!inscription) {
969  draw_ext_info(NDI_UNIQUE, 0, pl->ob, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE, "You don't know how to write!");
970  return;
971  }
972 
973  if (type == 0) {
974  if (len != 9) {
975  LOG(llevDebug, "Player %s sent an invalid inscribe command.\n", pl->ob->name);
976  return;
977  }
978  tscroll = GetInt_String((uint8_t *)buf+1);
979  tspell = GetInt_String((uint8_t *)buf+5);
980 
981  scroll = esrv_get_ob_from_count(pl->ob, tscroll);
982  if (!scroll) {
983  LOG(llevDebug, "Player %s sent an invalid scroll for inscribe command.\n", pl->ob->name);
984  return;
985  }
986 
987  spell = esrv_get_ob_from_count(pl->ob, tspell);
988  if (!spell) {
989  LOG(llevDebug, "Player %s sent an invalid spell for inscribe command.\n", pl->ob->name);
990  return;
991  }
992 
993  tmarked = pl->mark_count;
994  marked = pl->mark;
995  currentspell = pl->ranges[range_magic];
996 
997  pl->mark_count = tscroll;
998  pl->mark = scroll;
999  pl->ranges[range_magic] = spell;
1000 
1001  write_on_item(pl->ob, "", inscription);
1002 
1003  pl->mark_count = tmarked;
1004  pl->mark = marked;
1005  pl->ranges[range_magic] = currentspell;
1006  } else {
1007  }
1008 }
GET_MAP_OB
#define GET_MAP_OB(M, X, Y)
Definition: map.h:171
MAP_CLIENT_X
#define MAP_CLIENT_X
Definition: config.h:237
SockList_AddInt
void SockList_AddInt(SockList *sl, uint32_t data)
Definition: lowlevel.cpp:124
global.h
UPD_FACE
#define UPD_FACE
Definition: newclient.h:306
NS_FACESENT_FACE
#define NS_FACESENT_FACE
Definition: newserver.h:137
safe_strncpy
#define safe_strncpy
Definition: compat.h:27
FOR_MAP_FINISH
#define FOR_MAP_FINISH()
Definition: define.h:730
esrv_move_object
void esrv_move_object(object *pl, tag_t to, tag_t tag, long nrof)
Definition: item.cpp:890
MSG_TYPE_COMMAND_SUCCESS
#define MSG_TYPE_COMMAND_SUCCESS
Definition: newclient.h:519
BOW
@ BOW
Definition: object.h:123
BRACERS
@ BRACERS
Definition: object.h:222
F_OPEN
#define F_OPEN
Definition: newclient.h:278
llevError
@ llevError
Definition: logger.h:11
FABS
#define FABS(x)
Definition: define.h:22
WAND
@ WAND
Definition: object.h:225
LOG
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.cpp:51
SET_FLAG
#define SET_FLAG(xyz, p)
Definition: define.h:224
mark_item_cmd
void mark_item_cmd(uint8_t *data, int len, player *pl)
Definition: item.cpp:764
FLESH
@ FLESH
Definition: object.h:192
player
Definition: player.h:105
object::client_type
uint16_t client_type
Definition: object.h:350
GLOVES
@ GLOVES
Definition: object.h:218
GIRDLE
@ GIRDLE
Definition: object.h:228
F_UNPAID
#define F_UNPAID
Definition: newclient.h:274
diamondslots.x
x
Definition: diamondslots.py:15
CFweardisguise.tag
tag
Definition: CFweardisguise.py:25
MSG_TYPE_SKILL
#define MSG_TYPE_SKILL
Definition: newclient.h:396
QUERY_FLAG
#define QUERY_FLAG(xyz, p)
Definition: define.h:226
socket_struct
Definition: newserver.h:89
object_merge
object * object_merge(object *op, object *top)
Definition: object.cpp:2046
UPD_WEIGHT
#define UPD_WEIGHT
Definition: newclient.h:305
SockList_AddString
void SockList_AddString(SockList *sl, const char *data)
Definition: lowlevel.cpp:154
object::speed
float speed
Definition: object.h:337
object::x
int16_t x
Definition: object.h:335
ARMOUR
@ ARMOUR
Definition: object.h:125
object::map
struct mapstruct * map
Definition: object.h:305
UPD_LOCATION
#define UPD_LOCATION
Definition: newclient.h:303
do_dump
void do_dump(object *who, object *what)
Definition: c_wiz.cpp:2826
WEAPON
@ WEAPON
Definition: object.h:124
guildjoin.ob
ob
Definition: guildjoin.py:42
write_on_item
int write_on_item(object *pl, const char *params, object *skill)
Definition: skills.cpp:1762
commongive.inv
inv
Definition: commongive.py:29
AMULET
@ AMULET
Definition: object.h:144
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
MIN
#define MIN(x, y)
Definition: compat.h:21
SKILL
@ SKILL
Definition: object.h:148
examine_cmd
void examine_cmd(char *buf, int len, player *pl)
Definition: item.cpp:643
object::count
tag_t count
Definition: object.h:307
flags
static const flag_definition flags[]
Definition: gridarta-types-convert.cpp:101
Ice.tmp
int tmp
Definition: Ice.py:207
MSG_TYPE_COMMAND_EXAMINE
#define MSG_TYPE_COMMAND_EXAMINE
Definition: newclient.h:521
esrv_update_item
void esrv_update_item(int flags, object *pl, object *op)
Definition: item.cpp:414
FLAG_BLESSED
#define FLAG_BLESSED
Definition: define.h:369
UPD_NROF
#define UPD_NROF
Definition: newclient.h:310
MSG_TYPE_COMMAND_ERROR
#define MSG_TYPE_COMMAND_ERROR
Definition: newclient.h:518
FLAG_INV_LOCKED
#define FLAG_INV_LOCKED
Definition: define.h:329
SockList_Reset
void SockList_Reset(SockList *sl)
Definition: lowlevel.cpp:71
mapstruct::path
char path[HUGE_BUF]
Definition: map.h:355
object_get_value
const char * object_get_value(const object *op, const char *const key)
Definition: object.cpp:4337
FLAG_APPLIED
#define FLAG_APPLIED
Definition: define.h:235
a_active
@ a_active
Definition: newclient.h:270
Animations::num
uint16_t num
Definition: face.h:29
buf
StringBuffer * buf
Definition: readable.cpp:1552
HUGE_BUF
#define HUGE_BUF
Definition: define.h:37
MSG_TYPE_COMMAND
#define MSG_TYPE_COMMAND
Definition: newclient.h:393
MAX
#define MAX(x, y)
Definition: compat.h:24
F_READ
#define F_READ
Definition: newclient.h:282
FLAG_NO_PICK
#define FLAG_NO_PICK
Definition: define.h:239
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
SockList
Definition: newclient.h:670
SockList_Avail
size_t SockList_Avail(const SockList *sl)
Definition: lowlevel.cpp:243
LOOK_OBJ
#define LOOK_OBJ(ob)
Definition: object.h:512
object::y
int16_t y
Definition: object.h:335
m
static event_registration m
Definition: citylife.cpp:425
CLOAK
@ CLOAK
Definition: object.h:209
MAP_IN_MEMORY
#define MAP_IN_MEMORY
Definition: map.h:127
a_readied
@ a_readied
Definition: newclient.h:270
lock_item_cmd
void lock_item_cmd(uint8_t *data, int len, player *pl)
Definition: item.cpp:705
MAP_CLIENT_Y
#define MAP_CLIENT_Y
Definition: config.h:238
HELMET
@ HELMET
Definition: object.h:141
add_object_to_socklist
static void add_object_to_socklist(socket_struct *ns, SockList *sl, object *head)
Definition: item.cpp:122
F_DAMNED
#define F_DAMNED
Definition: newclient.h:277
object::anim_speed
uint8_t anim_speed
Definition: object.h:427
esrv_send_face
void esrv_send_face(socket_struct *ns, const Face *face, int nocache)
Definition: image.cpp:72
query_name
void query_name(const object *op, char *buf, size_t size)
Definition: item.cpp:592
FLAG_KNOWN_BLESSED
#define FLAG_KNOWN_BLESSED
Definition: define.h:370
FLAG_KNOWN_CURSED
#define FLAG_KNOWN_CURSED
Definition: define.h:320
a_applied
@ a_applied
Definition: newclient.h:270
FOR_OB_AND_BELOW_FINISH
#define FOR_OB_AND_BELOW_FINISH()
Definition: define.h:754
HEAD
#define HEAD(op)
Definition: object.h:598
range_magic
@ range_magic
Definition: player.h:32
SockList_AddShort
void SockList_AddShort(SockList *sl, uint16_t data)
Definition: lowlevel.cpp:113
transport_can_hold
int transport_can_hold(const object *transport, const object *op, int nrof)
Definition: apply.cpp:54
ROD
@ ROD
Definition: object.h:114
CONTAINER
@ CONTAINER
Definition: object.h:236
object::below
object * below
Definition: object.h:295
query_short_name
void query_short_name(const object *op, char *buf, size_t size)
Definition: item.cpp:517
SockList_AddChar
void SockList_AddChar(SockList *sl, unsigned char c)
Definition: lowlevel.cpp:103
object::face
const Face * face
Definition: object.h:341
out_of_map
int out_of_map(mapstruct *m, int x, int y)
Definition: map.cpp:2286
is_identified
int is_identified(const object *op)
Definition: item.cpp:1352
F_CURSED
#define F_CURSED
Definition: newclient.h:276
esrv_send_inventory
void esrv_send_inventory(object *pl, object *op)
Definition: item.cpp:316
FLAG_DAMNED
#define FLAG_DAMNED
Definition: define.h:317
navar-midane_time.data
data
Definition: navar-midane_time.py:11
UPD_FLAGS
#define UPD_FLAGS
Definition: newclient.h:304
spell
with a maximum of six This is not so if you are wearing plate you receive no benefit Armour is additive with all the supplementry forms of which means that it lasts until the next semi permanent spell effect is cast upon the character spell
Definition: tome-of-magic.txt:44
FOR_INV_FINISH
#define FOR_INV_FINISH()
Definition: define.h:677
F_UNIDENTIFIED
#define F_UNIDENTIFIED
Definition: newclient.h:273
esrv_get_ob_from_count
static object * esrv_get_ob_from_count(object *pl, tag_t count)
Definition: item.cpp:607
disinfect.count
int count
Definition: disinfect.py:7
tag_t
uint32_t tag_t
Definition: object.h:14
sproto.h
get_map_from_coord
mapstruct * get_map_from_coord(mapstruct *m, int16_t *x, int16_t *y)
Definition: map.cpp:2359
FOR_OB_AND_BELOW_PREPARE
#define FOR_OB_AND_BELOW_PREPARE(op_)
Definition: define.h:750
BOOK
@ BOOK
Definition: object.h:119
object::animation
const Animations * animation
Definition: object.h:426
RING
@ RING
Definition: object.h:190
SockList_Init
void SockList_Init(SockList *sl)
Definition: lowlevel.cpp:52
weight
TIPS on SURVIVING Crossfire is populated with a wealth of different monsters These monsters can have varying immunities and attack types In some of them can be quite a bit smarter than others It will be important for new players to learn the abilities of different monsters and learn just how much it will take to kill them This section discusses how monsters can interact with players Most monsters in the game are out to mindlessly kill and destroy the players These monsters will help boost a player s after he kills them When fighting a large amount of monsters in a single attempt to find a narrower hallway so that you are not being attacked from all sides Charging into a room full of Beholders for instance would not be open the door and fight them one at a time For there are several maps designed for them Find these areas and clear them out All throughout these a player can find signs and books which they can read by stepping onto them and hitting A to apply the book sign These messages will help the player to learn the system One more always keep an eye on your food If your food drops to your character will soon so BE CAREFUL ! NPCs Non Player Character are special monsters which have intelligence Players may be able to interact with these monsters to help solve puzzles and find items of interest To speak with a monster you suspect to be a simply move to an adjacent square to them and push the double ie Enter your and press< Return > You can also use say if you feel like typing a little extra Other NPCs may not speak to but display intelligence with their movement Some monsters can be and may attack the nearest of your enemies Others can be in that they follow you around and help you in your quest to kill enemies and find treasure SPECIAL ITEMS There are many special items which can be found in of these the most important may be the signs all a player must do is apply the handle In the case of the player must move items over the button to hold it down Some of the larger buttons may need very large items to be moved onto before they can be activated Gates and locked but be for you could fall down into a pit full of ghosts or dragons and not be able to get back out Break away sometimes it may be worth a player s time to test the walls of a map for secret doors Fire such as missile weapons and spells you will notice them going up in smoke ! So be careful not to destroy valuable items Spellbooks sometimes a player can learn the other times they cannot There are many different types of books and scrolls out there Improve item have lower weight
Definition: survival-guide.txt:100
F_NOPICK
#define F_NOPICK
Definition: newclient.h:279
player_can_find
bool player_can_find(object *op, object *ob)
Definition: item.cpp:587
find_skill_by_name
object * find_skill_by_name(object *who, const char *name)
Definition: skill_util.cpp:211
env
static std::shared_ptr< inja::Environment > env
Definition: mapper.cpp:2170
MAX_BUF
#define MAX_BUF
Definition: define.h:35
drop_object
object * drop_object(object *op, object *tmp, uint32_t nrof)
Definition: c_object.cpp:1073
strlcpy
size_t strlcpy(char *dst, const char *src, size_t size)
Definition: porting.cpp:222
SockList_Term
void SockList_Term(SockList *sl)
Definition: lowlevel.cpp:62
empty_face
const Face * empty_face
Definition: image.cpp:36
FLAG_CLIENT_SENT
#define FLAG_CLIENT_SENT
Definition: define.h:346
F_BLESSED
#define F_BLESSED
Definition: newclient.h:281
to
**Media tags please refer to the protocol file in doc Developers protocol Quick for your pleasure an example[/b][i] This is an old full of dirt and partially destroyed[hand] My dear as you two years i had to leave quickly Words have come to me of powerful magic scrolls discovered in an old temple by my uncle I have moved to study them I not forgot your knowledge in ancient languages I need your help for[print][b] Some parts of document are to damaged to be readable[/b][arcane] Arghis[color=Red] k h[color=dark slate blue] ark[color=#004000] fido[/color][hand] please come as fast as possible my friend[print][b] The bottom of letter seems deliberatly shredded What is but not limited to
Definition: media-tags.txt:30
UPD_ANIMSPEED
#define UPD_ANIMSPEED
Definition: newclient.h:309
MSG_TYPE_COMMAND_FAILURE
#define MSG_TYPE_COMMAND_FAILURE
Definition: newclient.h:520
FOR_MAP_PREPARE
#define FOR_MAP_PREPARE(map_, mx_, my_, it_)
Definition: define.h:723
FLAG_KNOWN_MAGICAL
#define FLAG_KNOWN_MAGICAL
Definition: define.h:319
FLAG_REMOVED
#define FLAG_REMOVED
Definition: define.h:232
FLAG_WIZ
#define FLAG_WIZ
Definition: define.h:231
esrv_send_animation
void esrv_send_animation(socket_struct *ns, const Animations *anim)
Definition: request.cpp:1021
NDI_UNIQUE
#define NDI_UNIQUE
Definition: newclient.h:251
object::name
sstring name
Definition: object.h:319
MSG_TYPE_SKILL_FAILURE
#define MSG_TYPE_SKILL_FAILURE
Definition: newclient.h:579
esrv_send_item
void esrv_send_item(object *pl, object *op)
Definition: item.cpp:522
esrv_del_item
void esrv_del_item(player *pl, object *ob)
Definition: item.cpp:562
object_can_pick
int object_can_pick(const object *who, const object *item)
Definition: object.cpp:3858
esrv_draw_look
void esrv_draw_look(object *pl)
Definition: item.cpp:193
newserver.h
mapstruct
Definition: map.h:314
object::env
object * env
Definition: object.h:301
sstring
const typedef char * sstring
Definition: sstring.h:2
give.op
op
Definition: give.py:33
ob_if_can_find
static object * ob_if_can_find(object *op, object *ob)
Definition: item.cpp:594
SockList_AddLen8Data
void SockList_AddLen8Data(SockList *sl, const void *data, size_t len)
Definition: lowlevel.cpp:176
apply_by_living
int apply_by_living(object *pl, object *op, int aflag, int quiet)
Definition: apply.cpp:637
diamondslots.y
y
Definition: diamondslots.py:16
look_at
void look_at(object *op, int dx, int dy)
Definition: item.cpp:795
a_wielded
@ a_wielded
Definition: newclient.h:270
CLEAR_FLAG
#define CLEAR_FLAG(xyz, p)
Definition: define.h:225
WEIGHT
#define WEIGHT(op)
Definition: define.h:651
socket_struct::anims_sent
uint8_t anims_sent[MAXANIMNUM]
Definition: newserver.h:97
socket_struct::faces_sent
uint8_t * faces_sent
Definition: newserver.h:96
apply_cmd
void apply_cmd(char *buf, int len, player *pl)
Definition: item.cpp:665
MAXITEMLEN
#define MAXITEMLEN
Definition: item.cpp:36
GetInt_String
int GetInt_String(const unsigned char *data)
Definition: lowlevel.cpp:251
make_face_from_files.int
int
Definition: make_face_from_files.py:32
F_MAGIC
#define F_MAGIC
Definition: newclient.h:275
FLAG_ANIMATE
#define FLAG_ANIMATE
Definition: define.h:242
UPD_NAME
#define UPD_NAME
Definition: newclient.h:307
newclient.h
look_at_cmd
void look_at_cmd(char *buf, int len, player *pl)
Definition: item.cpp:860
inscribe_scroll_cmd
void inscribe_scroll_cmd(char *buf, int len, player *pl)
Definition: item.cpp:955
F_LOCKED
#define F_LOCKED
Definition: newclient.h:280
inventory
void inventory(object *op, object *inv)
Definition: c_object.cpp:2165
positioning_system.marked
marked
Definition: positioning_system.py:21
FLAG_UNPAID
#define FLAG_UNPAID
Definition: define.h:236
UPD_ANIM
#define UPD_ANIM
Definition: newclient.h:308
object::nrof
uint32_t nrof
Definition: object.h:342
query_base_name
void query_base_name(const object *op, int plural, char *buf, size_t size)
Definition: item.cpp:693
Face::number
uint16_t number
Definition: face.h:15
query_flags
static unsigned int query_flags(const object *op)
Definition: item.cpp:49
BOOTS
@ BOOTS
Definition: object.h:217
replace.current
current
Definition: replace.py:64
SHIELD
@ SHIELD
Definition: object.h:140
a_worn
@ a_worn
Definition: newclient.h:270
FLAG_NO_SKILL_IDENT
#define FLAG_NO_SKILL_IDENT
Definition: define.h:335
FLAG_CURSED
#define FLAG_CURSED
Definition: define.h:316
pick_up
void pick_up(object *op, object *alt)
Definition: c_object.cpp:519
examine
void examine(object *op, object *tmp)
Definition: c_object.cpp:1983
sack_can_hold
int sack_can_hold(const object *pl, const object *sack, const object *op, uint32_t nrof)
Definition: c_object.cpp:317
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
CUSTOM_NAME_FIELD
#define CUSTOM_NAME_FIELD
Definition: object.h:98
FOR_INV_PREPARE
#define FOR_INV_PREPARE(op_, it_)
Definition: define.h:670
object.h
llevDebug
@ llevDebug
Definition: logger.h:13
is_valid_types_gen.type
list type
Definition: is_valid_types_gen.py:25
SockList_AddPrintf
void SockList_AddPrintf(SockList *sl, const char *format,...)
Definition: lowlevel.cpp:199
FLAG_IS_FLOOR
#define FLAG_IS_FLOOR
Definition: define.h:302
FLAG_IDENTIFIED
#define FLAG_IDENTIFIED
Definition: define.h:261
put_object_in_sack
void put_object_in_sack(object *op, object *sack, object *tmp, uint32_t nrof)
Definition: c_object.cpp:937