Crossfire Server, Trunk
player.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 
20 #include "global.h"
21 
22 #include <assert.h>
23 #include <ctype.h>
24 #include <math.h>
25 #include <stddef.h>
26 #include <stdlib.h>
27 #include <string.h>
28 
29 #ifndef WIN32 /* ---win32 remove headers */
30 #include <pwd.h>
31 #endif
32 
33 #include "server.h"
34 #include "living.h"
35 #include "object.h"
36 #include "shared/newclient.h"
37 #include "shop.h"
38 #include "skills.h"
39 #include "sounds.h"
40 #include "spells.h"
41 #include "sproto.h"
42 #include "assets.h"
43 #include "AssetsManager.h"
44 
46 
47 static void kill_player_not_permadeath(object *op);
48 static void kill_player_permadeath(object *op);
49 static int action_makes_visible(object *op);
50 
59 player *find_player(const char *plname) {
60  return find_player_options(plname, 0, NULL);
61 }
62 
70 player *find_player_options(const char *plname, int options, const mapstruct *map) {
71  player *pl;
72  player *found = NULL;
73  size_t namelen = strlen(plname);
74  char name[MAX_BUF];
75 
76  for (pl = first_player; pl != NULL; pl = pl->next) {
77  if ((options & FIND_PLAYER_NO_HIDDEN_DM) && (QUERY_FLAG(pl->ob, FLAG_WIZ) && pl->ob->contr->hidden))
78  continue;
79 
80  if (map != NULL && pl->ob->map != map)
81  continue;
82 
84  query_name(pl->ob, name, sizeof(name));
85  if (!strcmp(name, plname))
86  return pl;
87  continue;
88  }
89 
90  if (strlen(pl->ob->name) < namelen)
91  continue;
92 
93  if (!strcmp(pl->ob->name, plname))
94  return pl;
95 
96  if (!strncasecmp(pl->ob->name, plname, namelen)) {
97  if (found)
98  return NULL;
99 
100  found = pl;
101  }
102  }
103  return found;
104 }
105 
114 player *find_player_partial_name(const char *plname) {
115  return find_player_options(plname, FIND_PLAYER_PARTIAL_NAME, NULL);
116 }
117 
124  player *pl;
125 
126  for (pl = first_player; pl != NULL; pl = pl->next) {
127  if (pl->socket == ns)
128  return pl;
129  }
130  return NULL;
131 }
132 
139 void display_motd(const object *op) {
140  char buf[MAX_BUF];
141  char motd[HUGE_BUF];
142  FILE *fp;
143  size_t size;
144 
145  snprintf(buf, sizeof(buf), "%s/%s", settings.confdir, settings.motd);
146  fp = fopen(buf, "r");
147  if (fp == NULL) {
148  return;
149  }
150  motd[0] = '\0';
151  size = 0;
152 
153  while (fgets(buf, MAX_BUF, fp) != NULL) {
154  if (*buf != '#') {
155  safe_strcat(motd, buf, &size, sizeof(motd));
156  }
157  }
158 
160  motd);
161  fclose(fp);
162 }
163 
170 void send_rules(const object *op) {
171  char buf[MAX_BUF];
172  char rules[HUGE_BUF];
173  FILE *fp;
174  size_t size;
175 
176  snprintf(buf, sizeof(buf), "%s/%s", settings.confdir, settings.rules);
177  fp = fopen(buf, "r");
178  if (fp == NULL) {
179  return;
180  }
181  rules[0] = '\0';
182  size = 0;
183 
184  while (fgets(buf, MAX_BUF, fp) != NULL) {
185  if (size+strlen(buf) >= HUGE_BUF) {
186  LOG(llevDebug, "Warning, rules size is > %d bytes.\n", HUGE_BUF);
187  break;
188  }
189 
190  if (*buf != '#') {
191  safe_strcat(rules, buf, &size, sizeof(rules));
192  }
193  }
194 
196  MSG_TYPE_ADMIN_RULES, rules);
197  fclose(fp);
198 }
199 
206 void send_news(const object *op) {
207  char buf[MAX_BUF];
208  char news[HUGE_BUF];
209  char subject[MAX_BUF];
210  FILE *fp;
211  size_t size;
212 
213  snprintf(buf, sizeof(buf), "%s/%s", settings.confdir, settings.news);
214  fp = fopen(buf, "r");
215  if (fp == NULL)
216  return;
217  news[0] = '\0';
218  subject[0] = '\0';
219  size = 0;
220  while (fgets(buf, MAX_BUF, fp) != NULL) {
221  if (*buf == '#')
222  continue;
223  if (*buf == '%') { /* send one news */
224  if (size > 0)
227  "%s:\n%s",
228  subject, news); /*send previously read news*/
229  safe_strncpy(subject, buf + 1, sizeof(subject));
230  strip_endline(subject);
231  size = 0;
232  news[0] = '\0';
233  } else {
234  if (size+strlen(buf) >= HUGE_BUF) {
235  LOG(llevDebug, "Warning, one news item has size > %d bytes.\n", HUGE_BUF);
236  break;
237  }
238  safe_strcat(news, buf, &size, sizeof(news));
239  }
240  }
241 
244  "%s:\n%s",
245  subject, news);
246  fclose(fp);
247 }
248 
257 int playername_ok(const char *cp) {
258  /* Don't allow - or _ as first character in the name */
259  if (*cp == '-' || *cp == '_')
260  return 0;
261 
262  for (; *cp != '\0'; cp++)
263  if (!isalnum(*cp)
264  && *cp != '-'
265  && *cp != '_')
266  return 0;
267  return 1;
268 }
269 
286  object *op = arch_to_object(get_player_archetype(NULL));
287  int i;
288 
289  if (!p) {
290  player *tmp;
291 
292  p = (player *)calloc(1, sizeof(player));
293  if (p == NULL)
295 
296  /* This adds the player in the linked list. There is extra
297  * complexity here because we want to add the new player at the
298  * end of the list - there is in fact no compelling reason that
299  * that needs to be done except for things like output of
300  * 'who'.
301  */
302  tmp = first_player;
303  while (tmp != NULL && tmp->next != NULL)
304  tmp = tmp->next;
305  if (tmp != NULL)
306  tmp->next = p;
307  else
308  first_player = p;
309  } else {
310  /* Only needed when reusing existing player. */
311  clear_player(p);
312  }
313 
314  /* Clears basically the entire player structure except
315  * for next and socket.
316  */
317  memset((void *)((char *)p+offsetof(player, maplevel)), 0, sizeof(player)-offsetof(player, maplevel));
318 
319  /* There are some elements we want initialized to non zero value -
320  * we deal with that below this point.
321  */
322  p->party = NULL;
325  p->swap_first = -1;
326 
327 #ifdef AUTOSAVE
328  p->last_save_tick = 9999999;
329 #endif
330 
331  strcpy(p->savebed_map, first_map_path); /* Init. respawn position */
332 
333  op->contr = p; /* this aren't yet in archetype */
334  p->ob = op;
335  op->speed_left = 0.5;
336  op->speed = 1.0;
337  op->direction = 5; /* So player faces south */
338  op->stats.wc = 2;
339  op->run_away = 25; /* Then we panic... */
340 
341  roll_stats(op);
343  clear_los(p);
344 
345  p->gen_sp_armour = 10;
346  p->last_speed = -1;
347  p->shoottype = range_none;
348  p->bowtype = bow_normal;
349  p->petmode = pet_normal;
350  p->listening = 10;
351  p->last_weapon_sp = -1;
352  p->peaceful = 1; /* default peaceful */
353  p->do_los = 1;
354  p->no_shout = 0; /* default can shout */
355  p->language = i18n_get_language_by_code(""); // find default language
356  p->unarmed_skill = NULL;
357  p->ticks_played = 0;
358 
359  strncpy(p->title, op->arch->clone.name, sizeof(p->title)-1);
360  p->title[sizeof(p->title)-1] = '\0';
361  op->race = add_string(op->arch->clone.race);
362 
364 
365  /* we need to clear these to -1 and not zero - otherwise,
366  * if a player quits and starts a new character, we wont
367  * send new values to the client, as things like exp start
368  * at zero.
369  */
370  for (i = 0; i < MAX_SKILLS; i++) {
371  p->last_skill_exp[i] = -1;
372  p->last_skill_ob[i] = NULL;
373  }
374  for (i = 0; i < NROFATTACKS; i++) {
375  p->last_resist[i] = -1;
376  }
377  p->last_stats.exp = -1;
378  p->last_weight = (uint32_t)-1;
379  /* Set stats to invalid values so they are sent at first tick */
383  = p->last_applied_stats.Cha = -1;
384  p->last_character_flags = 0xFFFF;
385  return p;
386 }
387 
394 void set_first_map(object *op) {
395  strcpy(op->contr->maplevel, first_map_path);
396  op->x = -1;
397  op->y = -1;
399 }
400 
414  if (p->socket && p->socket->account_chars) {
416  }
417 
418  p->socket = static_cast<socket_struct *>(malloc(sizeof(socket_struct)));
419  if (!p->socket) {
421  }
422  memcpy(p->socket, ns, sizeof(socket_struct));
423 
424  /* The memcpy above copies the reference to faces sent. So we need to clear
425  * that pointer in ns, otherwise we get a double free.
426  */
427  ns->faces_sent = NULL;
428  ns->host = strdup_local("");
429  ns->account_name = strdup_local("");
430  ns->account_chars = NULL; // If not NULL, the reference is now kept by p
431 
432  if (p->socket->faces_sent == NULL)
434 
435  /* Needed because the socket we just copied over needs to be cleared.
436  * Note that this can result in a client reset if there is partial data
437  * on the incoming socket.
438  */
440 
441 
442 }
443 
461  player *p;
462 
463  p = get_player(NULL);
464  set_player_socket(p, ns);
465  ns->status = Ns_Avail;
466 
468 
469  if (!(flags & ADD_PLAYER_NO_MAP))
470  set_first_map(p->ob);
471 
473 
474  /* In this case, the client is provide all the informatin for the
475  * new character, so just return it. Do not display any messages,
476  * etc
477  */
479  return p;
480 
481  if (flags & ADD_PLAYER_NEW) {
482  roll_again(p->ob);
484  } else {
485  send_rules(p->ob);
486  send_news(p->ob);
487  display_motd(p->ob);
488  get_name(p->ob);
489  }
490  return p;
491 }
492 
493 std::vector<archetype *> players;
494 
506  if (players.empty()) {
507  getManager()->archetypes()->each([] (const auto &at) {
508  if (at->clone.type == PLAYER) {
509  players.push_back(at);
510  }
511  });
512  if (players.empty()) {
513  LOG(llevError, "No Player archetypes\n");
515  }
516  }
517 
518  if (!at) {
519  return players.front();
520  }
521  auto pos = std::find(players.cbegin(), players.cend(), at);
522  if (pos == players.cend()) {
523  return nullptr;
524  }
525  ++pos;
526  if (pos == players.cend()) {
527  return players.front();
528  }
529  return *pos;
530 }
531 
540 object *get_nearest_player(object *mon) {
541  object *op = NULL;
542  player *pl = NULL;
543  objectlink *list, *ol;
544  unsigned lastdist;
545  rv_vector rv;
546 
547  list = get_friends_of(NULL);
548 
549  for (ol = list, lastdist = 1000; ol != NULL; ol = ol->next) {
550  if (QUERY_FLAG(ol->ob, FLAG_FREED) || !QUERY_FLAG(ol->ob, FLAG_FRIENDLY)) {
551  continue;
552  }
553 
554  /* Remove special check for player from this. First, it looks to cause
555  * some crashes (ol->ob->contr not set properly?), but secondly, a more
556  * complicated method of state checking would be needed in any case -
557  * as it was, a clever player could type quit, and the function would
558  * skip them over while waiting for confirmation. Remove
559  * on_same_map check, as monster_can_detect_enemy() also does this
560  */
561  if (!monster_can_detect_enemy(mon, ol->ob, &rv))
562  continue;
563 
564  if (lastdist > rv.distance) {
565  op = ol->ob;
566  lastdist = rv.distance;
567  }
568  }
569  if (list) {
571  }
572  for (pl = first_player; pl != NULL; pl = pl->next) {
573  if (monster_can_detect_enemy(mon, pl->ob, &rv)) {
574  if (lastdist > rv.distance) {
575  op = pl->ob;
576  lastdist = rv.distance;
577  }
578  }
579  }
580  return op;
581 }
582 
583 object *get_nearest_criminal(object *mon) {
584  object *op = NULL;
585  for (player *pl = first_player; pl != NULL; pl = pl->next) {
586  rv_vector rv;
587  if (monster_can_detect_enemy(mon, pl->ob, &rv) && is_criminal(pl->ob)) {
588  op = pl->ob;
589  }
590  }
591  return op;
592 }
593 
603 #define DETOUR_AMOUNT 2
604 
618 #define MAX_SPACES 50
619 
651 int path_to_player(object *mon, object *pl, unsigned mindiff) {
652  rv_vector rv;
653  int16_t x, y;
654  int lastx, lasty, dir, i, diff, firstdir = 0, lastdir, max = MAX_SPACES, mflags, blocked;
655  mapstruct *m, *lastmap;
656 
657  if (!get_rangevector(mon, pl, &rv, 0))
658  return 0;
659 
660  if (rv.distance < mindiff)
661  return 0;
662 
663  x = mon->x;
664  y = mon->y;
665  m = mon->map;
666  dir = rv.direction;
667  lastdir = firstdir = rv.direction; /* perhaps we stand next to pl, init firstdir too */
668  diff = MAX(FABS(rv.distance_x), FABS(rv.distance_y));
669  /* If we can't solve it within the search distance, return now. */
670  if (diff > max)
671  return 0;
672  while (diff > 1 && max > 0) {
673  lastx = x;
674  lasty = y;
675  lastmap = m;
676  x = lastx+freearr_x[dir];
677  y = lasty+freearr_y[dir];
678 
679  mflags = get_map_flags(m, &m, x, y, &x, &y);
680  blocked = (mflags&P_OUT_OF_MAP) ? MOVE_ALL : GET_MAP_MOVE_BLOCK(m, x, y);
681 
682  /* Space is blocked - try changing direction a little */
683  if ((mflags&P_OUT_OF_MAP)
684  || ((OB_TYPE_MOVE_BLOCK(mon, blocked) || (mflags&P_IS_ALIVE))
685  && (m == mon->map && blocked_link(mon, m, x, y)))) {
686  /* recalculate direction from last good location. Possible
687  * we were not traversing ideal location before.
688  */
689  if (get_rangevector_from_mapcoord(lastmap, lastx, lasty, pl, &rv) && rv.direction != dir) {
690  /* OK - says direction should be different - lets reset the
691  * the values so it will try again.
692  */
693  x = lastx;
694  y = lasty;
695  m = lastmap;
696  dir = firstdir = rv.direction;
697  } else {
698  /* direct path is blocked - try taking a side step to
699  * either the left or right.
700  * Note increase the values in the loop below to be
701  * more than -1/1 respectively will mean the monster takes
702  * bigger detour. Have to be careful about these values getting
703  * too big (3 or maybe 4 or higher) as the monster may just try
704  * stepping back and forth
705  */
706  for (i = -DETOUR_AMOUNT; i <= DETOUR_AMOUNT; i++) {
707  if (i == 0)
708  continue; /* already did this, so skip it */
709  /* Use lastdir here - otherwise,
710  * since the direction that the creature should move in
711  * may change, you could get infinite loops.
712  * ie, player is northwest, but monster can only
713  * move west, so it does that. It goes some distance,
714  * gets blocked, finds that it should move north,
715  * can't do that, but now finds it can move east, and
716  * gets back to its original point. lastdir contains
717  * the last direction the creature has successfully
718  * moved.
719  */
720 
721  x = lastx+freearr_x[absdir(lastdir+i)];
722  y = lasty+freearr_y[absdir(lastdir+i)];
723  m = lastmap;
724  mflags = get_map_flags(m, &m, x, y, &x, &y);
725  if (mflags&P_OUT_OF_MAP)
726  continue;
727  blocked = GET_MAP_MOVE_BLOCK(m, x, y);
728  if (OB_TYPE_MOVE_BLOCK(mon, blocked))
729  continue;
730  if (mflags&P_IS_ALIVE)
731  continue;
732 
733  if (m == mon->map && blocked_link(mon, m, x, y))
734  break;
735  }
736  /* go through entire loop without finding a valid
737  * sidestep to take - thus, no valid path.
738  */
739  if (i == (DETOUR_AMOUNT+1))
740  return 0;
741  diff--;
742  lastdir = dir;
743  max--;
744  if (!firstdir)
745  firstdir = dir+i;
746  } /* else check alternate directions */
747  } /* if blocked */
748  else {
749  /* we moved towards creature, so diff is less */
750  diff--;
751  max--;
752  lastdir = dir;
753  if (!firstdir)
754  firstdir = dir;
755  }
756  if (diff <= 1) {
757  /* Recalculate diff (distance) because we may not have actually
758  * headed toward player for entire distance.
759  */
760  if (!get_rangevector_from_mapcoord(m, x, y, pl, &rv))
761  return 0;
762  diff = MAX(FABS(rv.distance_x), FABS(rv.distance_y));
763  }
764  if (diff > max)
765  return 0;
766  }
767  /* If we reached the max, didn't find a direction in time */
768  if (!max)
769  return 0;
770 
771  return firstdir;
772 }
773 
784 void give_initial_items(object *pl, treasurelist *items) {
785  if (pl->randomitems != NULL)
787 
788  FOR_INV_PREPARE(pl, op) {
789  /* Forces get applied per default, unless they have the
790  * flag "neutral" set. Sorry but I can't think of a better way
791  */
792  if (op->type == FORCE && !QUERY_FLAG(op, FLAG_NEUTRAL))
794 
795  /* we never give weapons/armour if these cannot be used
796  * by this player due to race restrictions
797  */
798  if (pl->type == PLAYER) {
801  || (!QUERY_FLAG(pl, FLAG_USE_SHIELD) && IS_SHIELD(op))) {
802  object_remove(op);
804  continue;
805  }
806  }
807 
808  /* This really needs to be better - we should really give
809  * a substitute spellbook. The problem is that we don't really
810  * have a good idea what to replace it with (need something like
811  * a first level treasurelist for each skill.)
812  * remove duplicate skills also
813  */
814  if (op->type == SPELLBOOK || op->type == SKILL) {
815  int found;
816 
817  found = 0;
818  // Make sure we set flags that are checked by object_can_merge
819  // *before* we run object_can_merge. Otherwise, we can get two
820  // entries of the same skill.
821  if (op->type == SKILL) {
823  op->stats.exp = 0;
824  op->level = 1;
825  // Since this also happens to the invisible skills, we need this so that the flags match.
827  }
829  if (object_can_merge(op, tmp)) {
830  found = 1;
831  break;
832  }
834  if (found) {
835  LOG(llevError, "give_initial_items: Removing duplicate object %s\n", op->name);
836  object_remove(op);
838  continue;
839  }
840  if (op->nrof > 1)
841  op->nrof = 1;
842  }
843 
844  if (op->type == SPELLBOOK && op->inv) {
846  }
847 
848  /* Give starting characters identified, uncursed, and undamned
849  * items. Just don't identify gold or silver, or it won't be
850  * merged properly.
851  */
852  if (is_identifiable_type(op)) {
856  }
857  /* lock all 'normal items by default */
858  else
860  } FOR_INV_FINISH(); /* for loop of objects in player inv */
861 
862  /* Need to set up the skill pointers */
864 
872  FOR_INV_FINISH();
873 }
874 
881 void get_name(object *op) {
883  send_query(op->contr->socket, 0, i18n(op, "What is your name?\n:"));
884 }
885 
892 void get_password(object *op) {
894  send_query(op->contr->socket, CS_QUERY_HIDEINPUT, i18n(op, "What is your password?\n:"));
895 }
896 
903 void play_again(object *op) {
904  SockList sl;
905 
906  op->contr->socket->status = Ns_Add;
908  op->chosen_skill = NULL;
909 
910  /*
911  * For old clients, ask if they want to play again.
912  * For clients with account support, just return to character seletion (see below).
913  */
914  if (op->contr->socket->login_method == 0) {
915  send_query(op->contr->socket, CS_QUERY_SINGLECHAR, i18n(op, "Do you want to play again (a/q)?"));
916  }
917  /* a bit of a hack, but there are various places early in th
918  * player creation process that a user can quit (eg, roll
919  * stats) that isn't removing the player. Taking a quick
920  * look, there are many places that call play_again without
921  * removing the player - it probably makes more sense
922  * to leave it to play_again to remove the object in all
923  * cases.
924  */
925  if (!QUERY_FLAG(op, FLAG_REMOVED))
926  object_remove(op);
927  /* Need to set this to null - otherwise, it could point to garbage,
928  * and draw() doesn't check to see if the player is removed, only if
929  * the map is null or not swapped out.
930  */
931  op->map = NULL;
932 
933  SockList_Init(&sl);
934  SockList_AddString(&sl, "player ");
935  SockList_AddInt(&sl, 0);
936  SockList_AddInt(&sl, 0);
937  SockList_AddInt(&sl, 0);
938  SockList_AddChar(&sl, 0);
939 
940  Send_With_Handling(op->contr->socket, &sl);
941  SockList_Term(&sl);
942 
943  if (op->contr->socket->login_method > 0) {
944  receive_play_again(op, 'a');
945  }
946 }
947 
956 void receive_play_again(object *op, char key) {
957  if (key == 'q' || key == 'Q') {
959  leave(op->contr, 0); /* ericserver will draw the message */
960  return;
961  } else if (key == 'a' || key == 'A') {
962  player *pl = op->contr;
963  const char *name = op->name;
964 
968  pl = get_player(pl);
969  op = pl->ob;
971  op->contr->password[0] = '~';
972  FREE_AND_CLEAR_STR(op->name);
973  FREE_AND_CLEAR_STR(op->name_pl);
974  if (pl->socket->login_method >= 1 && pl->socket->account_name != NULL) {
975  /* If we are using new login, we send the
976  * list of characters to the client - this should
977  * result in the client popping up this list so
978  * the player can choose which one to play - better
979  * than going to legacy login code.
980  * If the account_name is NULL, it means the client
981  * says it uses account but started playing without logging in.
982  */
983  send_account_players(pl->socket);
985  } else {
986  /* Lets put a space in here */
988  "\n");
989  get_name(op);
990  set_first_map(op);
991  }
992  op->name = name; /* Already added a refcount above */
993  op->name_pl = add_string(name);
994  } else {
995  /* user pressed something else so just ask again... */
996  play_again(op);
997  }
998 }
999 
1006 void confirm_password(object *op) {
1008  send_query(op->contr->socket, CS_QUERY_HIDEINPUT, i18n(op, "Please type your password again.\n:"));
1009 }
1010 
1021 int get_party_password(object *op, partylist *party) {
1022  if (*party_get_password(party) == '\0') {
1023  return 0;
1024  }
1025 
1027  op->contr->party_to_join = party;
1028  send_query(op->contr->socket, CS_QUERY_HIDEINPUT, i18n(op, "What is the password?\n:"));
1029  return 1;
1030 }
1031 
1038 int roll_stat(void) {
1039  int roll[4], i, low_index, k;
1040 
1041  for (i = 0; i < 4; ++i)
1042  roll[i] = (int)RANDOM()%6+1;
1043 
1044  for (i = 0, low_index = 0, k = 7; i < 4; ++i)
1045  if (roll[i] < k)
1046  k = roll[i],
1047  low_index = i;
1048 
1049  for (i = 0, k = 0; i < 4; ++i) {
1050  if (i != low_index)
1051  k += roll[i];
1052  }
1053  return k;
1054 }
1055 
1062 void roll_stats(object *op) {
1063  int i = 0, j = 0;
1064  int statsort[7];
1065 
1066  op->stats.Str = roll_stat();
1067  op->stats.Dex = roll_stat();
1068  op->stats.Int = roll_stat();
1069  op->stats.Con = roll_stat();
1070  op->stats.Wis = roll_stat();
1071  op->stats.Pow = roll_stat();
1072  op->stats.Cha = roll_stat();
1073  int sum = op->stats.Str+op->stats.Dex+op->stats.Int+op->stats.Con+op->stats.Wis+op->stats.Pow+op->stats.Cha;
1074  float scale = settings.roll_stat_points / sum;
1075  op->stats.Str = roundf(scale * op->stats.Str);
1076  op->stats.Dex = roundf(scale * op->stats.Dex);
1077  op->stats.Int = roundf(scale * op->stats.Int);
1078  op->stats.Con = roundf(scale * op->stats.Con);
1079  op->stats.Wis = roundf(scale * op->stats.Wis);
1080  op->stats.Pow = roundf(scale * op->stats.Pow);
1081  op->stats.Cha = roundf(scale * op->stats.Cha);
1082 
1083  /* Sort the stats so that rerolling is easier... */
1084  statsort[0] = op->stats.Str;
1085  statsort[1] = op->stats.Dex;
1086  statsort[2] = op->stats.Int;
1087  statsort[3] = op->stats.Con;
1088  statsort[4] = op->stats.Wis;
1089  statsort[5] = op->stats.Pow;
1090  statsort[6] = op->stats.Cha;
1091 
1092  /* a quick and dirty bubblesort? */
1093  do {
1094  if (statsort[i] < statsort[i+1]) {
1095  j = statsort[i];
1096  statsort[i] = statsort[i+1];
1097  statsort[i+1] = j;
1098  i = 0;
1099  } else {
1100  i++;
1101  }
1102  } while (i < 6);
1103 
1104  op->stats.Str = statsort[0];
1105  op->stats.Dex = statsort[1];
1106  op->stats.Con = statsort[2];
1107  op->stats.Int = statsort[3];
1108  op->stats.Wis = statsort[4];
1109  op->stats.Pow = statsort[5];
1110  op->stats.Cha = statsort[6];
1111 
1112  op->contr->orig_stats.Str = op->stats.Str;
1113  op->contr->orig_stats.Dex = op->stats.Dex;
1114  op->contr->orig_stats.Int = op->stats.Int;
1115  op->contr->orig_stats.Con = op->stats.Con;
1116  op->contr->orig_stats.Wis = op->stats.Wis;
1117  op->contr->orig_stats.Pow = op->stats.Pow;
1118  op->contr->orig_stats.Cha = op->stats.Cha;
1119 
1120  op->level = 1;
1121  op->stats.exp = 0;
1122  op->stats.ac = 0;
1123 
1124  op->contr->levhp[1] = 9;
1125  op->contr->levsp[1] = 6;
1126  op->contr->levgrace[1] = 3;
1127 
1128  fix_object(op);
1129  op->stats.hp = op->stats.maxhp;
1130  op->stats.sp = op->stats.maxsp;
1131  op->stats.grace = op->stats.maxgrace;
1132  op->contr->orig_stats = op->stats;
1133 }
1134 
1141 void roll_again(object *op) {
1142  esrv_new_player(op->contr, 0);
1143  send_query(op->contr->socket, CS_QUERY_SINGLECHAR, i18n(op, "<y> to roll new stats <n> to use stats\n<1-7> <1-7> to swap stats.\nRoll again (y/n/1-7)? "));
1144 }
1145 
1155 static void swap_stat(object *op, int swap_second) {
1156  signed char tmp;
1157 
1158  if (op->contr->swap_first == -1) {
1159  LOG(llevError, "player.c:swap_stat() - swap_first is -1\n");
1160  return;
1161  }
1162 
1163  tmp = get_attr_value(&op->contr->orig_stats, op->contr->swap_first);
1164 
1165  set_attr_value(&op->contr->orig_stats, op->contr->swap_first, get_attr_value(&op->contr->orig_stats, swap_second));
1166 
1167  set_attr_value(&op->contr->orig_stats, swap_second, tmp);
1168 
1170  "%s done\n",
1171  short_stat_name[swap_second]);
1172 
1173  op->stats.Str = op->contr->orig_stats.Str;
1174  op->stats.Dex = op->contr->orig_stats.Dex;
1175  op->stats.Con = op->contr->orig_stats.Con;
1176  op->stats.Int = op->contr->orig_stats.Int;
1177  op->stats.Wis = op->contr->orig_stats.Wis;
1178  op->stats.Pow = op->contr->orig_stats.Pow;
1179  op->stats.Cha = op->contr->orig_stats.Cha;
1180  op->stats.ac = 0;
1181 
1182  op->level = 1;
1183  op->stats.exp = 0;
1184  op->stats.ac = 0;
1185 
1186  op->contr->levhp[1] = 9;
1187  op->contr->levsp[1] = 6;
1188  op->contr->levgrace[1] = 3;
1189 
1190  fix_object(op);
1191  op->stats.hp = op->stats.maxhp;
1192  op->stats.sp = op->stats.maxsp;
1193  op->stats.grace = op->stats.maxgrace;
1194  op->contr->orig_stats = op->stats;
1195  op->contr->swap_first = -1;
1196 }
1197 
1213 void key_roll_stat(object *op, char key) {
1214  int keynum = key-'0';
1215  static const int8_t stat_trans[] = {
1216  -1,
1217  STRENGTH,
1218  DEXTERITY,
1219  CONSTITUTION,
1220  INTELLIGENCE,
1221  WISDOM,
1222  POWER,
1223  CHARISMA,
1224  };
1225 
1226  if (keynum > 0 && keynum <= 7) {
1227  if (op->contr->swap_first == -1) {
1228  op->contr->swap_first = stat_trans[keynum];
1230  "%s ->",
1231  short_stat_name[stat_trans[keynum]]);
1232  } else
1233  swap_stat(op, stat_trans[keynum]);
1234 
1235  send_query(op->contr->socket, CS_QUERY_SINGLECHAR, "");
1236  return;
1237  }
1238  switch (key) {
1239  case 'n':
1240  case 'N': {
1241  SET_FLAG(op, FLAG_WIZ);
1242  if (op->map == NULL) {
1243  LOG(llevError, "Map == NULL in state 2\n");
1244  break;
1245  }
1246 
1247  SET_ANIMATION(op, 2); /* So player faces south */
1248  /* Enter exit adds a player otherwise */
1249  add_statbonus(op);
1250  send_query(op->contr->socket, CS_QUERY_SINGLECHAR, i18n(op, "Now choose a character.\nPress any key to change outlook.\nPress `d' when you're pleased.\n"));
1252  if (op->msg)
1255  op->msg);
1256  return;
1257  }
1258  case 'y':
1259  case 'Y':
1260  roll_stats(op);
1261  send_query(op->contr->socket, CS_QUERY_SINGLECHAR, "");
1262  return;
1263 
1264  case 'q':
1265  case 'Q':
1266  play_again(op);
1267  return;
1268 
1269  default:
1270  send_query(op->contr->socket, CS_QUERY_SINGLECHAR, i18n(op, "Yes, No, Quit or 1-6. Roll again?"));
1271  return;
1272  }
1273  return;
1274 }
1275 
1289 void key_change_class(object *op, char key) {
1290  int tmp_loop;
1291 
1292  if (key == 'q' || key == 'Q') {
1293  object_remove(op);
1294  play_again(op);
1295  return;
1296  }
1297  if (key == 'd' || key == 'D') {
1298  char buf[MAX_BUF];
1299 
1300  /* this must before then initial items are given */
1301  esrv_new_player(op->contr, op->weight+op->carrying);
1302  create_treasure(find_treasurelist("starting_wealth"), op, 0, 0, 0);
1303 
1304  /* Here we handle the BORN global event */
1306 
1307  /* We then generate a LOGIN event */
1308  events_execute_global_event(EVENT_LOGIN, op->contr, op->contr->socket->host);
1309  player_set_state(op->contr, ST_PLAYING);
1310 
1311  object_set_msg(op, NULL);
1312 
1313  /* We create this now because some of the unique maps will need it
1314  * to save here.
1315  */
1316  snprintf(buf, sizeof(buf), "%s/%s/%s", settings.localdir, settings.playerdir, op->name);
1318 
1319 #ifdef AUTOSAVE
1320  op->contr->last_save_tick = pticks;
1321 #endif
1324  "Welcome to Crossfire!\n Press `?' for help\n");
1325 
1328  "%s entered the game.", op->name);
1329 
1331  give_initial_items(op, op->randomitems);
1334  fix_object(op);
1335 
1336  /* This moves the player to a different start map, if there
1337  * is one for this race
1338  */
1339  if (*first_map_ext_path) {
1340  object *tmp;
1341  char mapname[MAX_BUF];
1342  mapstruct *oldmap;
1343 
1344  oldmap = op->map;
1345 
1346  snprintf(mapname, MAX_BUF-1, "%s/%s", first_map_ext_path, op->arch->name);
1347  /*printf("%s\n", mapname);*/
1348  tmp = object_new();
1350  EXIT_X(tmp) = op->x;
1351  EXIT_Y(tmp) = op->y;
1352  enter_exit(op, tmp);
1353 
1354  if (oldmap != op->map) {
1355  /* map exists, update bed of reality location, in case player dies */
1356  op->contr->bed_x = op->x;
1357  op->contr->bed_y = op->y;
1358  strlcpy(op->contr->savebed_map, mapname, sizeof(op->contr->savebed_map));
1359  }
1360 
1362  } else {
1363  LOG(llevDebug, "first_map_ext_path not set\n");
1364  }
1365  return;
1366  }
1367 
1368  /* Following actually changes the race - this is the default command
1369  * if we don't match with one of the options above.
1370  */
1371 
1372  tmp_loop = 0;
1373  while (!tmp_loop) {
1374  const char *name = add_string(op->name);
1375  int x = op->x, y = op->y;
1376 
1378  object_remove(op);
1379  /* get_player_archetype() is really misnamed - it will
1380  * get the next archetype from the list.
1381  */
1382  op->arch = get_player_archetype(op->arch);
1383  object_copy(&op->arch->clone, op);
1384  op->stats = op->contr->orig_stats;
1385  free_string(op->name);
1386  op->name = name;
1387  free_string(op->name_pl);
1388  op->name_pl = add_string(name);
1389  SET_ANIMATION(op, 2); /* So player faces south */
1390  object_insert_in_map_at(op, op->map, op, 0, x, y);
1391  strncpy(op->contr->title, op->arch->clone.name, sizeof(op->contr->title)-1);
1392  op->contr->title[sizeof(op->contr->title)-1] = '\0';
1393  add_statbonus(op);
1394  tmp_loop = allowed_class(op);
1395  }
1398  fix_object(op);
1399  op->stats.hp = op->stats.maxhp;
1400  op->stats.sp = op->stats.maxsp;
1401  op->stats.grace = 0;
1402  if (op->msg)
1404  op->msg);
1405  send_query(op->contr->socket, CS_QUERY_SINGLECHAR, i18n(op, "Press any key for the next race.\nPress `d' to play this race.\n"));
1406 }
1407 
1430 {
1431  int i, stat, failure=0;
1432 
1433  for (i = 0; i < NUM_STATS; i++) {
1434  stat = get_attr_value(stats, i);
1435  if (race)
1436  stat += get_attr_value(&race->clone.stats, i);
1437 
1438  if (opclass)
1439  stat += get_attr_value(&opclass->clone.stats, i);
1440 
1441  set_attr_value(stats, i, stat);
1442 
1443  /* We process all stats, regardless if there is a failure
1444  * or not.
1445  */
1446  if (stat < MIN_STAT) failure=1;
1447 
1448  /* Maybe this should be an error? Player is losing
1449  * some stats points here, but it is legal.
1450  */
1451  if (stat > settings.max_stat) stat = settings.max_stat;
1452  }
1453  return failure;
1454 
1455 }
1456 
1479 int apply_race_and_class(object *op, archetype *race, archetype *opclass, living *stats)
1480 {
1481  const char *name = add_string(op->name);
1482  char buf[MAX_BUF];
1483  object *inv;
1484 
1485  /* Free any objects in character inventory - they
1486  * shouldn't have any, but there is the potential that
1487  * we give them objects below and then get a creation
1488  * failure (stat out of range), in which case
1489  * those objects would be in the inventory.
1490  */
1491  while (op->inv) {
1492  inv = op->inv;
1493  object_remove(inv);
1494  object_free(inv, 0);
1495  }
1496 
1497  object_copy(&race->clone, op);
1498  free_string(op->name);
1499  op->name = name;
1500  free_string(op->name_pl);
1501  op->name_pl = add_string(name);
1502  SET_ANIMATION(op, 2); /* So player faces south */
1503  strlcpy(op->contr->title, op->arch->clone.name, sizeof(op->contr->title));
1504 
1505  if (stats) {
1506  /* Copy over the stats. Use this instead a memcpy because
1507  * we only want to copy over a few specific stats, and
1508  * leave things like maxhp, maxsp, etc, unchanged.
1509  */
1510  int i, stat;
1511  for (i = 0; i < NUM_STATS; i++) {
1512  stat = get_attr_value(stats, i);
1513  set_attr_value(&op->stats, i, stat);
1514  set_attr_value(&op->contr->orig_stats, i, stat);
1515  }
1516  } else {
1517  /* Note that this will repeated increase the stat values
1518  * if the caller does not reset them. Only do this
1519  * if stats is not provided - if stats is provided, those
1520  * are already adjusted.
1521  */
1522  add_statbonus(op);
1523 
1524  /* Checks that all stats are greater than 1. Once again,
1525  * only do this if stats are not provided
1526  */
1527  if (!allowed_class(op)) return 1;
1528  }
1529 
1531  op->stats.hp = op->stats.maxhp;
1532  op->stats.sp = op->stats.maxsp;
1533  op->stats.grace = 0;
1534 
1535  /* this must before then initial items are given */
1536  esrv_new_player(op->contr, op->weight+op->carrying);
1537  create_treasure(find_treasurelist("starting_wealth"), op, 0, 0, 0);
1538 
1539  /* This has to be done before class, otherwise the NOCLASSFACECHANGE
1540  * object is not in the inventory, and racial face will get overwritten.
1541  */
1542  give_initial_items(op, op->randomitems);
1543 
1544  if (stats) {
1545  /* Apply class information */
1547  } else {
1548  apply_changes_to_player(op, &opclass->clone, 0);
1549 
1550  /* Checks that all stats are greater than 1 */
1551  if (!allowed_class(op)) return 2;
1552  }
1553 
1554  /* Here we handle the BORN global event */
1556 
1557  /* We then generate a LOGIN event */
1558  events_execute_global_event(EVENT_LOGIN, op->contr, op->contr->socket->host);
1559 
1560  object_set_msg(op, NULL);
1561 
1562  /* We create this now because some of the unique maps will need it
1563  * to save here.
1564  */
1565  snprintf(buf, sizeof(buf), "%s/%s/%s", settings.localdir, settings.playerdir, op->name);
1567 
1568 #ifdef AUTOSAVE
1569  op->contr->last_save_tick = pticks;
1570 #endif
1571 
1574  fix_object(op);
1575 
1578  esrv_add_spells(op->contr, NULL);
1579 
1580  return 0;
1581 
1582 }
1583 
1592 void key_confirm_quit(object *op, char key) {
1593  char buf[MAX_BUF];
1594  mapstruct *mp, *next;
1595 
1596  // this was tested when 'quit' command was issued, but better safe than sorry.
1597  if (QUERY_FLAG(op, FLAG_WIZ)) {
1598  player_set_state(op->contr, ST_PLAYING);
1599  draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_LOGIN, "Can't quit when in DM mode.");
1600  return;
1601  }
1602 
1603  if (key != 'y' && key != 'Y' && key != 'q' && key != 'Q') {
1604  player_set_state(op->contr, ST_PLAYING);
1606  "OK, continuing to play.");
1607  return;
1608  }
1609 
1612  object_remove(op);
1613  op->direction = 0;
1615  "%s quits the game.",
1616  op->name);
1617 
1618  strcpy(op->contr->killer, "quit");
1619  hiscore_check(op, 0);
1620  party_leave(op);
1621  if (settings.set_title == TRUE)
1622  player_set_own_title(op->contr, "");
1623 
1624 
1625  /* We need to hunt for any per player unique maps in memory and
1626  * get rid of them. The trailing slash in the path is intentional,
1627  * so that players named 'Ab' won't match against players 'Abe' pathname
1628  */
1629  snprintf(buf, sizeof(buf), "%s/%s/%s/", settings.localdir, settings.playerdir, op->name);
1630  for (mp = first_map; mp != NULL; mp = next) {
1631  next = mp->next;
1632  if (!strncmp(mp->path, buf, strlen(buf)))
1633  delete_map(mp);
1634  }
1635 
1636  delete_character(op->name);
1637 
1638  /* Remove player from account list and send back data if needed */
1639  if (op->contr->socket->account_chars != NULL) {
1640  account_char_remove(op->contr->socket->account_chars, op->name);
1641  account_char_save(op->contr->socket->account_chars);
1642  /* char information is reloaded in send_account_players below */
1643  account_char_free(op->contr->socket->account_chars);
1644  op->contr->socket->account_chars = NULL;
1645  account_remove_player(op->contr->socket->account_name, op->name);
1646  send_account_players(op->contr->socket);
1647  }
1648 
1649  play_again(op);
1650 }
1651 
1658 static void flee_player(object *op) {
1659  int dir, diff;
1660  rv_vector rv;
1661 
1662  if (op->stats.hp < 0) {
1663  LOG(llevDebug, "Fleeing player is dead.\n");
1665  return;
1666  }
1667 
1668  if (op->enemy == NULL) {
1669  LOG(llevDebug, "Fleeing player had no enemy.\n");
1671  return;
1672  }
1673 
1674  /* Seen some crashes here. Since we don't store an
1675  * op->enemy_count, it is possible that something destroys the
1676  * actual enemy, and the object is recycled.
1677  */
1678  if (op->enemy->map == NULL) {
1680  object_set_enemy(op, NULL);
1681  return;
1682  }
1683 
1684  if (!(random_roll(0, 4, op, PREFER_LOW)) && did_make_save(op, op->level, 0)) {
1685  object_set_enemy(op, NULL);
1687  return;
1688  }
1689  if (!get_rangevector(op, op->enemy, &rv, 0)) {
1690  object_set_enemy(op, NULL);
1692  return;
1693  }
1694 
1695  dir = absdir(4+rv.direction);
1696  for (diff = 0; diff < 3; diff++) {
1697  int m = 1-(RANDOM()&2);
1698  if (move_ob(op, absdir(dir+diff*m), op)
1699  || (diff == 0 && move_ob(op, absdir(dir-diff*m), op))) {
1700  return;
1701  }
1702  }
1703  /* Cornered, get rid of scared */
1705  object_set_enemy(op, NULL);
1706 }
1707 
1717 int check_pick(object *op) {
1718  tag_t op_tag;
1719  int stop = 0;
1720  int j, k, wvratio, current_ratio;
1721  char putstring[128], tmpstr[16];
1722 
1723  /* if you're flying, you can't pick up anything */
1724  if (op->move_type&MOVE_FLYING)
1725  return 1;
1726  /* If not a player, don't check anything. */
1727  if (!op->contr) {
1728  return 1;
1729  }
1730 
1731  op_tag = op->count;
1732 
1734  if (object_was_destroyed(op, op_tag))
1735  return 0;
1736 
1737  if (!object_can_pick(op, tmp))
1738  continue;
1739 
1740  if (op->contr->search_str[0] != '\0' && settings.search_items == TRUE) {
1741  if (object_matches_string(op, tmp, op->contr->search_str))
1742  pick_up(op, tmp);
1743  continue;
1744  }
1745 
1746  /* high not bit set? We're using the old autopickup model */
1747  if (!(op->contr->mode&PU_NEWMODE)) {
1748  switch (op->contr->mode) {
1749  case 0:
1750  return 1; /* don't pick up */
1751 
1752  case 1:
1753  pick_up(op, tmp);
1754  return 1;
1755 
1756  case 2:
1757  pick_up(op, tmp);
1758  return 0;
1759 
1760  case 3:
1761  return 0; /* stop before pickup */
1762 
1763  case 4:
1764  pick_up(op, tmp);
1765  break;
1766 
1767  case 5:
1768  pick_up(op, tmp);
1769  stop = 1;
1770  break;
1771 
1772  case 6:
1775  pick_up(op, tmp);
1776  break;
1777 
1778  case 7:
1779  if (tmp->type == MONEY || tmp->type == GEM)
1780  pick_up(op, tmp);
1781  break;
1782 
1783  default:
1784  /* use value density */
1785  if (!QUERY_FLAG(tmp, FLAG_UNPAID)
1786  && (price_base(tmp)*100/(tmp->weight*MAX(tmp->nrof, 1))) >= op->contr->mode)
1787  pick_up(op, tmp);
1788  }
1789  } else { /* old model */
1790  /* NEW pickup handling */
1791  if (op->contr->mode&PU_DEBUG) {
1792  /* some debugging code to figure out item information */
1794  "item name: %s item type: %d weight/value: %d",
1795  tmp->name ? tmp->name : tmp->arch->name, tmp->type,
1796  (int)(price_base(tmp)*100/(tmp->weight*MAX(tmp->nrof, 1))));
1797 
1798 
1799  snprintf(putstring, sizeof(putstring), "...flags: ");
1800  for (k = 0; k < 4; k++) {
1801  for (j = 0; j < 32; j++) {
1802  if ((tmp->flags[k]>>j)&0x01) {
1803  snprintf(tmpstr, sizeof(tmpstr), "%d ", k*32+j);
1804  strcat(putstring, tmpstr);
1805  }
1806  }
1807  }
1809  putstring);
1810  }
1811  /* philosophy:
1812  * It's easy to grab an item type from a pile, as long as it's
1813  * generic. This takes no game-time. For more detailed pickups
1814  * and selections, select-items should be used. This is a
1815  * grab-as-you-run type mode that's really useful for arrows for
1816  * example.
1817  * The drawback: right now it has no frontend, so you need to
1818  * stick the bits you want into a calculator in hex mode and then
1819  * convert to decimal and then 'pickup <#>
1820  */
1821 
1822  /* the first two modes are exclusive: if NOTHING we return, if
1823  * STOP then we stop. All the rest are applied sequentially,
1824  * meaning if any test passes, the item gets picked up. */
1825 
1826  /* if mode is set to pick nothing up, return */
1827 
1828  if (op->contr->mode == PU_NOTHING)
1829  return 1;
1830 
1831  /* if mode is set to stop when encountering objects, return.
1832  * Take STOP before INHIBIT since it doesn't actually pick
1833  * anything up */
1834 
1835  if (op->contr->mode&PU_STOP)
1836  return 0;
1837 
1838  /* useful for going into stores and not losing your settings... */
1839  /* and for battles where you don't want to get loaded down while
1840  * fighting */
1841  if (op->contr->mode&PU_INHIBIT)
1842  return 1;
1843 
1844  /* prevent us from turning into auto-thieves :) */
1845  if (QUERY_FLAG(tmp, FLAG_UNPAID))
1846  continue;
1847 
1848  /* ignore known cursed objects */
1849  if (QUERY_FLAG(tmp, FLAG_KNOWN_CURSED) && op->contr->mode&PU_NOT_CURSED)
1850  continue;
1851 
1852  static int checks[] = {
1853  PU_FOOD,
1854  PU_DRINK,
1855  PU_FLESH,
1856  PU_POTION,
1857  PU_SPELLBOOK,
1859  PU_READABLES,
1861  PU_MAGICAL,
1862  PU_VALUABLES,
1863  PU_JEWELS,
1864  PU_BOW,
1865  PU_ARROW,
1866  PU_ARMOUR,
1867  PU_HELMET,
1868  PU_SHIELD,
1869  PU_BOOTS,
1870  PU_GLOVES,
1871  PU_CLOAK,
1874  PU_KEY,
1875  PU_CONTAINER,
1876  PU_CURSED,
1877  0
1878  };
1879  int found = 0;
1880  for (int m = 0; checks[m] != 0; m++) {
1881  if (op->contr->mode & checks[m] && object_matches_pickup_mode(tmp, checks[m])) {
1882  pick_up(op, tmp);
1883  found = 1;
1884  break;
1885  }
1886  }
1887  if (found) {
1888  continue;
1889  }
1890 
1891  /* any of the last 4 bits set means we use the ratio for value
1892  * pickups */
1893  if (op->contr->mode&PU_RATIO) {
1894  /* use value density to decide what else to grab.
1895  * >=7 was >= op->contr->mode
1896  * >=7 is the old standard setting. Now we take the last 4 bits
1897  * and multiply them by 5, giving 0..15*5== 5..75 */
1898  wvratio = (op->contr->mode&PU_RATIO)*5;
1899  current_ratio = price_base(tmp)*100/(tmp->weight*MAX(tmp->nrof, 1));
1900  if (current_ratio >= wvratio) {
1901  pick_up(op, tmp);
1902  continue;
1903  }
1904  }
1905  } /* the new pickup model */
1906  } FOR_BELOW_FINISH();
1907  return !stop;
1908 }
1909 
1922 static object *find_arrow(object *op, const char *type) {
1923  object *tmp = NULL;
1924 
1926  if (!tmp
1927  && inv->type == CONTAINER
1928  && inv->race == type
1930  tmp = find_arrow(inv, type);
1931  else if (inv->type == ARROW && inv->race == type)
1932  return inv;
1933  FOR_INV_FINISH();
1934  return tmp;
1935 }
1936 
1954 static object *find_better_arrow(object *op, object *target, const char *type, int *better) {
1955  object *tmp = NULL, *ntmp;
1956  int attacknum, attacktype, betterby = 0, i;
1957 
1958  if (!type)
1959  return NULL;
1960 
1961  FOR_INV_PREPARE(op, arrow) {
1962  if (arrow->type == CONTAINER
1963  && arrow->race == type
1964  && QUERY_FLAG(arrow, FLAG_APPLIED)) {
1965  i = 0;
1966  ntmp = find_better_arrow(arrow, target, type, &i);
1967  if (i > betterby) {
1968  tmp = ntmp;
1969  betterby = i;
1970  }
1971  } else if (arrow->type == ARROW && arrow->race == type) {
1972  /* always prefer assassination/slaying */
1973  if (target->race != NULL
1974  && arrow->slaying != NULL
1975  && strstr(arrow->slaying, target->race)) {
1976  if (arrow->attacktype&AT_DEATH) {
1977  if (better)
1978  *better = 100;
1979  return arrow;
1980  } else {
1981  tmp = arrow;
1982  betterby = (arrow->magic+arrow->stats.dam)*2;
1983  }
1984  } else {
1985  for (attacknum = 0; attacknum < NROFATTACKS; attacknum++) {
1986  attacktype = 1<<attacknum;
1987  if ((arrow->attacktype&attacktype) && (target->arch->clone.resist[attacknum]) < 0)
1988  if (((arrow->magic+arrow->stats.dam)*(100-target->arch->clone.resist[attacknum])/100) > betterby) {
1989  tmp = arrow;
1990  betterby = (arrow->magic+arrow->stats.dam)*(100-target->arch->clone.resist[attacknum])/100;
1991  }
1992  }
1993  if ((2+arrow->magic+arrow->stats.dam) > betterby) {
1994  tmp = arrow;
1995  betterby = 2+arrow->magic+arrow->stats.dam;
1996  }
1997  if (arrow->title && (1+arrow->magic+arrow->stats.dam) > betterby) {
1998  tmp = arrow;
1999  betterby = 1+arrow->magic+arrow->stats.dam;
2000  }
2001  }
2002  }
2003  } FOR_INV_FINISH();
2004  if (tmp == NULL)
2005  return find_arrow(op, type);
2006 
2007  if (better)
2008  *better = betterby;
2009  return tmp;
2010 }
2011 
2024 static object *pick_arrow_target(object *op, const char *type, int dir) {
2025  object *tmp = NULL;
2026  mapstruct *m;
2027  int i, mflags, found, number;
2028  int16_t x, y;
2029 
2030  if (op->map == NULL)
2031  return find_arrow(op, type);
2032 
2033  /* do a dex check */
2034  number = (die_roll(2, 40, op, PREFER_LOW)-2)/2;
2035  if (number > (op->stats.Dex+(op->chosen_skill ? op->chosen_skill->level : op->level)))
2036  return find_arrow(op, type);
2037 
2038  m = op->map;
2039  x = op->x;
2040  y = op->y;
2041 
2042  /* find the first target */
2043  for (i = 0, found = 0; i < 20; i++) {
2044  x += freearr_x[dir];
2045  y += freearr_y[dir];
2046  mflags = get_map_flags(m, &m, x, y, &x, &y);
2047  if (mflags&P_OUT_OF_MAP || mflags&P_BLOCKSVIEW) {
2048  tmp = NULL;
2049  break;
2050  } else if (GET_MAP_MOVE_BLOCK(m, x, y) == MOVE_FLY_LOW) {
2051  /* This block presumes arrows and the like are MOVE_FLY_SLOW -
2052  * perhaps a bad assumption.
2053  */
2054  tmp = NULL;
2055  break;
2056  }
2057  if (mflags&P_IS_ALIVE) {
2058  FOR_MAP_PREPARE(m, x, y, tmp2)
2059  if (QUERY_FLAG(tmp2, FLAG_ALIVE)) {
2060  tmp = tmp2;
2061  found++;
2062  break;
2063  }
2064  FOR_MAP_FINISH();
2065  if (found)
2066  break;
2067  }
2068  }
2069  if (tmp == NULL)
2070  return find_arrow(op, type);
2071 
2072  return find_better_arrow(op, HEAD(tmp), type, NULL);
2073 }
2074 
2093 int fire_bow(object *op, object *arrow, int dir, int wc_mod, int16_t sx, int16_t sy) {
2094  object *bow;
2095  tag_t tag;
2096  int bowspeed, mflags;
2097  mapstruct *m;
2098 
2099  if (!dir) {
2101  "You can't shoot yourself!");
2102  return 0;
2103  }
2104  if (op->type == PLAYER) {
2105  bow = op->contr->ranges[range_bow];
2106  // Make sure the bow's skill is readied.
2107  // Otherwise, you can get wrong modifiers from unarmed attacks
2108  // because that ends up the readied skill.
2109  object *skill = find_skill_by_name(op, bow->skill);
2110  if (skill && change_skill(op, skill, 1) == 0) {
2112  "You cannot use %s without the skill %s", bow->name, bow->skill);
2113  return 0;
2114  }
2115  }
2116  else {
2117  /* Don't check for applied - monsters don't apply bows - in that way, they
2118  * don't need to switch back and forth between bows and weapons.
2119  */
2120  bow = object_find_by_type(op, BOW);
2121  if (!bow) {
2122  LOG(llevError, "Range: bow without activated bow (%s).\n", op->name);
2123  return 0;
2124  }
2125  }
2126  if (!bow->race || !bow->skill) {
2128  "Your %s is broken.",
2129  bow->name);
2130  return 0;
2131  }
2132 
2133  bowspeed = bow->stats.sp+get_dex_bonus(op->stats.Dex);
2134 
2135  /* penalize ROF for bestarrow */
2136  if (op->type == PLAYER && op->contr->bowtype == bow_bestarrow)
2137  bowspeed -= get_dex_bonus(op->stats.Dex)+5;
2138  if (bowspeed < 1)
2139  bowspeed = 1;
2140 
2141  if (arrow == NULL) {
2142  arrow = find_arrow(op, bow->race);
2143  if (arrow == NULL) {
2144  if (op->type == PLAYER)
2147  "You have no %s left.",
2148  bow->race);
2149  /* FLAG_READY_BOW will get reset if the monsters picks up some arrows */
2150  else
2152  return 0;
2153  }
2154  }
2155  mflags = get_map_flags(op->map, &m, sx, sy, &sx, &sy);
2156  if (mflags&P_OUT_OF_MAP) {
2157  return 0;
2158  }
2159  if (GET_MAP_MOVE_BLOCK(m, sx, sy)&MOVE_FLY_LOW) {
2160  return 0;
2161  }
2162 
2163  /* this should not happen, but sometimes does */
2164  if (arrow->nrof == 0) {
2165  object_remove(arrow);
2167  return 0;
2168  }
2169 
2170  arrow = object_split(arrow, 1, NULL, 0);
2171  if (arrow == NULL) {
2173  "You have no %s left.",
2174  bow->race);
2175  return 0;
2176  }
2177  object_set_owner(arrow, op);
2178  if (arrow->skill)
2179  free_string(arrow->skill);
2180  arrow->skill = add_refcount(bow->skill);
2181 
2182  arrow->direction = dir;
2183 
2184  if (op->type == PLAYER) {
2185  op->speed_left = 0.01-(float)FABS(op->speed)*100/bowspeed;
2186  fix_object(op);
2187  }
2188 
2189  if (bow->anim_suffix != NULL)
2191 
2192 /* SET_ANIMATION(arrow, arrow->direction);*/
2193  object_update_turn_face(arrow);
2194  arrow->stats.sp = arrow->stats.wc; /* save original wc and dam */
2195  arrow->stats.hp = arrow->stats.dam;
2196  arrow->stats.grace = arrow->attacktype;
2197  if (arrow->slaying != NULL)
2198  arrow->spellarg = strdup_local(arrow->slaying);
2199 
2200  /* Note that this was different for monsters - they got their level
2201  * added to the damage. I think the strength bonus is more proper.
2202  */
2203 
2204  arrow->stats.dam += (QUERY_FLAG(bow, FLAG_NO_STRENGTH) ? 0 : get_dam_bonus(op->stats.Str))
2205  +bow->stats.dam
2206  +bow->magic
2207  +arrow->magic;
2208 
2209  /* update the speed */
2210  arrow->speed = (float)((QUERY_FLAG(bow, FLAG_NO_STRENGTH) ? 0 : get_dam_bonus(op->stats.Str))+bow->magic+arrow->magic)/5.0
2211  +(float)bow->stats.dam/7.0;
2212 
2213  if (arrow->speed < 1.0)
2214  arrow->speed = 1.0;
2215  object_update_speed(arrow);
2216  arrow->speed_left = 0;
2217 
2218  if (op->type == PLAYER) {
2219  /* we don't want overflows of wc (sint), so cap the value - mod and pl should be subtracted */
2220  int mod = bow->magic
2221  +arrow->magic
2222  +get_dex_bonus(op->stats.Dex)
2223  +get_thaco_bonus(op->stats.Str)
2224  +arrow->stats.wc
2225  +bow->stats.wc
2226  -wc_mod;
2227  int plmod = (op->chosen_skill ? op->chosen_skill->level : op->level);
2228  if (plmod+mod > 140)
2229  plmod = 140-mod;
2230  else if (plmod+mod < -100)
2231  plmod = -100-mod;
2232  arrow->stats.wc = 20-(int8_t)plmod-(int8_t)mod;
2233 
2234  arrow->level = op->chosen_skill ? op->chosen_skill->level : op->level;
2235  } else {
2236  arrow->stats.wc = op->stats.wc
2237  -bow->magic
2238  -arrow->magic
2239  -arrow->stats.wc
2240  +wc_mod;
2241 
2242  arrow->level = op->level;
2243  }
2244  if (arrow->attacktype == AT_PHYSICAL)
2245  arrow->attacktype |= bow->attacktype;
2246  if (bow->slaying != NULL)
2247  arrow->slaying = add_string(bow->slaying);
2248 
2249  /* If move_type is ever changed, monster.c:monster_use_bow() needs to be changed too. */
2250  arrow->move_type = MOVE_FLY_LOW;
2251  arrow->move_on = MOVE_FLY_LOW|MOVE_WALK;
2252 
2253  tag = arrow->count;
2254  object_insert_in_map_at(arrow, m, op, 0, sx, sy);
2255 
2256  if (!object_was_destroyed(arrow, tag)) {
2257  play_sound_map(SOUND_TYPE_ITEM, arrow, arrow->direction, "fire");
2258  ob_process(arrow);
2259  }
2260 
2261  return 1;
2262 }
2263 
2274 static int similar_direction(int a, int b) {
2275  /* shortcut the obvious */
2276  if (a == b)
2277  return 1;
2278  /* Made this cleaner using modulus instead of a switch statement
2279  * We only needed the direction and the two adjacent to it
2280  * (8 is adjacent to 1 here) to return true, so a - 1, a, and a + 1
2281  * are the three directions that get "similar" affirmed.
2282  * -- Daniel Hawkins 2015-05-28
2283  */
2284  // The last one for the offset is added afterwards so we get
2285  // 1-8 instead of 0-7 (specifically, 0 becomes 8 without changing
2286  // the other values).
2287  if ((a % 8) + 1 == b || (a + 6 % 8) + 1 == b)
2288  return 1;
2289  return 0;
2290 }
2291 
2308 static int player_fire_bow(object *op, int dir) {
2309  int ret = 0, wcmod = 0;
2310 
2311  if (op->contr->bowtype == bow_bestarrow) {
2312  ret = fire_bow(op, pick_arrow_target(op, op->contr->ranges[range_bow]->race, dir), dir, 0, op->x, op->y);
2313  } else if (op->contr->bowtype >= bow_n && op->contr->bowtype <= bow_nw) {
2314  if (!similar_direction(dir, op->contr->bowtype-bow_n+1))
2315  wcmod = -1;
2316  ret = fire_bow(op, NULL, op->contr->bowtype-bow_n+1, wcmod, op->x, op->y);
2317  } else if (op->contr->bowtype == bow_threewide) {
2318  ret = fire_bow(op, NULL, dir, 0, op->x, op->y);
2319  ret |= fire_bow(op, NULL, dir, -5, op->x+freearr_x[absdir(dir+2)], op->y+freearr_y[absdir(dir+2)]);
2320  ret |= fire_bow(op, NULL, dir, -5, op->x+freearr_x[absdir(dir-2)], op->y+freearr_y[absdir(dir-2)]);
2321  } else if (op->contr->bowtype == bow_spreadshot) {
2322  ret |= fire_bow(op, NULL, dir, 0, op->x, op->y);
2323  ret |= fire_bow(op, NULL, absdir(dir-1), -5, op->x, op->y);
2324  ret |= fire_bow(op, NULL, absdir(dir+1), -5, op->x, op->y);
2325  } else {
2326  /* Simple case */
2327  ret = fire_bow(op, NULL, dir, 0, op->x, op->y);
2328  }
2329  return ret;
2330 }
2331 
2344 static void fire_misc_object(object *op, int dir) {
2345  object *item;
2346  char name[MAX_BUF];
2347 
2348  item = op->contr->ranges[range_misc];
2349  if (!item) {
2351  "You have no range item readied.");
2352  return;
2353  }
2354  if (!item->inv) {
2355  LOG(llevError, "Object %s lacks a spell\n", item->name);
2356  return;
2357  }
2358  if (item->type == WAND) {
2359  if (item->stats.food <= 0) {
2360  play_sound_player_only(op->contr, SOUND_TYPE_ITEM, item, 0, "poof");
2363  "The %s goes poof.",
2364  name);
2365  return;
2366  }
2367  } else if (item->type == ROD) {
2368  if (item->stats.hp < SP_level_spellpoint_cost(item, item->inv, SPELL_HIGHEST)) {
2369  play_sound_player_only(op->contr, SOUND_TYPE_ITEM, item, 0, "poof");
2372  "The %s whines for a while, but nothing happens.",
2373  name);
2374  return;
2375  }
2376  }
2377 
2378  if (cast_spell(op, item, dir, item->inv, NULL)) {
2379  SET_FLAG(op, FLAG_BEEN_APPLIED); /* You now know something about it */
2380  if (item->type == WAND) {
2382  } else if (item->type == ROD) {
2384  }
2385  }
2386 }
2387 
2396 void fire(object *op, int dir) {
2397 
2398  /* check for loss of invisiblity/hide */
2399  if (action_makes_visible(op))
2400  make_visible(op);
2401 
2402  switch (op->contr->shoottype) {
2403  case range_none:
2404  return;
2405 
2406  case range_bow:
2407  player_fire_bow(op, dir);
2408  return;
2409 
2410  case range_magic: /* Casting spells */
2411  cast_spell(op, op, dir, op->contr->ranges[range_magic], op->contr->spellparam[0] ? op->contr->spellparam : NULL);
2412  return;
2413 
2414  case range_misc:
2415  fire_misc_object(op, dir);
2416  return;
2417 
2418  case range_golem: /* Control summoned monsters from scrolls */
2419  if (op->contr->ranges[range_golem] == NULL
2420  || op->contr->golem_count != op->contr->ranges[range_golem]->count) {
2421  op->contr->ranges[range_golem] = NULL;
2422  op->contr->shoottype = range_none;
2423  op->contr->golem_count = 0;
2424  } else
2425  pets_control_golem(op->contr->ranges[range_golem], dir);
2426  return;
2427 
2428  case range_skill:
2429  if (!op->chosen_skill) {
2430  if (op->type == PLAYER)
2432  "You have no applicable skill to use.");
2433  return;
2434  }
2435  (void)do_skill(op, op, op->chosen_skill, dir, NULL);
2436  return;
2437 
2438  case range_builder:
2439  apply_map_builder(op, dir);
2440  return;
2441 
2442  default:
2444  "Illegal shoot type.");
2445  return;
2446  }
2447 }
2448 
2468 object *find_key(object *pl, object *container, object *door) {
2469  object *tmp, *key;
2470 
2471  /* Should not happen, but sanity checking is never bad */
2472  if (container->inv == NULL)
2473  return NULL;
2474 
2475  /* First, lets try to find a key in the top level inventory */
2476  tmp = NULL;
2477  if (door->type == DOOR) {
2478  int flag = FLAG_UNPAID;
2479  tmp = object_find_by_type_without_flags(container, KEY, &flag, 1);
2480  }
2481  /* For sanity, we should really check door type, but other stuff
2482  * (like containers) can be locked with special keys
2483  */
2484  if (!tmp && door->slaying != NULL) {
2486  }
2487  /* No key found - lets search inventories now */
2488  /* If we find and use a key in an inventory, return at that time.
2489  * otherwise, if we search all the inventories and still don't find
2490  * a key, return
2491  */
2492  if (!tmp) {
2493  FOR_INV_PREPARE(container, tmp) {
2494  /* No reason to search empty containers */
2495  if (tmp->type == CONTAINER && tmp->inv) {
2496  key = find_key(pl, tmp, door);
2497  if (key != NULL)
2498  return key;
2499  }
2500  } FOR_INV_FINISH();
2501  return NULL;
2502  }
2503  /* We get down here if we have found a key. Now if its in a container,
2504  * see if we actually want to use it
2505  */
2506  if (pl != container) {
2507  /* Only let players use keys in containers */
2508  if (!pl->contr)
2509  return NULL;
2510  /* cases where this fails:
2511  * If we only search the player inventory, return now since we
2512  * are not in the players inventory.
2513  * If the container is not active, return now since only active
2514  * containers can be used.
2515  * If we only search keyrings and the container does not have
2516  * a race/isn't a keyring.
2517  * No checking for all containers - to fall through past here,
2518  * inv must have been an container and must have been active.
2519  *
2520  * Change the color so that the message doesn't disappear with
2521  * all the others.
2522  */
2523  if (pl->contr->usekeys == key_inventory
2524  || !QUERY_FLAG(container, FLAG_APPLIED)
2525  || (pl->contr->usekeys == keyrings && (!container->race || strcmp(container->race, "keys")))) {
2526  char name_tmp[MAX_BUF], name_cont[MAX_BUF];
2527 
2528  query_name(tmp, name_tmp, MAX_BUF);
2529  query_name(container, name_cont, MAX_BUF);
2532  "The %s in your %s vibrates as you approach the door",
2533  name_tmp, name_cont);
2534  return NULL;
2535  }
2536  }
2537  return tmp;
2538 }
2539 
2550 static int player_attack_door(object *op, object *door) {
2551  /* If its a door, try to find a use a key. If we do destroy the door,
2552  * might as well return immediately as there is nothing more to do -
2553  * otherwise, we fall through to the rest of the code.
2554  */
2555  object *key = find_key(op, op, door);
2556 
2557  assert(door->type == DOOR || door->type == LOCKED_DOOR);
2558 
2559  /* IF we found a key, do some extra work */
2560  if (key) {
2561  char name[HUGE_BUF];
2562 
2563  play_sound_map(SOUND_TYPE_GROUND, door, 0, "open");
2564  if (action_makes_visible(op))
2565  make_visible(op);
2566  if (door->inv && (door->inv->type == RUNE || door->inv->type == TRAP))
2567  spring_trap(door->inv, op);
2568 
2572  "You open the door with the %s",
2573  name);
2574 
2575  if (door->type == DOOR)
2576  remove_door(door);
2577  else
2578  remove_locked_door(door); /* remove door without violence ;-) */
2579 
2580  /* Do this after we print the message */
2581  object_decrease_nrof_by_one(key); /* Use up one of the keys */
2582 
2583  return 1; /* Nothing more to do below */
2584 
2585  }
2586 
2587  if (door->type == LOCKED_DOOR) {
2588  /* Might as well return now - no other way to open this */
2589  if (door->msg && *door->msg) {
2591  door->msg);
2592  }
2593  return 1;
2594  }
2595 
2596  if (door->type == DOOR && op->contr && !op->contr->run_on) {
2597  /* Player so try to pick the door */
2598  object *lock = find_skill_by_name(op, "lockpicking");
2599  if (lock) {
2600  /* Even if the lockpicking failed, don't go on moving, player should explicitely attack or run
2601  * to bash the door. */
2602  do_skill(op, op, lock, op->facing, NULL);
2603  return 1;
2604  }
2605  }
2606 
2607  return 0;
2608 }
2609 
2623 void move_player_attack(object *op, int dir) {
2624  object *mon, *tpl, *mon_owner;
2625  int16_t nx, ny;
2626  int on_battleground;
2627  mapstruct *m;
2628 
2629  if (op->contr->transport)
2630  tpl = op->contr->transport;
2631  else
2632  tpl = op;
2633  assert(tpl->map != NULL); // op must be on a map in order to move it
2634  nx = freearr_x[dir]+tpl->x;
2635  ny = freearr_y[dir]+tpl->y;
2636 
2637  on_battleground = op_on_battleground(tpl, NULL, NULL, NULL);
2638 
2639  // Temporarily store the map we are on before movement.
2640  mapstruct *bef = tpl->map;
2641 
2642  /* If braced, or can't move to the square, and it is not out of the
2643  * map, attack it. Note order of if statement is important - don't
2644  * want to be calling move_ob if braced, because move_ob will move the
2645  * player. This is a pretty nasty hack, because if we could
2646  * move to some space, it then means that if we are braced, we should
2647  * do nothing at all. As it is, if we are braced, we go through
2648  * quite a bit of processing. However, it probably is less than what
2649  * move_ob uses.
2650  */
2651  if ((op->contr->braced || !move_ob(tpl, dir, tpl)) && !out_of_map(tpl->map, nx, ny)) {
2652  if (OUT_OF_REAL_MAP(tpl->map, nx, ny)) {
2653  m = get_map_from_coord(tpl->map, &nx, &ny);
2654  if (!m)
2655  return; /* Don't think this should happen */
2656  } else
2657  m = tpl->map;
2658 
2659  if (GET_MAP_OB(m, nx, ny) == NULL) {
2660  /* LOG(llevError, "player_move_attack: GET_MAP_OB returns NULL, but player can not move there.\n");*/
2661  return;
2662  }
2663 
2664  mon = NULL;
2665  /* Go through all the objects, and find ones of interest. Only stop if
2666  * we find a monster - that is something we know we want to attack.
2667  * if its a door or barrel (can roll) see if there may be monsters
2668  * on the space
2669  */
2670  FOR_MAP_PREPARE(m, nx, ny, tmp) {
2671  if (tmp == op) {
2672  continue;
2673  }
2674  if (QUERY_FLAG(tmp, FLAG_ALIVE)) {
2675  mon = tmp;
2676  /* Gros: Objects like (pass-through) doors are alive, but haven't
2677  * their monster flag set - so this is a good way attack real
2678  * monsters in priority.
2679  */
2680  if (QUERY_FLAG(tmp, FLAG_MONSTER))
2681  break;
2682  }
2683  if (tmp->type == LOCKED_DOOR || QUERY_FLAG(tmp, FLAG_CAN_ROLL))
2684  mon = tmp;
2685  } FOR_MAP_FINISH();
2686 
2687  if (mon == NULL) /* This happens anytime the player tries to move */
2688  return; /* into a wall */
2689 
2690  mon = HEAD(mon);
2691  if ((mon->type == DOOR && mon->stats.hp >= 0) || (mon->type == LOCKED_DOOR))
2692  if (player_attack_door(op, mon))
2693  return;
2694 
2695  /* The following deals with possibly attacking peaceful
2696  * or friendly creatures. Basically, all players are considered
2697  * unaggressive. If the moving player has peaceful set, then the
2698  * object should be pushed instead of attacked. It is assumed that
2699  * if you are braced, you will not attack friends accidently,
2700  * and thus will not push them.
2701  */
2702 
2703  /* If the creature is a pet, push it even if the player is not
2704  * peaceful. Our assumption is the creature is a pet if the
2705  * player owns it and it is either friendly or unaggressive.
2706  */
2707  mon_owner = object_get_owner(mon);
2708  if ((op->type == PLAYER)
2709  && (mon_owner == op || (mon_owner != NULL && mon_owner->type == PLAYER && mon_owner->contr->party != NULL && mon_owner->contr->party == op->contr->party))
2711  /* If we're braced, we don't want to switch places with it */
2712  if (op->contr->braced)
2713  return;
2714  play_sound_map(SOUND_TYPE_LIVING, mon, dir, "push");
2715  (void)push_ob(mon, dir, op);
2716  if (op->contr->tmp_invis || op->hide)
2717  make_visible(op);
2718  return;
2719  }
2720 
2721  /* in certain circumstances, you shouldn't attack friendly
2722  * creatures. Note that if you are braced, you can't push
2723  * someone, but put it inside this loop so that you won't
2724  * attack them either.
2725  */
2726  if ((mon->type == PLAYER || mon->enemy != op)
2728  && (op->contr->peaceful && !on_battleground)) {
2729  if (!op->contr->braced) {
2730  play_sound_map(SOUND_TYPE_LIVING, mon, dir, "push");
2731  (void)push_ob(mon, dir, op);
2732  } else {
2734  "You withhold your attack");
2735  }
2736  if (op->contr->tmp_invis || op->hide)
2737  make_visible(op);
2738  }
2739 
2740  /* If the object is a boulder or other rollable object, then
2741  * roll it if not braced. You can't roll it if you are braced.
2742  */
2743  else if (QUERY_FLAG(mon, FLAG_CAN_ROLL) && (!op->contr->braced)) {
2744  recursive_roll(mon, dir, op);
2745  if (action_makes_visible(op))
2746  make_visible(op);
2747 
2748  /* Any generic living creature. Including things like doors.
2749  * Way it works is like this: First, it must have some hit points
2750  * and be living. Then, it must be one of the following:
2751  * 1) Not a player, 2) A player, but of a different party. Note
2752  * that party_number -1 is no party, so attacks can still happen.
2753  */
2754  } else if ((mon->stats.hp >= 0)
2756  && ((mon->type != PLAYER || op->contr->party == NULL || op->contr->party != mon->contr->party))) {
2757  /* If the player hasn't hit something this tick, and does
2758  * so, give them speed boost based on weapon speed. Doing
2759  * it here is better than process_players2, which basically
2760  * incurred a 1 tick offset.
2761  */
2762  if (op->weapon_speed_left < 0) {
2763  op->speed_left = -0.01;
2764  return;
2765  }
2766  op->weapon_speed_left -= 1.0;
2767 
2768  skill_attack(mon, op, 0, NULL, NULL);
2769 
2770  /* If attacking another player, that player gets automatic
2771  * hitback, and doesn't loose luck either.
2772  * Disable hitback on the battleground or if the target is
2773  * the wiz.
2774  */
2775  if (mon->type == PLAYER
2776  && mon->stats.hp >= 0
2777  && !mon->contr->has_hit
2778  && !on_battleground
2779  && !QUERY_FLAG(mon, FLAG_WIZ)) {
2780  short luck = mon->stats.luck;
2781  mon->contr->has_hit = 1;
2782  skill_attack(op, mon, 0, NULL, NULL);
2783  mon->stats.luck = luck;
2784  }
2785  if (action_makes_visible(op))
2786  make_visible(op);
2787  }
2788  } /* if player should attack something */
2789  else if (bef != tpl->map) {
2790  player_map_change_common(op, bef, tpl->map);
2791  }
2792 }
2793 
2800 static void update_transport_block(object *transport, int dir) {
2801  object *part;
2802  int sx, sy, x, y;
2803 
2804  object_get_multi_size(transport, &sx, &sy, NULL, NULL);
2805  assert(sx == sy);
2806 
2807  if (dir == 1 || dir == 5) {
2808  part = transport;
2809  for (y = 0; y <= sy; y++) {
2810  for (x = 0; x < sx; x++) {
2811  part->move_type = transport->move_type;
2812  part = part->more;
2813  }
2814  part->move_type = 0;
2815  part = part->more;
2816  }
2817  } else if (dir == 3 || dir == 7) {
2818  part = transport;
2819  for (y = 0; y < sy; y++) {
2820  for (x = 0; x <= sx; x++) {
2821  part->move_type = transport->move_type;
2822  part = part->more;
2823  }
2824  }
2825  while (part) {
2826  part->move_type = 0;
2827  part = part->more;
2828  }
2829  } else {
2830  for (part = transport; part; part = part->more) {
2831  part->move_type = transport->move_type;
2832  }
2833  }
2834 }
2835 
2845 static int turn_one_transport(object *transport, object *captain, int dir) {
2846  int x, y, scroll_dir = 0;
2847 
2848  assert(transport->type == TRANSPORT);
2849 
2850  x = transport->x;
2851  y = transport->y;
2852 
2853  if (transport->direction == 1 && dir == 8) {
2854  x--;
2855  } else if (transport->direction == 2 && dir == 3) {
2856  y++;
2857  } else if (transport->direction == 3 && dir == 2) {
2858  y--;
2859  } else if (transport->direction == 5 && dir == 6) {
2860  x--;
2861  } else if (transport->direction == 6 && dir == 5) {
2862  x++;
2863  } else if (transport->direction == 7 && dir == 8) {
2864  y--;
2865  } else if (transport->direction == 8 && dir == 7) {
2866  y++;
2867  } else if (transport->direction == 8 && dir == 1) {
2868  x++;
2869  }
2870 
2871  update_transport_block(transport, dir);
2872  object_remove(transport);
2873  if (ob_blocked(transport, transport->map, x, y)) {
2874  update_transport_block(transport, transport->direction);
2875  object_insert_in_map_at(transport, transport->map, NULL, 0, x, y);
2876  return 2;
2877  }
2878 
2879  if (x != transport->x || y != transport->y) {
2880 /* assert(scroll_dir != 0);*/
2881 
2882  FOR_INV_PREPARE(transport, pl) {
2883  if (pl->type == PLAYER) {
2884  pl->contr->do_los = 1;
2885  pl->map = transport->map;
2886  pl->x = x;
2887  pl->y = y;
2888  esrv_map_scroll(pl->contr->socket, freearr_x[scroll_dir], freearr_y[scroll_dir]);
2889  pl->contr->socket->update_look = 1;
2890  pl->contr->socket->look_position = 0;
2891  }
2892  } FOR_INV_FINISH();
2893  }
2894 
2895  object_insert_in_map_at(transport, transport->map, NULL, 0, x, y);
2896  transport->direction = dir;
2897  transport->facing = dir;
2898  animate_object(transport, dir);
2899  captain->direction = dir;
2900  return 1;
2901 }
2902 
2915 static int turn_transport(object *transport, object *captain, int dir) {
2916  assert(transport->type == TRANSPORT);
2917 
2918  if (object_value_set(transport, "turnable_transport") == false) {
2919  transport->direction = dir;
2920  transport->facing = dir;
2921  if (QUERY_FLAG(transport, FLAG_ANIMATE)) {
2922  animate_object(transport, dir);
2923  }
2924  captain->direction = dir;
2925  return 0;
2926  }
2927 
2928  if (transport->direction == dir)
2929  return 0;
2930 
2931  if (absdir(transport->direction-dir) > 2)
2932  return turn_one_transport(transport, captain, absdir(transport->direction+1));
2933  else
2934  return turn_one_transport(transport, captain, absdir(transport->direction-1));
2935 }
2936 
2948 int move_player(object *op, int dir) {
2949  object *transport = op->contr->transport; //< Transport player is in
2950 
2951  if (!transport && (op->map == NULL || op->map->in_memory != MAP_IN_MEMORY))
2952  return 0;
2953 
2954  /* Sanity check: make sure dir is valid */
2955  if ((dir < 0) || (dir >= 9)) {
2956  LOG(llevError, "move_player: invalid direction %d\n", dir);
2957  return 0;
2958  }
2959 
2960  if (QUERY_FLAG(op, FLAG_CONFUSED) && dir)
2961  dir = get_randomized_dir(dir);
2962 
2963  op->facing = dir;
2964 
2965  if (transport) {
2966  /* transport->contr is set up for the person in charge of the boat.
2967  * if that isn't this person, he can't steer it, etc
2968  */
2969  if (transport->contr != op->contr)
2970  return 0;
2971 
2972  /* Transport is out of movement. But update dir so it at least
2973  * will point in the same direction if player is running.
2974  */
2975  if (transport->speed_left < 0.0) {
2976  return 0;
2977  }
2978  /* Remove transport speed. Give player just a little speed -
2979  * enough so that they will get an action again quickly.
2980  */
2981  transport->speed_left -= 1.0;
2982  if (op->speed_left < 0.0)
2983  op->speed_left = -0.01;
2984 
2985  int turn = turn_transport(transport, op, dir);
2986  if (turn != 0)
2987  return 0;
2988  } else {
2989  if (op->hide) {
2990  do_hidden_move(op);
2991  }
2992 
2993  /* it is important to change the animation now, as fire or move_player_attack can start a compound animation,
2994  * and leave us with state = 0, which we don't want to change again. */
2995  op->state++; /* player moved, so change animation. */
2996  animate_object(op, op->facing);
2997  }
2998 
2999  if (op->contr->fire_on) {
3000  fire(op, dir);
3001  } else
3002  move_player_attack(op, dir);
3003 
3004  int pick = check_pick(op);
3005 
3006  /* Add special check for newcs players and fire on - this way, the
3007  * server can handle repeat firing.
3008  */
3009  if (op->contr->fire_on || (op->contr->run_on && pick != 0)) {
3010  op->direction = dir;
3011  } else {
3012  op->direction = 0;
3013  }
3014  return 0;
3015 }
3016 
3027 int face_player(object *op, int dir) {
3028  object *transport = op->contr->transport; //< Transport player is in
3029 
3030  if (!transport && (op->map == NULL || op->map->in_memory != MAP_IN_MEMORY))
3031  return 0;
3032 
3033  /* Sanity check: make sure dir is valid */
3034  if ((dir < 0) || (dir >= 9)) {
3035  LOG(llevError, "move_player: invalid direction %d\n", dir);
3036  return 0;
3037  }
3038 
3039  if (QUERY_FLAG(op, FLAG_CONFUSED) && dir)
3040  dir = get_randomized_dir(dir);
3041 
3042  op->facing = dir;
3043 
3044  if (transport) {
3045  /* transport->contr is set up for the person in charge of the boat.
3046  * if that isn't this person, he can't steer it, etc
3047  */
3048  if (transport->contr != op->contr)
3049  return 0;
3050 
3051  turn_transport(transport, op, dir);
3052  } else {
3053  if (op->hide) {
3054  do_hidden_move(op);
3055  }
3056 
3057  /* it is important to change the animation now, as fire or move_player_attack can start a compound animation,
3058  * and leave us with state = 0, which we don't want to change again. */
3059  op->state++; /* player moved, so change animation. */
3060  animate_object(op, op->facing);
3061  }
3062 
3063  /* Add special check for newcs players and fire on - this way, the
3064  * server can handle repeat firing.
3065  */
3066  if (op->contr->fire_on || op->contr->run_on) {
3067  op->direction = dir;
3068  } else {
3069  op->direction = 0;
3070  }
3071  return 0;
3072 }
3073 
3086 int handle_newcs_player(object *op) {
3087  if (op->contr->hidden) {
3088  op->invisible = 1000;
3089  /* the socket code flashes the player visible/invisible
3090  * depending on the value if invisible, so we need to
3091  * alternate it here for it to work correctly.
3092  */
3093  if (pticks&2)
3094  op->invisible--;
3095  } else if (op->invisible && !(QUERY_FLAG(op, FLAG_MAKE_INVIS))) {
3096  op->invisible--;
3097  if (!op->invisible) {
3098  make_visible(op);
3100  "Your invisibility spell runs out.");
3101  }
3102  }
3103 
3104  if (QUERY_FLAG(op, FLAG_SCARED)) {
3105  flee_player(op);
3106  /* If player is still scared, that is his action for this tick */
3107  if (QUERY_FLAG(op, FLAG_SCARED)) {
3108  op->speed_left--;
3109  return 0;
3110  }
3111  }
3112 
3113  /* I've been seeing crashes where the golem has been destroyed, but
3114  * the player object still points to the defunct golem. The code that
3115  * destroys the golem looks correct, and it doesn't always happen, so
3116  * put this in a a workaround to clean up the golem pointer.
3117  */
3118  if (op->contr->ranges[range_golem]
3119  && ((op->contr->golem_count != op->contr->ranges[range_golem]->count) || QUERY_FLAG(op->contr->ranges[range_golem], FLAG_REMOVED))) {
3120  op->contr->ranges[range_golem] = NULL;
3121  op->contr->golem_count = 0;
3122  }
3123 
3124  /*
3125  * If the player has been paralyzed, we unmark the flag and give a message to the player
3126  */
3127  if (QUERY_FLAG(op, FLAG_PARALYZED)) {
3129  // TODO: Is this check necessary? We are in player.c, after all.
3130  if (op->type == PLAYER)
3131  {
3133  "You can stretch your stiff joints once more.");
3134  }
3135  }
3136 
3137  if (op->direction && (op->contr->run_on || op->contr->fire_on)) {
3138  /* All move commands take 1 tick, at least for now */
3139  op->speed_left--;
3140 
3141  /* Instead of all the stuff below, let move_player take care
3142  * of it. Also, some of the skill stuff is only put in
3143  * there, as well as the confusion stuff.
3144  */
3145  move_player(op, op->direction);
3146  if (op->speed_left > 0)
3147  return 1;
3148  else
3149  return 0;
3150  }
3151  return 0;
3152 }
3153 
3164 static int save_life(object *op) {
3165  object *tmp;
3166 
3167  if (!QUERY_FLAG(op, FLAG_LIFESAVE))
3168  return 0;
3169 
3171  if (tmp != NULL) {
3172  char name[MAX_BUF];
3173 
3175  play_sound_map(SOUND_TYPE_ITEM, tmp, 0, "evaporate");
3177  "Your %s vibrates violently, then evaporates.",
3178  name);
3179  object_remove(tmp);
3182  if (op->stats.hp < 0)
3183  op->stats.hp = op->stats.maxhp;
3184  if (op->stats.food < 0)
3185  op->stats.food = MAX_FOOD;
3186  fix_object(op);
3187  return 1;
3188  }
3189  LOG(llevError, "Error: LIFESAVE set without applied object.\n");
3191  enter_player_savebed(op); /* bring him home. */
3192  return 0;
3193 }
3194 
3207 void remove_unpaid_objects(object *op, object *env, int free_items) {
3209  if (QUERY_FLAG(op, FLAG_UNPAID)) {
3210  object_remove(op);
3211  if (free_items)
3213  else
3214  object_insert_in_map_at(op, env->map, NULL, 0, env->x, env->y);
3215  } else if (op->inv)
3216  remove_unpaid_objects(op->inv, env, free_items);
3218 }
3219 
3237 static const char *gravestone_text(object *op, char *buf2, int len) {
3238  char buf[MAX_BUF];
3239  time_t now = time(NULL);
3240 
3241  strncpy(buf2, " R.I.P.\n\n", len);
3242  if (op->type == PLAYER)
3243  snprintf(buf, sizeof(buf), "%s the %s\n", op->name, op->contr->title);
3244  else
3245  snprintf(buf, sizeof(buf), "%s\n", op->name);
3246  strncat(buf2, " ", 20-strlen(buf)/2);
3247  strncat(buf2, buf, len-strlen(buf2)-1);
3248  if (op->type == PLAYER)
3249  snprintf(buf, sizeof(buf), "who was in level %d when killed\n", op->level);
3250  else
3251  snprintf(buf, sizeof(buf), "who was in level %d when died.\n\n", op->level);
3252  strncat(buf2, " ", 20-strlen(buf)/2);
3253  strncat(buf2, buf, len-strlen(buf2)-1);
3254  if (op->type == PLAYER) {
3255  snprintf(buf, sizeof(buf), "by %s.\n\n", op->contr->killer);
3256  strncat(buf2, " ", 21-strlen(buf)/2);
3257  strncat(buf2, buf, len-strlen(buf2)-1);
3258  }
3259  strftime(buf, MAX_BUF, "%b %d %Y\n", localtime(&now));
3260  strncat(buf2, " ", 20-strlen(buf)/2);
3261  strncat(buf2, buf, len-strlen(buf2)-1);
3262  return buf2;
3263 }
3264 
3272 void do_some_living(object *op) {
3273  int last_food = op->stats.food;
3274  int gen_hp, gen_sp, gen_grace;
3275  int rate_hp = 1200;
3276  int rate_sp = 2500;
3277  int rate_grace = 2000;
3278 
3279  if (op->contr->state == ST_PLAYING) {
3280  /* these next three if clauses make it possible to SLOW DOWN
3281  hp/grace/spellpoint regeneration. */
3282  if (op->contr->gen_hp >= 0)
3283  gen_hp = (op->contr->gen_hp+1)*op->stats.maxhp;
3284  else {
3285  gen_hp = op->stats.maxhp;
3286  rate_hp -= rate_hp/2*op->contr->gen_hp;
3287  }
3288  if (op->contr->gen_sp >= 0)
3289  gen_sp = (op->contr->gen_sp+1)*op->stats.maxsp;
3290  else {
3291  gen_sp = op->stats.maxsp;
3292  rate_sp -= rate_sp/2*op->contr->gen_sp;
3293  }
3294  if (op->contr->gen_grace >= 0)
3295  gen_grace = (op->contr->gen_grace+1)*op->stats.maxgrace;
3296  else {
3297  gen_grace = op->stats.maxgrace;
3298  rate_grace -= rate_grace/2*op->contr->gen_grace;
3299  }
3300 
3301  /* Regenerate Spell Points */
3302  if (op->contr->ranges[range_golem] == NULL && --op->last_sp < 0) {
3303  gen_sp = gen_sp*10/MAX(op->contr->gen_sp_armour, 10);
3304  if (op->stats.sp < op->stats.maxsp) {
3305  op->stats.sp++;
3306  /* dms do not consume food */
3307  if (!QUERY_FLAG(op, FLAG_WIZ)) {
3308  op->stats.food--;
3309  if (op->contr->digestion < 0)
3310  op->stats.food += op->contr->digestion;
3311  else if (op->contr->digestion > 0
3312  && random_roll(0, op->contr->digestion, op, PREFER_HIGH))
3313  op->stats.food = last_food;
3314  }
3315  }
3316  op->last_sp = rate_sp/(MAX(gen_sp, 20)+10);
3317  }
3318 
3319  /* Regenerate Grace */
3320  /* I altered this a little - maximum grace is only achieved through prayer -b.t.*/
3321  if (--op->last_grace < 0) {
3322  if (op->stats.grace < op->stats.maxgrace/2)
3323  op->stats.grace++; /* no penalty in food for regaining grace */
3324  op->last_grace = rate_grace/(MAX(gen_grace, 20)+10);
3325  /* wearing stuff doesn't detract from grace generation. */
3326  }
3327 
3328  /* Regenerate Hit Points (unless you are a wraith player) */
3329  if (--op->last_heal < 0 && !is_wraith_pl(op)) {
3330  if (op->stats.hp < op->stats.maxhp) {
3331  op->stats.hp++;
3332  /* dms do not consume food */
3333  if (!QUERY_FLAG(op, FLAG_WIZ)) {
3334  op->stats.food--;
3335  if (op->contr->digestion < 0)
3336  op->stats.food += op->contr->digestion;
3337  else if (op->contr->digestion > 0
3338  && random_roll(0, op->contr->digestion, op, PREFER_HIGH))
3339  op->stats.food = last_food;
3340  }
3341  }
3342  op->last_heal = rate_hp/(MAX(gen_hp, 20)+10);
3343  }
3344 
3345  /* Digestion */
3346  if (--op->last_eat < 0) {
3347  int bonus = MAX(op->contr->digestion, 0);
3348  int penalty = MAX(-op->contr->digestion, 0);
3349  if (op->contr->gen_hp > 0)
3350  op->last_eat = 25*(1+bonus)/(op->contr->gen_hp+penalty+1);
3351  else
3352  op->last_eat = 25*(1+bonus)/(penalty+1);
3353  /* dms do not consume food */
3354  if (!QUERY_FLAG(op, FLAG_WIZ))
3355  op->stats.food--;
3356  }
3357  }
3358 
3359  if (op->contr->state == ST_PLAYING && op->stats.food < 0 && op->stats.hp >= 0) {
3360  if (is_wraith_pl(op))
3361  draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ITEM, MSG_TYPE_ITEM_REMOVE, "You feel a hunger for living flesh.");
3362  /* Only allow eat if not paralyzed. Otherwise our paralyzed player is "moving" to eat.
3363  * Daniel Hawkins 2017-08-23
3364  */
3365  else if (!QUERY_FLAG(op, FLAG_PARALYZED)){
3366  object *flesh = NULL;
3367 
3368  FOR_INV_PREPARE(op, tmp) {
3369  if (!QUERY_FLAG(tmp, FLAG_UNPAID)) {
3370  if (tmp->type == FOOD || tmp->type == DRINK || tmp->type == POISON) {
3372  "You blindly grab for a bite of food.");
3373  apply_manual(op, tmp, 0);
3374  if (op->stats.food >= 0 || op->stats.hp < 0)
3375  break;
3376  } else if (tmp->type == FLESH)
3377  flesh = tmp;
3378  } /* End if paid for object */
3379  } FOR_INV_FINISH(); /* end of for loop */
3380  /* If player is still starving, it means they don't have any food, so
3381  * eat flesh instead.
3382  */
3383  if (op->stats.food < 0 && op->stats.hp >= 0 && flesh) {
3385  "You blindly grab for a bite of food.");
3386  apply_manual(op, flesh, 0);
3387  }
3388  } /* end not wraith and not paralyzed */
3389  else { // Print a message for when the player is starving and paralyzed
3390  draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ITEM, MSG_TYPE_ITEM_REMOVE, "Your stomach rumbles, but you can't reach your food.");
3391  } /* end not wraith and is paralyzed */
3392  } /* end if player is starving */
3393 
3394  if (op->stats.food < 0 && op->stats.hp > 0){
3395  // We know food < 0, so we want the absolute value of food
3396  int32_t adjust_by = MIN(-(op->stats.food), op->stats.hp);
3397  op->stats.food += adjust_by;
3398  op->stats.hp -= adjust_by;
3399  }
3400 
3401  if (!op->contr->state && !QUERY_FLAG(op, FLAG_WIZ) && (op->stats.hp < 0 || op->stats.food < 0))
3402  kill_player(op, NULL);
3403 }
3404 
3411 static void loot_object(object *op) {
3412  object *tmp2;
3413 
3414  if (op->container) { /* close open sack first */
3415  apply_container(op, op->container, AP_NULL);
3416  }
3417 
3418  FOR_INV_PREPARE(op, tmp) {
3419  if (tmp->invisible)
3420  continue;
3421  object_remove(tmp);
3422  tmp->x = op->x,
3423  tmp->y = op->y;
3424  if (tmp->type == CONTAINER) { /* empty container to ground */
3425  loot_object(tmp);
3426  }
3427  if (!QUERY_FLAG(tmp, FLAG_UNIQUE)
3429  if (tmp->nrof > 1) {
3430  tmp2 = object_split(tmp, 1+RANDOM()%(tmp->nrof-1), NULL, 0);
3432  object_insert_in_map_at(tmp, op->map, NULL, 0, op->x, op->y);
3433  } else
3435  } else
3436  object_insert_in_map_at(tmp, op->map, NULL, 0, op->x, op->y);
3437  } FOR_INV_FINISH();
3438 }
3439 
3450 static void restore_player(object *op) {
3451  object *tmp;
3452  archetype *at = find_archetype("poisoning");
3453  if (at != NULL) {
3454  tmp = arch_present_in_ob(at, op);
3455  if (tmp) {
3456  object_remove(tmp);
3459  "Your body feels cleansed");
3460  }
3461  }
3462 
3463  at = find_archetype("confusion");
3464  if (at != NULL) {
3465  tmp = arch_present_in_ob(at, op);
3466  if (tmp) {
3467  object_remove(tmp);
3470  "Your mind feels clearer");
3471  }
3472  }
3473 
3474  cure_disease(op, NULL, NULL); /* remove any disease */
3475 }
3476 
3488 void kill_player(object *op, const object *killer) {
3489  char buf[MAX_BUF];
3490  int x, y;
3491  object *tmp;
3492  archetype *trophy = NULL;
3493 
3494  /* Don't die if the player's life can be saved. */
3495  if (save_life(op)) {
3496  return;
3497  }
3498 
3499  /* If player dies on BATTLEGROUND, no stat/exp loss! For Combat-Arenas
3500  * in cities ONLY!!! It is very important that this doesn't get abused.
3501  * Look at op_on_battleground() for more info --AndreasV
3502  */
3503  if (op_on_battleground(op, &x, &y, &trophy)) {
3504  assert(trophy != NULL);
3506  "You have been defeated in combat!\n"
3507  "Local medics have saved your life...");
3508 
3509  /* restore player */
3510  restore_player(op);
3511 
3512  op->stats.hp = op->stats.maxhp;
3513  if (op->stats.food <= 0)
3514  op->stats.food = MAX_FOOD;
3515 
3516  /* create a bodypart-trophy to make the winner happy */
3517  tmp = arch_to_object(trophy);
3518  if (tmp != NULL) {
3519  snprintf(buf, sizeof(buf), "%s's %s", op->name, tmp->name);
3520  tmp->name = add_string(buf);
3521 
3522  snprintf(buf, sizeof(buf),
3523  "This %s was %s %s the %s, who was defeated at level %d by %s.\n",
3524  tmp->name, tmp->type == FLESH ? "cut off" : "taken from",
3525  op->name, op->contr->title,
3526  (int)(op->level), op->contr->killer);
3527 
3529  tmp->type = 0;
3530  tmp->value = 0;
3531  tmp->material = 0;
3532  tmp->materialname = NULL;
3533  object_insert_in_map_at(tmp, op->map, op, 0, op->x, op->y);
3534  }
3535 
3536  /* teleport defeated player to new destination*/
3537  transfer_ob(op, x, y, 0, NULL);
3538  op->contr->braced = 0;
3539  return;
3540  }
3541 
3542  if (events_execute_object_event(op, EVENT_DEATH, NULL, NULL, NULL, SCRIPT_FIX_ALL) != 0)
3543  return;
3544 
3546  if (op->stats.food < 0) {
3547  snprintf(buf, sizeof(buf), "%s starved to death.", op->name);
3548  strcpy(op->contr->killer, "starvation");
3549  } else {
3550  snprintf(buf, sizeof(buf), "%s died.", op->name);
3551  }
3552  play_sound_player_only(op->contr, SOUND_TYPE_LIVING, op, 0, "death");
3553 
3554  if (settings.not_permadeth == TRUE) {
3556  } else {
3558  }
3559 }
3560 
3569 static void kill_player_not_permadeath(object *op) {
3570  int num_stats_lose;
3571  int will_kill_again;
3572  int lost_a_stat;
3573  int z;
3574  object *tmp;
3575  char buf[MAX_BUF];
3576  archetype *at;
3577 
3578  /* Basically two ways to go - remove a stat permanently, or just
3579  * make it depletion. This bunch of code deals with that aspect
3580  * of death.
3581  */
3583  /* If stat loss is permanent, lose one stat only. */
3584  /* Lower level chars don't lose as many stats because they suffer
3585  more if they do. */
3586  /* Higher level characters can afford things such as potions of
3587  restoration, or better, stat potions. So we slug them that
3588  little bit harder. */
3589  /* GD */
3591  num_stats_lose = 1;
3592  else
3593  num_stats_lose = 1+op->level/BALSL_NUMBER_LOSSES_RATIO;
3594  } else {
3595  num_stats_lose = 1;
3596  }
3597  lost_a_stat = 0;
3598 
3599  for (z = 0; z < num_stats_lose; z++) {
3601  int i;
3602 
3603  /* Pick a random stat and take a point off it. Tell the player
3604  * what he lost.
3605  */
3606  i = RANDOM()%7;
3607  change_attr_value(&(op->stats), i, -1);
3609  change_attr_value(&(op->contr->orig_stats), i, -1);
3610  check_stat_bounds(&(op->contr->orig_stats), MIN_STAT, settings.max_stat);
3613  lose_msg[i]);
3614  lost_a_stat = 1;
3615  } else {
3616  /* deplete a stat */
3618  if (deparch == NULL) {
3619  continue;
3620  }
3621  object *dep;
3622  int lose_this_stat;
3623  int i;
3624 
3625  i = RANDOM()%7;
3626  dep = arch_present_in_ob(deparch, op);
3627  if (!dep) {
3628  dep = arch_to_object(deparch);
3629  object_insert_in_ob(dep, op);
3630  }
3631  lose_this_stat = 1;
3633  int this_stat;
3634 
3635  /* GD */
3636  /* Get the stat that we're about to deplete. */
3637  this_stat = get_attr_value(&(dep->stats), i);
3638  if (this_stat < 0) {
3639  int loss_chance = 1+op->level/BALSL_LOSS_CHANCE_RATIO;
3640  int keep_chance = this_stat*this_stat;
3641  /* Yes, I am paranoid. Sue me. */
3642  if (keep_chance < 1)
3643  keep_chance = 1;
3644 
3645  /* There is a maximum depletion total per level. */
3646  if (this_stat < -1-op->level/BALSL_MAX_LOSS_RATIO) {
3647  lose_this_stat = 0;
3648  /* Take loss chance vs keep chance to see if we
3649  retain the stat. */
3650  } else {
3651  if (random_roll(0, loss_chance+keep_chance-1, op, PREFER_LOW) < keep_chance)
3652  lose_this_stat = 0;
3653  /* LOG(llevDebug, "Determining stat loss. Stat: %d Keep: %d Lose: %d Result: %s.\n", this_stat, keep_chance, loss_chance, lose_this_stat ? "LOSE" : "KEEP"); */
3654  }
3655  }
3656  }
3657 
3658  if (lose_this_stat) {
3659  int this_stat;
3660 
3661  this_stat = get_attr_value(&(dep->stats), i);
3662  /* We could try to do something clever like find another
3663  * stat to reduce if this fails. But chances are, if
3664  * stats have been depleted to -50, all are pretty low
3665  * and should be roughly the same, so it shouldn't make a
3666  * difference.
3667  */
3668  if (this_stat >= -50) {
3669  change_attr_value(&(dep->stats), i, -1);
3670  SET_FLAG(dep, FLAG_APPLIED);
3672  drain_msg[i]);
3673  fix_object(op);
3674  lost_a_stat = 1;
3675  }
3676  }
3677  }
3678  }
3679  /* If no stat lost, tell the player. */
3680  if (!lost_a_stat) {
3681  /* determine_god() seems to not work sometimes... why is this? Should I be using something else? GD */
3682  const char *god = determine_god(op);
3683 
3684  if (god && (strcmp(god, "none")))
3687  "For a brief moment you feel the holy presence of %s protecting you",
3688  god);
3689  else
3692  "For a brief moment you feel a holy presence protecting you.");
3693  }
3694 
3695  /* Put a gravestone up where the character 'almost' died. List the
3696  * exp loss on the stone.
3697  */
3698  at = find_archetype("gravestone");
3699  if (at != NULL) {
3700  tmp = arch_to_object(at);
3701  snprintf(buf, sizeof(buf), "%s's gravestone", op->name);
3702  FREE_AND_COPY(tmp->name, buf);
3703  snprintf(buf, sizeof(buf), "%s's gravestones", op->name);
3704  FREE_AND_COPY(tmp->name_pl, buf);
3705  snprintf(buf, sizeof(buf), "RIP\nHere rests the hero %s the %s,\n"
3706  "who was killed\n"
3707  "by %s.\n",
3708  op->name, op->contr->title,
3709  op->contr->killer);
3711  object_insert_in_map_at(tmp, op->map, NULL, 0, op->x, op->y);
3712  }
3713 
3714  /* restore player: remove any poisoning, disease and confusion the
3715  * character may be suffering.*/
3716  restore_player(op);
3717 
3718  /* Subtract the experience points, if we died cause of food, give
3719  * us food, and reset HP's...
3720  */
3722  if (op->stats.food < 100)
3723  op->stats.food = 900;
3724  op->stats.hp = op->stats.maxhp;
3725  op->stats.sp = MAX(op->stats.sp, op->stats.maxsp);
3726  op->stats.grace = MAX(op->stats.grace, op->stats.maxgrace);
3727 
3728  /* Check to see if the player is in a shop. IF so, then check to see if
3729  * the player has any unpaid items. If so, remove them and put them back
3730  * in the map.
3731  *
3732  * If they are not in a shop, just free the unpaid items instead of
3733  * putting them back on map.
3734  */
3735  if (shop_contains(op))
3736  remove_unpaid_objects(op->inv, op, 0);
3737  else
3738  remove_unpaid_objects(op->inv, op, 1);
3739 
3740  /* Move player to his current respawn-position (usually last savebed) */
3742 
3743  /* Save the player before inserting the force to reduce chance of abuse. */
3744  op->contr->braced = 0;
3745  /* don't pick up in apartment */
3746  if (op->contr->mode & PU_NEWMODE) {
3747  op->contr->mode = op->contr->mode | PU_INHIBIT;
3748  esrv_send_pickup(op->contr);
3749  } else {
3750  op->contr->mode = 0;
3751  }
3752  if ( op->contr->search_str[0] ) command_search_items(op,NULL); /* turn off search-items */
3753  save_player(op, 1);
3754 
3755  /* it is possible that the player has blown something up
3756  * at his savebed location, and that can have long lasting
3757  * spell effects. So first see if there is a spell effect
3758  * on the space that might harm the player.
3759  */
3760  will_kill_again = 0;
3761  FOR_MAP_PREPARE(op->map, op->x, op->y, tmp)
3762  if (tmp->type == SPELL_EFFECT)
3763  will_kill_again |= tmp->attacktype;
3764  FOR_MAP_FINISH();
3765  if (will_kill_again) {
3766  object *force;
3767  int at;
3768 
3770  /* 50 ticks should be enough time for the spell to abate */
3771  force->speed = 0.1;
3772  force->speed_left = -5.0;
3774  for (at = 0; at < NROFATTACKS; at++) {
3775  if (will_kill_again&(1<<at))
3776  force->resist[at] = 100;
3777  }
3779  fix_object(op);
3780  }
3781 
3782  /* Tell the player they have died */
3784  "YOU HAVE DIED.");
3785 }
3786 
3793 static void kill_player_permadeath(object *op) {
3794  char buf[MAX_BUF];
3795  char ac_buf[MAX_BUF];
3796  int x, y;
3797  mapstruct *map;
3798  object *tmp;
3799  archetype *at;
3800 
3801  /* save the map location for corpse, gravestone*/
3802  x = op->x;
3803  y = op->y;
3804  map = op->map;
3805 
3806  party_leave(op);
3807  if (settings.set_title == TRUE)
3808  player_set_own_title(op->contr, "");
3809 
3810  /* buf should be the kill message */
3812  buf);
3813  hiscore_check(op, 0);
3814  if (op->contr->ranges[range_golem] != NULL) {
3815  remove_friendly_object(op->contr->ranges[range_golem]);
3816  object_remove(op->contr->ranges[range_golem]);
3817  object_free_drop_inventory(op->contr->ranges[range_golem]);
3818  op->contr->ranges[range_golem] = NULL;
3819  op->contr->golem_count = 0;
3820  }
3821  loot_object(op); /* Remove some of the items for good */
3822  object_remove(op);
3823  op->direction = 0;
3824 
3825  if (!QUERY_FLAG(op, FLAG_WAS_WIZ) && op->stats.exp) {
3826  if (settings.resurrection == TRUE) {
3827  /* save playerfile sans equipment when player dies
3828  * -then save it as player.pl.dead so that future resurrection
3829  * -type spells will work on them nicely
3830  */
3831  op->stats.hp = op->stats.maxhp;
3832  op->stats.food = MAX_FOOD;
3833 
3834  /* set the location of where the person will reappear when */
3835  /* maybe resurrection code should fix map also */
3836  safe_strncpy(op->contr->maplevel, settings.emergency_mapname,
3837  sizeof(op->contr->maplevel));
3838  if (op->map != NULL)
3839  op->map = NULL;
3840  op->x = settings.emergency_x;
3841  op->y = settings.emergency_y;
3842  save_player(op, 0);
3843  op->map = map;
3844  /* please see resurrection.c: peterm */
3845  dead_player(op);
3846  } else {
3847  delete_character(op->name);
3848 
3849  /* Remove player from account list and send back data if needed */
3850  if (op->contr->socket->account_chars != NULL) {
3851  account_char_remove(op->contr->socket->account_chars, op->name);
3852  account_char_save(op->contr->socket->account_chars);
3853  /* char information is reloaded in send_account_players below */
3854  account_char_free(op->contr->socket->account_chars);
3855  op->contr->socket->account_chars = NULL;
3856  account_remove_player(op->contr->socket->account_name, op->name);
3857  send_account_players(op->contr->socket);
3858  }
3859  }
3860  }
3861  play_again(op);
3862 
3863  /* peterm: added to create a corpse at deathsite. */
3864  at = find_archetype("corpse_pl");
3865  if (at != NULL) {
3866  tmp = arch_to_object(at);
3867  snprintf(buf, sizeof(buf), "%s", op->name);
3868  FREE_AND_COPY(tmp->name, buf);
3869  FREE_AND_COPY(tmp->name_pl, buf);
3870  tmp->level = op->level;
3871  object_set_msg(tmp, gravestone_text(op, buf, sizeof(buf)));
3873  /*
3874  * Put the account name under slaying.
3875  * Does not seem to cause weird effects, but more testing may ensure this.
3876  */
3877  snprintf(ac_buf, sizeof(ac_buf), "%s", op->contr->socket->account_name);
3878  FREE_AND_COPY(tmp->slaying, ac_buf);
3879  object_insert_in_map_at(tmp, map, NULL, 0, x, y);
3880  }
3881 }
3882 
3890 void fix_weight(void) {
3891  player *pl;
3892 
3893  for (pl = first_player; pl != NULL; pl = pl->next) {
3894  int old = pl->ob->carrying, sum = object_sum_weight(pl->ob);
3895 
3896  if (old == sum)
3897  continue;
3898  fix_object(pl->ob);
3899  LOG(llevDebug, "Fixed inventory in %s (%d -> %d)\n", pl->ob->name, old, sum);
3900  }
3901 }
3902 
3906 void fix_luck(void) {
3907  player *pl;
3908 
3909  for (pl = first_player; pl != NULL; pl = pl->next)
3910  if (!pl->ob->contr->state)
3911  change_luck(pl->ob, 0);
3912 }
3913 
3914 
3927 void cast_dust(object *op, object *throw_ob, int dir) {
3928  object *skop, *spob;
3929 
3930  skop = find_skill_by_name(op, throw_ob->skill);
3931 
3932  /* casting POTION 'dusts' is really a use_magic_item skill */
3933  if (op->type == PLAYER && throw_ob->type == POTION && !skop) {
3934  LOG(llevError, "Player %s lacks critical skill use_magic_item!\n", op->name);
3935  return;
3936  }
3937  spob = throw_ob->inv;
3938  if (op->type == PLAYER && spob)
3940  "You cast %s.",
3941  spob->name);
3942 
3943  cast_spell(op, throw_ob, dir, spob, NULL);
3944 
3945  if (!QUERY_FLAG(throw_ob, FLAG_REMOVED))
3946  object_remove(throw_ob);
3947  object_free_drop_inventory(throw_ob);
3948 }
3949 
3956 void make_visible(object *op) {
3957  op->hide = 0;
3958  op->invisible = 0;
3959  if (op->type == PLAYER) {
3960  op->contr->tmp_invis = 0;
3961  if (op->contr->invis_race)
3962  FREE_AND_CLEAR_STR(op->contr->invis_race);
3963  }
3965 }
3966 
3976 int is_true_undead(object *op) {
3977  if (QUERY_FLAG(&op->arch->clone, FLAG_UNDEAD))
3978  return 1;
3979 
3980  if (op->type == PLAYER) {
3981  FOR_INV_PREPARE(op, tmp) {
3982  if (tmp->type == 44 && tmp->stats.Wis)
3983  if (QUERY_FLAG(tmp, FLAG_UNDEAD))
3984  return 1;
3985  } FOR_INV_FINISH();
3986  }
3987  return 0;
3988 }
3989 
4000 int hideability(object *ob) {
4001  int i, level = 0, mflag;
4002  int16_t x, y;
4003 
4004  if (!ob || !ob->map)
4005  return 0;
4006 
4007  /* so, on normal lighted maps, its hard to hide */
4008  level = ob->map->darkness-2;
4009 
4010  /* this also picks up whether the object is glowing.
4011  * If you carry a light on a non-dark map, its not
4012  * as bad as carrying a light on a pitch dark map
4013  */
4014  if (has_carried_lights(ob))
4015  level = -(10+(2*ob->map->darkness));
4016 
4017  /* scan through all nearby squares for terrain to hide in */
4018  for (i = 0, x = ob->x, y = ob->y; i < 9; i++, x = ob->x+freearr_x[i], y = ob->y+freearr_y[i]) {
4019  mflag = get_map_flags(ob->map, NULL, x, y, NULL, NULL);
4020  if (mflag&P_OUT_OF_MAP) {
4021  continue;
4022  }
4023  if (mflag&P_BLOCKSVIEW) /* something to hide near! */
4024  level += 2;
4025  else /* open terrain! */
4026  level -= 1;
4027  }
4028 
4029  return level;
4030 }
4031 
4041 void do_hidden_move(object *op) {
4042  int hide = 0, num = random_roll(0, 19, op, PREFER_LOW);
4043  object *skop;
4044 
4045  if (!op || !op->map)
4046  return;
4047 
4049 
4050  /* its *extremely *hard to run and sneak/hide at the same time! */
4051  if (op->type == PLAYER && op->contr->run_on) {
4052  if (!skop || num >= skop->level) {
4054  "You ran too much! You are no longer hidden!");
4055  make_visible(op);
4056  return;
4057  } else
4058  num += 20;
4059  }
4060  num += op->map->difficulty;
4061  hide = hideability(op); /* modify by terrain hidden level */
4062  num -= hide;
4063  if ((op->type == PLAYER && hide < -10)
4064  || ((op->invisible -= num) <= 0)) {
4065  make_visible(op);
4066  if (op->type == PLAYER)
4068  "You moved out of hiding! You are visible!");
4069  } else if (op->type == PLAYER && skop) {
4070  change_exp(op, calc_skill_exp(op, NULL, skop), skop->skill, 0);
4071  }
4072 }
4073 
4082 int stand_near_hostile(object *who) {
4083  int i, friendly = 0, player = 0, mflags;
4084  mapstruct *m;
4085  int16_t x, y;
4086 
4087  if (!who)
4088  return 0;
4089 
4090  if (who->type == PLAYER)
4091  player = 1;
4092  else
4094 
4095  /* search adjacent squares */
4096  for (i = 1; i < 9; i++) {
4097  x = who->x+freearr_x[i];
4098  y = who->y+freearr_y[i];
4099  m = who->map;
4100  mflags = get_map_flags(m, &m, x, y, &x, &y);
4101  /* space must be blocked if there is a monster. If not
4102  * blocked, don't need to check this space.
4103  */
4104  if (mflags&P_OUT_OF_MAP)
4105  continue;
4107  continue;
4108 
4109  FOR_MAP_PREPARE(m, x, y, tmp) {
4110  if ((player || friendly)
4113  return 1;
4114  else if (tmp->type == PLAYER) {
4115  /*don't let a hidden DM prevent you from hiding*/
4116  if (!QUERY_FLAG(tmp, FLAG_WIZ) || tmp->contr->hidden == 0)
4117  return 1;
4118  }
4119  } FOR_MAP_FINISH();
4120  }
4121  return 0;
4122 }
4123 
4150 int player_can_view(object *pl, object *op) {
4151  rv_vector rv;
4152  int dx, dy;
4153 
4154  if (!pl || !op)
4155  return 0;
4156 
4157  if (pl->type != PLAYER) {
4158  LOG(llevError, "player_can_view() called for non-player object\n");
4159  return -1;
4160  }
4161 
4162  op = HEAD(op);
4163  if (!get_rangevector(pl, op, &rv, 0x1))
4164  return 0;
4165 
4166  /* starting with the 'head' part, lets loop
4167  * through the object and find if it has any
4168  * part that is in the los array but isnt on
4169  * a blocked los square.
4170  * we use the archetype to figure out offsets.
4171  */
4172  while (op) {
4173  dx = rv.distance_x+op->arch->clone.x;
4174  dy = rv.distance_y+op->arch->clone.y;
4175 
4176  /* only the viewable area the player sees is updated by LOS
4177  * code, so we need to restrict ourselves to that range of values
4178  * for any meaningful values.
4179  */
4180  if (FABS(dx) <= (pl->contr->socket->mapx/2)
4181  && FABS(dy) <= (pl->contr->socket->mapy/2)
4182  && !pl->contr->blocked_los[dx+(pl->contr->socket->mapx/2)][dy+(pl->contr->socket->mapy/2)])
4183  return 1;
4184  op = op->more;
4185  }
4186  return 0;
4187 }
4188 
4202 static int action_makes_visible(object *op) {
4203  if (op->invisible && QUERY_FLAG(op, FLAG_ALIVE)) {
4205  return 0;
4206 
4207  if (op->contr && op->contr->tmp_invis == 0)
4208  return 0;
4209 
4210  /* If monsters, they should become visible */
4211  if (op->hide || !op->contr || (op->contr && op->contr->tmp_invis)) {
4213  "You become %s!",
4214  op->hide ? "unhidden" : "visible");
4215  return 1;
4216  }
4217  }
4218  return 0;
4219 }
4220 
4249 int op_on_battleground(object *op, int *x, int *y, archetype **trophy) {
4251  if (QUERY_FLAG(tmp, FLAG_IS_FLOOR)) {
4253  && strcmp(tmp->name, "battleground") == 0
4254  && tmp->type == BATTLEGROUND
4255  && EXIT_X(tmp)
4256  && EXIT_Y(tmp)) {
4257  /*before we assign the exit, check if this is a teambattle*/
4258  if (EXIT_ALT_X(tmp) && EXIT_ALT_Y(tmp) && EXIT_PATH(tmp)) {
4259  object *invtmp;
4260 
4262  if (invtmp != NULL) {
4263  if (x != NULL && y != NULL)
4264  *x = EXIT_ALT_X(tmp),
4265  *y = EXIT_ALT_Y(tmp);
4266  return 1;
4267  }
4268  }
4269  if (x != NULL && y != NULL)
4270  *x = EXIT_X(tmp),
4271  *y = EXIT_Y(tmp);
4272 
4273  /* If 'other_arch' is not specified, give a finger. */
4274  if (trophy != NULL) {
4275  if (tmp->other_arch) {
4276  *trophy = tmp->other_arch;
4277  } else {
4278  *trophy = find_archetype("finger");
4279  }
4280  }
4281  return 1;
4282  }
4283  }
4284  } FOR_BELOW_FINISH();
4285  /* If we got here, did not find a battleground */
4286  return 0;
4287 }
4288 
4299 void dragon_ability_gain(object *who, int atnr, int level) {
4300  treasurelist *trlist = NULL; /* treasurelist */
4301  treasure *tr; /* treasure */
4302  object *tmp, *skop; /* tmp. object */
4303  object *item; /* treasure object */
4304  char buf[MAX_BUF]; /* tmp. string buffer */
4305  int i = 0, j = 0;
4306 
4307  /* get the appropriate treasurelist */
4308  if (atnr == ATNR_FIRE)
4309  trlist = find_treasurelist("dragon_ability_fire");
4310  else if (atnr == ATNR_COLD)
4311  trlist = find_treasurelist("dragon_ability_cold");
4312  else if (atnr == ATNR_ELECTRICITY)
4313  trlist = find_treasurelist("dragon_ability_elec");
4314  else if (atnr == ATNR_POISON)
4315  trlist = find_treasurelist("dragon_ability_poison");
4316 
4317  if (trlist == NULL || who->type != PLAYER)
4318  return;
4319 
4320  // tr->magic is being used to define what level of the metabolism the ability is gained at.
4321  for (tr = trlist->items; tr != NULL && tr->magic != level; tr = tr->next)
4322  ;
4323  if (tr == NULL || tr->item == NULL) {
4324  /* LOG(llevDebug, "-> no more treasure for %s\n", change_resist_msg[atnr]); */
4325  return;
4326  }
4327 
4328  /* everything seems okay - now bring on the gift: */
4329  item = &(tr->item->clone);
4330 
4331  if (item->type == SPELL) {
4332  if (check_spell_known(who, item->name))
4333  return;
4334 
4337  "You gained the ability of %s",
4338  item->name);
4339  do_learn_spell(who, item, 0);
4340  return;
4341  }
4342 
4343  /* grant direct spell */
4344  if (item->type == SPELLBOOK) {
4345  if (!item->inv) {
4346  LOG(llevDebug, "dragon_ability_gain: Broken spellbook %s\n", item->name);
4347  return;
4348  }
4349  if (check_spell_known(who, item->inv->name))
4350  return;
4351  if (item->invisible) {
4354  "You gained the ability of %s",
4355  item->inv->name);
4356  do_learn_spell(who, item->inv, 0);
4357  return;
4358  }
4359  } else if (item->type == SKILL_TOOL && item->invisible) {
4360  if (item->subtype == SK_CLAWING && (skop = find_skill_by_name(who, item->skill)) != NULL) {
4361  /* should this perhaps be (skop->attackyp&item->attacktype) != item->attacktype ...
4362  * in this way, if the player is missing any of the attacktypes, he gets
4363  * them. As it is now, if the player has any that match the granted skill,
4364  * but not all of them, he gets nothing.
4365  */
4366  if (!(skop->attacktype&item->attacktype)) {
4367  /* Give new attacktype */
4368  skop->attacktype |= item->attacktype;
4369 
4370  /* always add physical if there's none */
4371  skop->attacktype |= AT_PHYSICAL;
4372 
4373  if (item->msg != NULL)
4376  item->msg);
4377 
4378  /* Give player new face */
4379  if (item->animation) {
4380  who->face = skop->face;
4381  who->animation = item->animation;
4382  who->anim_speed = item->anim_speed;
4383  who->last_anim = 0;
4384  who->state = 0;
4385  animate_object(who, who->direction);
4386  }
4387  }
4388  }
4389  } else if (item->type == FORCE) {
4390  /* forces in the treasurelist can alter the player's stats */
4391  object *skin;
4392 
4393  /* first get the dragon skin force */
4394  skin = object_find_by_arch_name(who, "dragon_skin_force");
4395  if (skin == NULL)
4396  return;
4397 
4398  /* adding new spellpath attunements */
4399  if (item->path_attuned > 0 && !(skin->path_attuned&item->path_attuned)) {
4400  skin->path_attuned |= item->path_attuned; /* add attunement to skin */
4401 
4402  /* print message */
4403  snprintf(buf, sizeof(buf), "You feel attuned to ");
4404  for (i = 0, j = 0; i < NRSPELLPATHS; i++) {
4405  if (item->path_attuned&(1<<i)) {
4406  if (j)
4407  strcat(buf, " and ");
4408  else
4409  j = 1;
4410  strcat(buf, spellpathnames[i]);
4411  }
4412  }
4413  strcat(buf, ".");
4416  buf);
4417  }
4418 
4419  /* evtl. adding flags: */
4420  if (QUERY_FLAG(item, FLAG_XRAYS))
4421  SET_FLAG(skin, FLAG_XRAYS);
4423  SET_FLAG(skin, FLAG_STEALTH);
4425  SET_FLAG(skin, FLAG_SEE_IN_DARK);
4426 
4427  /* print message if there is one */
4428  if (item->msg != NULL)
4431  item->msg);
4432  } else {
4433  /* generate misc. treasure */
4434  char name[HUGE_BUF];
4435 
4436  tmp = arch_to_object(tr->item);
4440  "You gained %s",
4441  name);
4443  if (who->type == PLAYER)
4445  }
4446 }
4447 
4458  int i;
4459 
4460  for (i = 0; i < static_cast<int>(range_size); i++) {
4461  if (pl->ranges[i] == ob) {
4462  pl->ranges[i] = NULL;
4463  if (pl->shoottype == i) {
4464  pl->shoottype = range_none;
4465  }
4466  }
4467  }
4468 }
4469 
4475 void player_set_state(player *pl, uint8_t state) {
4476 
4477  assert(pl);
4478  assert(state <= ST_CHANGE_PASSWORD_CONFIRM);
4479  pl->state = state;
4480 }
4481 
4489  SockList *sl;
4490  assert(pl);
4491  if (pl->delayed_buffers_used == pl->delayed_buffers_allocated) {
4492  pl->delayed_buffers_allocated += 5;
4493  pl->delayed_buffers = static_cast<SockList **>(realloc(pl->delayed_buffers, pl->delayed_buffers_allocated * sizeof(pl->delayed_buffers[0])));
4494  if (!pl->delayed_buffers) {
4495  LOG(llevError, "Unable to allocated %d delayed buffers, aborting\n", pl->delayed_buffers_allocated);
4497  }
4498  for (uint8_t s = pl->delayed_buffers_allocated - 5; s < pl->delayed_buffers_allocated; s++) {
4499  pl->delayed_buffers[s] = static_cast<SockList *>(calloc(1, sizeof(SockList)));
4500  }
4501  }
4502  sl = pl->delayed_buffers[pl->delayed_buffers_used];
4503  pl->delayed_buffers_used++;
4504  SockList_Init(sl);
4505  return sl;
4506 }
object_was_destroyed
#define object_was_destroyed(op, old_tag)
Definition: object.h:70
ADD_PLAYER_NO_STATS_ROLL
#define ADD_PLAYER_NO_STATS_ROLL
Definition: player.h:247
object_value_set
bool object_value_set(const object *op, const char *const key)
Definition: object.cpp:4367
op_on_battleground
int op_on_battleground(object *op, int *x, int *y, archetype **trophy)
Definition: player.cpp:4249
give.next
def next
Definition: give.py:44
GET_MAP_OB
#define GET_MAP_OB(M, X, Y)
Definition: map.h:171
MSG_TYPE_COMMAND_NEWPLAYER
#define MSG_TYPE_COMMAND_NEWPLAYER
Definition: newclient.h:525
do_skill
int do_skill(object *op, object *part, object *skill, int dir, const char *string)
Definition: skill_util.cpp:431
player::do_los
uint32_t do_los
Definition: player.h:141
apply_changes_to_player
void apply_changes_to_player(object *pl, object *change, int limit_stats)
Definition: apply.cpp:1672
living::exp
int64_t exp
Definition: living.h:47
UP_OBJ_FACE
#define UP_OBJ_FACE
Definition: object.h:524
check_pick
int check_pick(object *op)
Definition: player.cpp:1717
make_visible
void make_visible(object *op)
Definition: player.cpp:3956
PLAYER
@ PLAYER
Definition: object.h:112
PU_BOOTS
#define PU_BOOTS
Definition: define.h:125
object_get_owner
object * object_get_owner(object *op)
Definition: object.cpp:804
kill_player_permadeath
static void kill_player_permadeath(object *op)
Definition: player.cpp:3793
SockList_AddInt
void SockList_AddInt(SockList *sl, uint32_t data)
Definition: lowlevel.cpp:124
global.h
FLAG_NEUTRAL
#define FLAG_NEUTRAL
Definition: define.h:354
UPD_FACE
#define UPD_FACE
Definition: newclient.h:306
object_find_by_type_subtype
object * object_find_by_type_subtype(const object *who, int type, int subtype)
Definition: object.cpp:4292
first_player
player * first_player
Definition: init.cpp:106
settings
struct Settings settings
Definition: init.cpp:139
SOUND_TYPE_ITEM
#define SOUND_TYPE_ITEM
Definition: newclient.h:324
bow_spreadshot
@ bow_spreadshot
Definition: player.h:44
friendly
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 friendly
Definition: survival-guide.txt:38
object_update_turn_face
void object_update_turn_face(object *op)
Definition: object.cpp:1327
safe_strncpy
#define safe_strncpy
Definition: compat.h:27
FOR_MAP_FINISH
#define FOR_MAP_FINISH()
Definition: define.h:730
remove_friendly_object
void remove_friendly_object(object *op)
Definition: friend.cpp:52
pets_terminate_all
void pets_terminate_all(object *owner)
Definition: pets.cpp:225
bow_normal
@ bow_normal
Definition: player.h:42
face_player
int face_player(object *op, int dir)
Definition: player.cpp:3027
options
static struct Command_Line_Options options[]
Definition: init.cpp:376
ST_GET_PASSWORD
#define ST_GET_PASSWORD
Definition: define.h:547
MSG_TYPE_COMMAND_SUCCESS
#define MSG_TYPE_COMMAND_SUCCESS
Definition: newclient.h:519
FLAG_CONFUSED
#define FLAG_CONFUSED
Definition: define.h:311
player::unarmed_skill
const char * unarmed_skill
Definition: player.h:221
BOW
@ BOW
Definition: object.h:123
player_fire_bow
static int player_fire_bow(object *op, int dir)
Definition: player.cpp:2308
kill_player
void kill_player(object *op, const object *killer)
Definition: player.cpp:3488
llevError
@ llevError
Definition: logger.h:11
get_randomized_dir
int get_randomized_dir(int dir)
Definition: utils.cpp:412
FABS
#define FABS(x)
Definition: define.h:22
MOVE_ALL
#define MOVE_ALL
Definition: define.h:398
object::path_attuned
uint32_t path_attuned
Definition: object.h:353
command_search_items
void command_search_items(object *op, const char *params)
Definition: c_object.cpp:2477
WAND
@ WAND
Definition: object.h:225
range_bow
@ range_bow
Definition: player.h:31
get_player_archetype
static archetype * get_player_archetype(archetype *at)
Definition: player.cpp:505
MSG_TYPE_ADMIN_PLAYER
#define MSG_TYPE_ADMIN_PLAYER
Definition: newclient.h:485
LOG
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.cpp:51
FLAG_UNDEAD
#define FLAG_UNDEAD
Definition: define.h:270
SET_FLAG
#define SET_FLAG(xyz, p)
Definition: define.h:224
bow_threewide
@ bow_threewide
Definition: player.h:43
FLESH
@ FLESH
Definition: object.h:192
monster_can_detect_enemy
int monster_can_detect_enemy(object *op, object *enemy, rv_vector *rv)
Definition: monster.cpp:2583
PU_ARROW
#define PU_ARROW
Definition: define.h:120
PU_NOT_CURSED
#define PU_NOT_CURSED
Definition: define.h:140
player
Definition: player.h:105
path_to_player
int path_to_player(object *mon, object *pl, unsigned mindiff)
Definition: player.cpp:651
esrv_map_scroll
void esrv_map_scroll(socket_struct *ns, int dx, int dy)
Definition: request.cpp:1668
blocked_link
int blocked_link(object *ob, mapstruct *m, int16_t sx, int16_t sy)
Definition: map.cpp:343
strdup_local
#define strdup_local
Definition: compat.h:29
object::inv
object * inv
Definition: object.h:298
recursive_roll
void recursive_roll(object *op, int dir, object *pusher)
Definition: move.cpp:293
safe_strcat
void safe_strcat(char *dest, const char *orig, size_t *curlen, size_t maxlen)
Definition: porting.cpp:202
diamondslots.x
x
Definition: diamondslots.py:15
FLAG_STARTEQUIP
#define FLAG_STARTEQUIP
Definition: define.h:268
first_map_path
char first_map_path[MAX_BUF]
Definition: init.cpp:120
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
treasurelist::items
treasure * items
Definition: treasure.h:92
remove_locked_door
void remove_locked_door(object *op)
Definition: time.cpp:64
spellpathnames
const char *const spellpathnames[NRSPELLPATHS]
Definition: init.cpp:239
SK_CLAWING
@ SK_CLAWING
Definition: skills.h:50
ARCH_DEPLETION
#define ARCH_DEPLETION
Definition: object.h:581
NDI_GREEN
#define NDI_GREEN
Definition: newclient.h:238
PU_INHIBIT
#define PU_INHIBIT
Definition: define.h:109
do_learn_spell
void do_learn_spell(object *op, object *spell, int special_prayer)
Definition: apply.cpp:484
socket_struct
Definition: newserver.h:89
Settings::search_items
uint8_t search_items
Definition: global.h:267
ST_GET_NAME
#define ST_GET_NAME
Definition: define.h:546
first_map_ext_path
char first_map_ext_path[MAX_BUF]
Definition: init.cpp:121
esrv_new_player
void esrv_new_player(player *pl, uint32_t weight)
Definition: request.cpp:991
FLAG_SEE_IN_DARK
#define FLAG_SEE_IN_DARK
Definition: define.h:337
price_base
uint64_t price_base(const object *obj)
Definition: shop.cpp:75
player_get_delayed_buffer
SockList * player_get_delayed_buffer(player *pl)
Definition: player.cpp:4488
get_friends_of
objectlink * get_friends_of(const object *owner)
Definition: friend.cpp:117
esrv_send_inventory
void esrv_send_inventory(object *pl, object *op)
Definition: item.cpp:316
roll_again
void roll_again(object *op)
Definition: player.cpp:1141
find_player_socket
player * find_player_socket(const socket_struct *ns)
Definition: player.cpp:123
AT_PHYSICAL
#define AT_PHYSICAL
Definition: attack.h:76
object::arch
struct archetype * arch
Definition: object.h:422
cast_spell
int cast_spell(object *op, object *caster, int dir, object *spell_ob, char *stringarg)
Definition: spell_util.cpp:1424
find_better_arrow
static object * find_better_arrow(object *op, object *target, const char *type, int *better)
Definition: player.cpp:1954
absdir
int absdir(int d)
Definition: object.cpp:3705
player::no_shout
uint32_t no_shout
Definition: player.h:148
SockList_AddString
void SockList_AddString(SockList *sl, const char *data)
Definition: lowlevel.cpp:154
FOR_BELOW_PREPARE
#define FOR_BELOW_PREPARE(op_, it_)
Definition: define.h:704
object::speed
float speed
Definition: object.h:337
object_set_enemy
void object_set_enemy(object *op, object *enemy)
Definition: object.cpp:915
GEM
@ GEM
Definition: object.h:172
FLAG_UNIQUE
#define FLAG_UNIQUE
Definition: define.h:287
MSG_TYPE_VICTIM_DIED
#define MSG_TYPE_VICTIM_DIED
Definition: newclient.h:645
EXIT_PATH
#define EXIT_PATH(xyz)
Definition: define.h:439
living::Dex
int8_t Dex
Definition: living.h:36
TRAP
@ TRAP
Definition: object.h:246
object::x
int16_t x
Definition: object.h:335
player::peaceful
uint32_t peaceful
Definition: player.h:146
player::ob
object * ob
Definition: player.h:177
turn_transport
static int turn_transport(object *transport, object *captain, int dir)
Definition: player.cpp:2915
spring_trap
void spring_trap(object *trap, object *victim)
Definition: rune.cpp:205
get_nearest_player
object * get_nearest_player(object *mon)
Definition: player.cpp:540
guildoracle.list
list
Definition: guildoracle.py:87
apply_map_builder
void apply_map_builder(object *pl, int dir)
Definition: build_map.cpp:960
object::speed_left
float speed_left
Definition: object.h:338
PREFER_LOW
#define PREFER_LOW
Definition: define.h:564
PU_FOOD
#define PU_FOOD
Definition: define.h:115
object::map
struct mapstruct * map
Definition: object.h:305
object::anim_suffix
sstring anim_suffix
Definition: object.h:324
Settings::rules
const char * rules
Definition: global.h:279
arch_present_in_ob
object * arch_present_in_ob(const archetype *at, const object *op)
Definition: object.cpp:3213
object_set_owner
void object_set_owner(object *op, object *owner)
Definition: object.cpp:840
esrv_send_pickup
void esrv_send_pickup(player *pl)
Definition: request.cpp:1795
guildjoin.ob
ob
Definition: guildjoin.py:42
player::last_applied_stats
living last_applied_stats
Definition: player.h:172
PU_BOW
#define PU_BOW
Definition: define.h:118
Settings::localdir
const char * localdir
Definition: global.h:249
MSG_TYPE_ATTRIBUTE
#define MSG_TYPE_ATTRIBUTE
Definition: newclient.h:394
GT_ONLY_GOOD
@ GT_ONLY_GOOD
Definition: treasure.h:34
fix_weight
void fix_weight(void)
Definition: player.cpp:3890
player_set_own_title
void player_set_own_title(struct player *pl, const char *title)
Definition: player.cpp:272
range_none
@ range_none
Definition: player.h:30
PU_MAGIC_DEVICE
#define PU_MAGIC_DEVICE
Definition: define.h:138
commongive.inv
inv
Definition: commongive.py:29
SET_ANIMATION
#define SET_ANIMATION(ob, newanim)
Definition: global.h:162
time
non standard information is not specified or uptime this means how long since the executable has been started A particular host may have been running a server for quite a long time
Definition: arch-handbook.txt:206
CHARISMA
@ CHARISMA
Definition: living.h:15
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
party_rejoin_if_exists
@ party_rejoin_if_exists
Definition: player.h:100
mon
object * mon
Definition: comet_perf.cpp:75
MIN
#define MIN(x, y)
Definition: compat.h:21
CS_QUERY_SINGLECHAR
#define CS_QUERY_SINGLECHAR
Definition: newclient.h:56
play_sound_player_only
void play_sound_player_only(player *pl, int8_t sound_type, object *emitter, int dir, const char *action)
Definition: sounds.cpp:51
allowed_class
int allowed_class(const object *op)
Definition: living.cpp:1650
SKILL
@ SKILL
Definition: object.h:148
object::direction
int8_t direction
Definition: object.h:344
play_sound_map
void play_sound_map(int8_t sound_type, object *emitter, int dir, const char *action)
Definition: sounds.cpp:113
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
RUNE
@ RUNE
Definition: object.h:245
change_skill
int change_skill(object *who, object *new_skill, int flag)
Definition: skill_util.cpp:359
object_copy
void object_copy(const object *src_ob, object *dest_ob)
Definition: object.cpp:1192
FLAG_SCARED
#define FLAG_SCARED
Definition: define.h:271
player::savebed_map
char savebed_map[MAX_BUF]
Definition: player.h:110
find_treasurelist
treasurelist * find_treasurelist(const char *name)
Definition: assets.cpp:249
fix_object
void fix_object(object *op)
Definition: living.cpp:1125
object_find_by_flag_applied
object * object_find_by_flag_applied(const object *who, int flag)
Definition: object.cpp:4220
PU_MISSILEWEAPON
#define PU_MISSILEWEAPON
Definition: define.h:130
player_can_view
int player_can_view(object *pl, object *op)
Definition: player.cpp:4150
confirm_password
void confirm_password(object *op)
Definition: player.cpp:1006
NDI_NAVY
#define NDI_NAVY
Definition: newclient.h:233
player::last_stats
living last_stats
Definition: player.h:168
get_name
void get_name(object *op)
Definition: player.cpp:881
SEE_LAST_ERROR
@ SEE_LAST_ERROR
Definition: define.h:52
TRANSPORT
@ TRANSPORT
Definition: object.h:113
SOUND_TYPE_LIVING
#define SOUND_TYPE_LIVING
Definition: newclient.h:322
socket_struct::inbuf
SockList inbuf
Definition: newserver.h:99
object::enemy
object * enemy
Definition: object.h:391
PU_FLESH
#define PU_FLESH
Definition: define.h:142
NROFATTACKS
#define NROFATTACKS
Definition: attack.h:17
PU_READABLES
#define PU_READABLES
Definition: define.h:137
BALSL_LOSS_CHANCE_RATIO
#define BALSL_LOSS_CHANCE_RATIO
Definition: config.h:142
send_account_players
void send_account_players(socket_struct *ns)
Definition: request.cpp:2032
skills.h
PU_POTION
#define PU_POTION
Definition: define.h:133
motd
**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 book signs motd
Definition: media-tags.txt:31
MSG_TYPE_COMMAND_ERROR
#define MSG_TYPE_COMMAND_ERROR
Definition: newclient.h:518
esrv_add_spells
void esrv_add_spells(player *pl, object *spell)
Definition: request.cpp:1909
range_golem
@ range_golem
Definition: player.h:34
partylist
Definition: party.h:10
FLAG_INV_LOCKED
#define FLAG_INV_LOCKED
Definition: define.h:329
apply_manual
int apply_manual(object *op, object *tmp, int aflag)
Definition: apply.cpp:597
mapstruct::path
char path[HUGE_BUF]
Definition: map.h:355
AssetsManager.h
create_treasure
void create_treasure(treasurelist *t, object *op, int flag, int difficulty, int tries)
Definition: treasure.cpp:263
P_IS_ALIVE
#define P_IS_ALIVE
Definition: map.h:236
MSG_TYPE_MISC
#define MSG_TYPE_MISC
Definition: newclient.h:402
FLAG_APPLIED
#define FLAG_APPLIED
Definition: define.h:235
NDI_BLUE
#define NDI_BLUE
Definition: newclient.h:236
object::level
int16_t level
Definition: object.h:361
FLAG_STEALTH
#define FLAG_STEALTH
Definition: define.h:312
events_execute_object_event
int events_execute_object_event(object *op, int eventcode, object *activator, object *third, const char *message, int fix)
Definition: events.cpp:292
bow_bestarrow
@ bow_bestarrow
Definition: player.h:53
BALSL_MAX_LOSS_RATIO
#define BALSL_MAX_LOSS_RATIO
Definition: config.h:144
pticks
uint32_t pticks
Definition: time.cpp:47
GT_STARTEQUIP
@ GT_STARTEQUIP
Definition: treasure.h:33
buf
StringBuffer * buf
Definition: readable.cpp:1552
hiscore_check
void hiscore_check(object *op, int quiet)
Definition: hiscore.cpp:348
player::swap_first
int swap_first
Definition: player.h:166
getManager
AssetsManager * getManager()
Definition: assets.cpp:305
object_insert_in_ob
object * object_insert_in_ob(object *op, object *where)
Definition: object.cpp:2848
HUGE_BUF
#define HUGE_BUF
Definition: define.h:37
MSG_TYPE_VICTIM
#define MSG_TYPE_VICTIM
Definition: newclient.h:404
MSG_TYPE_COMMAND
#define MSG_TYPE_COMMAND
Definition: newclient.h:393
MAX
#define MAX(x, y)
Definition: compat.h:24
handle_newcs_player
int handle_newcs_player(object *op)
Definition: player.cpp:3086
WISDOM
@ WISDOM
Definition: living.h:14
AT_DEATH
#define AT_DEATH
Definition: attack.h:93
object::resist
int16_t resist[NROFATTACKS]
Definition: object.h:351
FLAG_NO_PICK
#define FLAG_NO_PICK
Definition: define.h:239
MSG_TYPE_SPELL_END
#define MSG_TYPE_SPELL_END
Definition: newclient.h:623
lose_msg
const char *const lose_msg[NUM_STATS]
Definition: living.cpp:172
name
Plugin animator file specs[Config] name
Definition: animfiles.txt:4
player_set_state
void player_set_state(player *pl, uint8_t state)
Definition: player.cpp:4475
FOR_BELOW_FINISH
#define FOR_BELOW_FINISH()
Definition: define.h:711
MIN_STAT
#define MIN_STAT
Definition: define.h:33
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
FLAG_ALIVE
#define FLAG_ALIVE
Definition: define.h:230
add_statbonus
void add_statbonus(object *op)
Definition: living.cpp:868
MSG_TYPE_MOTD
#define MSG_TYPE_MOTD
Definition: newclient.h:390
range_builder
@ range_builder
Definition: player.h:36
SockList
Definition: newclient.h:670
esrv_send_item
void esrv_send_item(object *pl, object *op)
Definition: main.cpp:354
rv_vector::distance_y
int distance_y
Definition: map.h:373
short_stat_name
const char *const short_stat_name[NUM_STATS]
Definition: living.cpp:194
MSG_TYPE_COMMAND_DEBUG
#define MSG_TYPE_COMMAND_DEBUG
Definition: newclient.h:517
AP_NULL
#define AP_NULL
Definition: define.h:573
PU_GLOVES
#define PU_GLOVES
Definition: define.h:126
set_player_socket
void set_player_socket(player *p, socket_struct *ns)
Definition: player.cpp:413
remove_door
void remove_door(object *op)
Definition: time.cpp:38
object::y
int16_t y
Definition: object.h:335
PU_CONTAINER
#define PU_CONTAINER
Definition: define.h:143
m
static event_registration m
Definition: citylife.cpp:425
clear_los
void clear_los(player *pl)
Definition: los.cpp:270
rv_vector::distance_x
int distance_x
Definition: map.h:372
socket_struct::account_chars
Account_Chars * account_chars
Definition: newserver.h:127
autojail.who
who
Definition: autojail.py:3
MAP_IN_MEMORY
#define MAP_IN_MEMORY
Definition: map.h:127
Settings::motd
const char * motd
Definition: global.h:278
Ns_Avail
@ Ns_Avail
Definition: newserver.h:65
object_free_drop_inventory
void object_free_drop_inventory(object *ob)
Definition: object.cpp:1555
turn_one_transport
static int turn_one_transport(object *transport, object *captain, int dir)
Definition: player.cpp:2845
object_update
void object_update(object *op, int action)
Definition: object.cpp:1429
object::contr
struct player * contr
Definition: object.h:284
get_party_password
int get_party_password(object *op, partylist *party)
Definition: player.cpp:1021
player_unready_range_ob
void player_unready_range_ob(player *pl, object *ob)
Definition: player.cpp:4457
PREFER_HIGH
#define PREFER_HIGH
Definition: define.h:563
EVENT_LOGIN
#define EVENT_LOGIN
Definition: events.h:44
disinfect.map
map
Definition: disinfect.py:4
object_decrease_nrof_by_one
#define object_decrease_nrof_by_one(xyz)
Definition: compat.h:32
MAX_SPACES
#define MAX_SPACES
Definition: player.cpp:618
POISON
@ POISON
Definition: object.h:118
PU_RATIO
#define PU_RATIO
Definition: define.h:113
Settings::set_title
uint8_t set_title
Definition: global.h:265
BALSL_NUMBER_LOSSES_RATIO
#define BALSL_NUMBER_LOSSES_RATIO
Definition: config.h:143
flee_player
static void flee_player(object *op)
Definition: player.cpp:1658
MSG_TYPE_ATTRIBUTE_STAT_LOSS
#define MSG_TYPE_ATTRIBUTE_STAT_LOSS
Definition: newclient.h:558
FLAG_WAS_WIZ
#define FLAG_WAS_WIZ
Definition: define.h:234
MSG_TYPE_ADMIN_NEWS
#define MSG_TYPE_ADMIN_NEWS
Definition: newclient.h:484
is_criminal
bool is_criminal(object *op)
Definition: player.cpp:312
player::last_speed
float last_speed
Definition: player.h:173
add_refcount
sstring add_refcount(sstring str)
Definition: shstr.cpp:210
check_race_and_class
int check_race_and_class(living *stats, archetype *race, archetype *opclass)
Definition: player.cpp:1429
enter_player_savebed
void enter_player_savebed(object *op)
Definition: server.cpp:138
PU_KEY
#define PU_KEY
Definition: define.h:128
determine_god
const char * determine_god(object *op)
Definition: gods.cpp:55
MSG_TYPE_ATTRIBUTE_RACE
#define MSG_TYPE_ATTRIBUTE_RACE
Definition: newclient.h:553
freearr_y
short freearr_y[SIZEOFFREE]
Definition: object.cpp:305
news
**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 book signs news
Definition: media-tags.txt:31
ST_CHANGE_PASSWORD_CONFIRM
#define ST_CHANGE_PASSWORD_CONFIRM
Definition: define.h:552
range_size
@ range_size
Definition: player.h:37
move_ob
int move_ob(object *op, int dir, object *originator)
Definition: move.cpp:58
MSG_TYPE_APPLY_SUCCESS
#define MSG_TYPE_APPLY_SUCCESS
Definition: newclient.h:592
query_name
void query_name(const object *op, char *buf, size_t size)
Definition: item.cpp:592
Settings::emergency_x
uint16_t emergency_x
Definition: global.h:300
POTION
@ POTION
Definition: object.h:116
find_player_options
player * find_player_options(const char *plname, int options, const mapstruct *map)
Definition: player.cpp:70
do_hidden_move
void do_hidden_move(object *op)
Definition: player.cpp:4041
MOVE_WALK
#define MOVE_WALK
Definition: define.h:392
FLAG_KNOWN_CURSED
#define FLAG_KNOWN_CURSED
Definition: define.h:320
pick_arrow_target
static object * pick_arrow_target(object *op, const char *type, int dir)
Definition: player.cpp:2024
ST_PLAY_AGAIN
#define ST_PLAY_AGAIN
Definition: define.h:542
treasurelist
Definition: treasure.h:85
action_makes_visible
static int action_makes_visible(object *op)
Definition: player.cpp:4202
ADD_PLAYER_NO_MAP
#define ADD_PLAYER_NO_MAP
Definition: player.h:246
calc_skill_exp
int64_t calc_skill_exp(const object *who, const object *op, const object *skill)
Definition: skill_util.cpp:667
account_char_free
void account_char_free(Account_Chars *chars)
Definition: account_char.cpp:345
archetype::clone
object clone
Definition: object.h:478
fire_misc_object
static void fire_misc_object(object *op, int dir)
Definition: player.cpp:2344
loot_object
static void loot_object(object *op)
Definition: player.cpp:3411
party_leave
void party_leave(object *op)
Definition: party.cpp:123
add_string
sstring add_string(const char *str)
Definition: shstr.cpp:124
account_remove_player
int account_remove_player(const char *account_name, const char *player_name)
Definition: account.cpp:475
apply_anim_suffix
void apply_anim_suffix(object *who, const char *suffix)
Definition: anim.cpp:150
first_map
mapstruct * first_map
Definition: init.cpp:107
FOR_OB_AND_BELOW_FINISH
#define FOR_OB_AND_BELOW_FINISH()
Definition: define.h:754
object_get_multi_size
void object_get_multi_size(const object *ob, int *sx, int *sy, int *hx, int *hy)
Definition: object.cpp:4735
HEAD
#define HEAD(op)
Definition: object.h:598
range_magic
@ range_magic
Definition: player.h:32
apply_container
int apply_container(object *op, object *sack, int aflags)
Definition: apply.cpp:222
similar_direction
static int similar_direction(int a, int b)
Definition: player.cpp:2274
ROD
@ ROD
Definition: object.h:114
CONTAINER
@ CONTAINER
Definition: object.h:236
enter_player_maplevel
void enter_player_maplevel(object *op)
Definition: server.cpp:689
object::move_type
MoveType move_type
Definition: object.h:434
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
PU_STOP
#define PU_STOP
Definition: define.h:110
PU_VALUABLES
#define PU_VALUABLES
Definition: define.h:117
set_attr_value
void set_attr_value(living *stats, int attr, int8_t value)
Definition: living.cpp:218
FLAG_FREED
#define FLAG_FREED
Definition: define.h:233
delete_character
void delete_character(const char *name)
Definition: login.cpp:88
LOCKED_DOOR
@ LOCKED_DOOR
Definition: object.h:128
MSG_TYPE_ITEM
#define MSG_TYPE_ITEM
Definition: newclient.h:401
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
player::last_character_flags
uint32_t last_character_flags
Definition: player.h:163
socket_struct::host
char * host
Definition: newserver.h:100
player::last_skill_exp
int64_t last_skill_exp[MAX_SKILLS]
Definition: player.h:154
SCRIPT_FIX_ALL
#define SCRIPT_FIX_ALL
Definition: global.h:388
MSG_TYPE_ATTACK
#define MSG_TYPE_ATTACK
Definition: newclient.h:398
Settings::news
const char * news
Definition: global.h:280
FLAG_MAKE_INVIS
#define FLAG_MAKE_INVIS
Definition: define.h:328
SPECIAL_KEY
@ SPECIAL_KEY
Definition: object.h:129
MOVE_FLYING
#define MOVE_FLYING
Definition: define.h:395
socket_struct::account_name
char * account_name
Definition: newserver.h:126
has_carried_lights
int has_carried_lights(const object *op)
Definition: los.cpp:346
POWER
@ POWER
Definition: living.h:17
object_update_speed
void object_update_speed(object *op)
Definition: object.cpp:1344
SPELL_HIGHEST
#define SPELL_HIGHEST
Definition: spells.h:60
Ice.b
b
Definition: Ice.py:48
FREE_AND_COPY
#define FREE_AND_COPY(sv, nv)
Definition: global.h:204
EVENT_BORN
#define EVENT_BORN
Definition: events.h:39
guild_questpoints_apply.mapname
mapname
Definition: guild_questpoints_apply.py:8
object::type
uint8_t type
Definition: object.h:348
FLAG_DAMNED
#define FLAG_DAMNED
Definition: define.h:317
living::dam
int16_t dam
Definition: living.h:46
object::magic
int8_t magic
Definition: object.h:358
do_some_living
void do_some_living(object *op)
Definition: player.cpp:3272
bow_nw
@ bow_nw
Definition: player.h:52
treasure::item
struct archetype * item
Definition: treasure.h:64
FLAG_PARALYZED
#define FLAG_PARALYZED
Definition: define.h:371
gravestone_text
static const char * gravestone_text(object *op, char *buf2, int len)
Definition: player.cpp:3237
FLAG_USE_SHIELD
#define FLAG_USE_SHIELD
Definition: define.h:237
hideability
int hideability(object *ob)
Definition: player.cpp:4000
leave
void leave(player *pl, int draw_exit)
Definition: server.cpp:1305
object_free
void object_free(object *ob, int flags)
Definition: object.cpp:1587
GET_MAP_MOVE_BLOCK
#define GET_MAP_MOVE_BLOCK(M, X, Y)
Definition: map.h:191
FOR_INV_FINISH
#define FOR_INV_FINISH()
Definition: define.h:677
find_player
player * find_player(const char *plname)
Definition: player.cpp:59
AssetsManager::archetypes
Archetypes * archetypes()
Definition: AssetsManager.h:44
FLAG_CAN_ROLL
#define FLAG_CAN_ROLL
Definition: define.h:254
find_arrow
static object * find_arrow(object *op, const char *type)
Definition: player.cpp:1922
kill_player_not_permadeath
static void kill_player_not_permadeath(object *op)
Definition: player.cpp:3569
object::move_on
MoveType move_on
Definition: object.h:437
PU_SPELLBOOK
#define PU_SPELLBOOK
Definition: define.h:135
change_exp
void change_exp(object *op, int64_t exp, const char *skill_name, int flag)
Definition: living.cpp:2168
Settings::roll_stat_points
uint8_t roll_stat_points
Definition: global.h:323
FLAG_UNAGGRESSIVE
#define FLAG_UNAGGRESSIVE
Definition: define.h:272
say.max
dictionary max
Definition: say.py:148
tag_t
uint32_t tag_t
Definition: object.h:14
die_roll
int die_roll(int num, int size, const object *op, int goodbad)
Definition: utils.cpp:122
archetype
Definition: object.h:474
display_motd
void display_motd(const object *op)
Definition: player.cpp:139
player::shoottype
rangetype shoottype
Definition: player.h:112
object_matches_string
int object_matches_string(object *pl, object *op, const char *name)
Definition: object.cpp:4565
PU_MELEEWEAPON
#define PU_MELEEWEAPON
Definition: define.h:131
ATNR_POISON
#define ATNR_POISON
Definition: attack.h:59
Settings::playerdir
const char * playerdir
Definition: global.h:250
FLAG_USE_WEAPON
#define FLAG_USE_WEAPON
Definition: define.h:296
sproto.h
drain_msg
const char *const drain_msg[NUM_STATS]
Definition: living.cpp:139
ARROW
@ ARROW
Definition: object.h:122
FLAG_NO_DROP
#define FLAG_NO_DROP
Definition: define.h:288
IS_SHIELD
#define IS_SHIELD(op)
Definition: define.h:170
living::sp
int16_t sp
Definition: living.h:42
MAX_SKILLS
#define MAX_SKILLS
Definition: skills.h:70
FIND_PLAYER_PARTIAL_NAME
#define FIND_PLAYER_PARTIAL_NAME
Definition: player.h:235
get_map_from_coord
mapstruct * get_map_from_coord(mapstruct *m, int16_t *x, int16_t *y)
Definition: map.cpp:2359
AC_PLAYER_STAT_NO_CHANGE
#define AC_PLAYER_STAT_NO_CHANGE
Definition: define.h:597
FLAG_CAN_USE_SKILL
#define FLAG_CAN_USE_SKILL
Definition: define.h:321
PU_MAGICAL
#define PU_MAGICAL
Definition: define.h:132
DETOUR_AMOUNT
#define DETOUR_AMOUNT
Definition: player.cpp:603
FOR_OB_AND_BELOW_PREPARE
#define FOR_OB_AND_BELOW_PREPARE(op_)
Definition: define.h:750
MSG_TYPE_SPELL
#define MSG_TYPE_SPELL
Definition: newclient.h:400
get_rangevector_from_mapcoord
int get_rangevector_from_mapcoord(const mapstruct *m, int x, int y, const object *op2, rv_vector *retval)
Definition: map.cpp:2587
living::Int
int8_t Int
Definition: living.h:36
SP_level_spellpoint_cost
int16_t SP_level_spellpoint_cost(object *caster, object *spell, int flags)
Definition: spell_util.cpp:236
delete_map
void delete_map(mapstruct *m)
Definition: map.cpp:1696
MSG_SUBTYPE_NONE
#define MSG_SUBTYPE_NONE
Definition: newclient.h:409
object::race
sstring race
Definition: object.h:326
object::facing
int8_t facing
Definition: object.h:345
get_dam_bonus
int get_dam_bonus(int stat)
Definition: living.cpp:2378
DEXTERITY
@ DEXTERITY
Definition: living.h:12
SockList_Init
void SockList_Init(SockList *sl)
Definition: lowlevel.cpp:52
PU_HELMET
#define PU_HELMET
Definition: define.h:121
ob_blocked
int ob_blocked(const object *ob, mapstruct *m, int16_t x, int16_t y)
Definition: map.cpp:478
nlohmann::detail::void
j template void())
Definition: json.hpp:4099
receive_play_again
void receive_play_again(object *op, char key)
Definition: player.cpp:956
object_find_by_type_without_flags
object * object_find_by_type_without_flags(const object *who, int type, int *flags, int num_flags)
Definition: object.cpp:3995
object_insert_in_map_at
object * object_insert_in_map_at(object *op, mapstruct *m, object *originator, int flag, int x, int y)
Definition: object.cpp:2095
living
Definition: living.h:35
treasure::next
treasure * next
Definition: treasure.h:69
FLAG_MONSTER
#define FLAG_MONSTER
Definition: define.h:245
fire
void fire(object *op, int dir)
Definition: player.cpp:2396
push_ob
int push_ob(object *who, int dir, object *pusher)
Definition: move.cpp:434
key_inventory
@ key_inventory
Definition: player.h:66
find_skill_by_name
object * find_skill_by_name(object *who, const char *name)
Definition: skill_util.cpp:211
player::last_skill_ob
object * last_skill_ob[MAX_SKILLS]
Definition: player.h:153
apply_death_exp_penalty
void apply_death_exp_penalty(object *op)
Definition: living.cpp:2233
get_dex_bonus
int get_dex_bonus(int stat)
Definition: living.cpp:2354
P_OUT_OF_MAP
#define P_OUT_OF_MAP
Definition: map.h:248
move_player_attack
void move_player_attack(object *op, int dir)
Definition: player.cpp:2623
env
static std::shared_ptr< inja::Environment > env
Definition: mapper.cpp:2170
EXIT_X
#define EXIT_X(xyz)
Definition: define.h:441
MAX_BUF
#define MAX_BUF
Definition: define.h:35
player::last_weight
int32_t last_weight
Definition: player.h:158
Ns_Add
@ Ns_Add
Definition: newserver.h:66
player::listening
uint8_t listening
Definition: player.h:133
strlcpy
size_t strlcpy(char *dst, const char *src, size_t size)
Definition: porting.cpp:222
object_new
object * object_new(void)
Definition: object.cpp:1268
is_wraith_pl
int is_wraith_pl(object *op)
Definition: player.cpp:173
ADD_PLAYER_NEW
#define ADD_PLAYER_NEW
Definition: player.h:245
create_archetype
object * create_archetype(const char *name)
Definition: arch.cpp:278
IS_WEAPON
#define IS_WEAPON(op)
Definition: define.h:163
cast_dust
void cast_dust(object *op, object *throw_ob, int dir)
Definition: player.cpp:3927
free_string
void free_string(sstring str)
Definition: shstr.cpp:280
transfer_ob
int transfer_ob(object *op, int x, int y, int randomly, object *originator)
Definition: move.cpp:163
set_first_map
void set_first_map(object *op)
Definition: player.cpp:394
living::wc
int8_t wc
Definition: living.h:37
key_confirm_quit
void key_confirm_quit(object *op, char key)
Definition: player.cpp:1592
player::unapply
unapplymode unapply
Definition: player.h:121
offsetof
#define offsetof(type, member)
Definition: shstr.h:37
remove_unpaid_objects
void remove_unpaid_objects(object *op, object *env, int free_items)
Definition: player.cpp:3207
RANDOM
#define RANDOM()
Definition: define.h:644
SockList_Term
void SockList_Term(SockList *sl)
Definition: lowlevel.cpp:62
SK_HIDING
@ SK_HIDING
Definition: skills.h:21
FREE_AND_CLEAR_STR
#define FREE_AND_CLEAR_STR(xyz)
Definition: global.h:198
MOVE_FLY_LOW
#define MOVE_FLY_LOW
Definition: define.h:393
EVENT_PLAYER_DEATH
#define EVENT_PLAYER_DEATH
Definition: events.h:53
dead_player
void dead_player(object *op)
Definition: resurrection.cpp:297
ATNR_FIRE
#define ATNR_FIRE
Definition: attack.h:51
player::gen_sp_armour
int16_t gen_sp_armour
Definition: player.h:128
MSG_TYPE_ITEM_ADD
#define MSG_TYPE_ITEM_ADD
Definition: newclient.h:632
is_valid_types_gen.found
found
Definition: is_valid_types_gen.py:39
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
key_change_class
void key_change_class(object *op, char key)
Definition: player.cpp:1289
FLAG_KNOWN_MAGICAL
#define FLAG_KNOWN_MAGICAL
Definition: define.h:319
stand_near_hostile
int stand_near_hostile(object *who)
Definition: player.cpp:4082
FIND_PLAYER_NO_HIDDEN_DM
#define FIND_PLAYER_NO_HIDDEN_DM
Definition: player.h:236
OUT_OF_REAL_MAP
#define OUT_OF_REAL_MAP(M, X, Y)
Definition: map.h:216
ST_PLAYING
#define ST_PLAYING
Definition: define.h:541
roll_stat
int roll_stat(void)
Definition: player.cpp:1038
living::Wis
int8_t Wis
Definition: living.h:36
get_nearest_criminal
object * get_nearest_criminal(object *mon)
Definition: player.cpp:583
range_misc
@ range_misc
Definition: player.h:33
FLAG_READY_SKILL
#define FLAG_READY_SKILL
Definition: define.h:333
FLAG_READY_BOW
#define FLAG_READY_BOW
Definition: define.h:299
sounds.h
FLAG_REMOVED
#define FLAG_REMOVED
Definition: define.h:232
ST_CHANGE_CLASS
#define ST_CHANGE_CLASS
Definition: define.h:544
treasure::magic
uint8_t magic
Definition: treasure.h:74
object_find_by_type_and_slaying
object * object_find_by_type_and_slaying(const object *who, int type, const char *slaying)
Definition: object.cpp:4149
FLAG_WIZ
#define FLAG_WIZ
Definition: define.h:231
NDI_UNIQUE
#define NDI_UNIQUE
Definition: newclient.h:251
fatal
void fatal(enum fatal_error err)
Definition: utils.cpp:570
object::slaying
sstring slaying
Definition: object.h:327
roll-o-matic.stop
def stop()
Definition: roll-o-matic.py:78
FLAG_FRIENDLY
#define FLAG_FRIENDLY
Definition: define.h:246
spells.h
EVENT_DEATH
#define EVENT_DEATH
Definition: events.h:25
object::name
sstring name
Definition: object.h:319
ob_process
method_ret ob_process(object *op)
Definition: ob_methods.cpp:67
PU_CLOAK
#define PU_CLOAK
Definition: define.h:127
BATTLEGROUND
@ BATTLEGROUND
Definition: object.h:168
MSG_TYPE_SKILL_FAILURE
#define MSG_TYPE_SKILL_FAILURE
Definition: newclient.h:579
pet_normal
@ pet_normal
Definition: player.h:58
player::has_hit
uint32_t has_hit
Definition: player.h:144
AssetsCollection::each
void each(std::function< void(T *)> op)
Definition: AssetsCollection.h:158
object_find_by_arch_name
object * object_find_by_arch_name(const object *who, const char *name)
Definition: object.cpp:4243
Moving_Fog.z
z
Definition: Moving_Fog.py:17
party_get_password
const char * party_get_password(const partylist *party)
Definition: party.cpp:232
get_thaco_bonus
int get_thaco_bonus(int stat)
Definition: living.cpp:2358
i18n_get_language_by_code
language_t i18n_get_language_by_code(const char *code)
Definition: languages.cpp:73
object_can_pick
int object_can_pick(const object *who, const object *item)
Definition: object.cpp:3858
players
std::vector< archetype * > players
Definition: player.cpp:493
add_friendly_object
void add_friendly_object(object *op)
Definition: friend.cpp:32
key_roll_stat
void key_roll_stat(object *op, char key)
Definition: player.cpp:1213
ST_CONFIRM_PASSWORD
#define ST_CONFIRM_PASSWORD
Definition: define.h:548
IS_ARMOR
#define IS_ARMOR(op)
Definition: define.h:166
unapply_nochoice
@ unapply_nochoice
Definition: player.h:76
get_map_flags
int get_map_flags(mapstruct *oldmap, mapstruct **newmap, int16_t x, int16_t y, int16_t *nx, int16_t *ny)
Definition: map.cpp:299
object_find_by_type
object * object_find_by_type(const object *who, int type)
Definition: object.cpp:3971
SOUND_TYPE_GROUND
#define SOUND_TYPE_GROUND
Definition: newclient.h:325
KEY
@ KEY
Definition: object.h:132
FLAG_USE_ARMOUR
#define FLAG_USE_ARMOUR
Definition: define.h:295
PU_JEWELS
#define PU_JEWELS
Definition: define.h:141
item
Definition: item.py:1
MSG_TYPE_ITEM_REMOVE
#define MSG_TYPE_ITEM_REMOVE
Definition: newclient.h:631
is_identifiable_type
int is_identifiable_type(const object *op)
Definition: item.cpp:1328
change_attr_value
void change_attr_value(living *stats, int attr, int8_t value)
Definition: living.cpp:264
mapstruct
Definition: map.h:314
enter_exit
void enter_exit(object *op, object *exit_ob)
Definition: server.cpp:734
check_spell_known
object * check_spell_known(object *op, const char *name)
Definition: spell_util.cpp:394
living::Cha
int8_t Cha
Definition: living.h:36
give.op
op
Definition: give.py:33
NDI_ALL
#define NDI_ALL
Definition: newclient.h:252
socket_struct::status
enum Sock_Status status
Definition: newserver.h:90
fire_bow
int fire_bow(object *op, object *arrow, int dir, int wc_mod, int16_t sx, int16_t sy)
Definition: player.cpp:2093
save_life
static int save_life(object *op)
Definition: player.cpp:3164
STRENGTH
@ STRENGTH
Definition: living.h:11
object::skill
sstring skill
Definition: object.h:329
animate_object
void animate_object(object *op, int dir)
Definition: anim.cpp:44
player::language
language_t language
Definition: player.h:220
find_archetype
archetype * find_archetype(const char *name)
Definition: assets.cpp:266
shop.h
object_split
object * object_split(object *orig_ob, uint32_t nr, char *err, size_t size)
Definition: object.cpp:2628
EXIT_ALT_X
#define EXIT_ALT_X(xyz)
Definition: define.h:443
MSG_TYPE_ATTRIBUTE_GOD
#define MSG_TYPE_ATTRIBUTE_GOD
Definition: newclient.h:564
Settings::stat_loss_on_death
uint8_t stat_loss_on_death
Definition: global.h:256
dragon_ability_gain
void dragon_ability_gain(object *who, int atnr, int level)
Definition: player.cpp:4299
change_luck
void change_luck(object *op, int value)
Definition: living.cpp:796
restore_player
static void restore_player(object *op)
Definition: player.cpp:3450
PU_ARMOUR
#define PU_ARMOUR
Definition: define.h:123
object_set_msg
void object_set_msg(object *op, const char *msg)
Definition: object.cpp:4802
esrv_update_item
void esrv_update_item(int flags, object *pl, object *op)
Definition: main.cpp:359
did_make_save
int did_make_save(const object *op, int level, int bonus)
Definition: living.cpp:2282
send_rules
void send_rules(const object *op)
Definition: player.cpp:170
SPELL_EFFECT
@ SPELL_EFFECT
Definition: object.h:220
object::msg
sstring msg
Definition: object.h:330
PU_NEWMODE
#define PU_NEWMODE
Definition: define.h:111
rv_vector
Definition: map.h:370
MSG_TYPE_ADMIN_RULES
#define MSG_TYPE_ADMIN_RULES
Definition: newclient.h:483
SKILL_TOOL
@ SKILL_TOOL
Definition: object.h:194
send_news
void send_news(const object *op)
Definition: player.cpp:206
EXIT_Y
#define EXIT_Y(xyz)
Definition: define.h:442
diamondslots.y
y
Definition: diamondslots.py:16
FLAG_BEEN_APPLIED
#define FLAG_BEEN_APPLIED
Definition: define.h:323
Settings::resurrection
uint8_t resurrection
Definition: global.h:266
assets.h
CLEAR_FLAG
#define CLEAR_FLAG(xyz, p)
Definition: define.h:225
random_roll
int random_roll(int min, int max, const object *op, int goodbad)
Definition: utils.cpp:42
NDI_BROWN
#define NDI_BROWN
Definition: newclient.h:242
AP_NOPRINT
#define AP_NOPRINT
Definition: define.h:585
Settings::max_stat
uint8_t max_stat
Definition: global.h:324
EVENT_REMOVE
#define EVENT_REMOVE
Definition: events.h:54
SockList_ResetRead
void SockList_ResetRead(SockList *sl)
Definition: lowlevel.cpp:80
guild_entry.x1
int x1
Definition: guild_entry.py:33
NDI_DK_ORANGE
#define NDI_DK_ORANGE
Definition: newclient.h:237
bow_n
@ bow_n
Definition: player.h:45
socket_struct::faces_sent
uint8_t * faces_sent
Definition: newserver.h:96
get_password
void get_password(object *op)
Definition: player.cpp:892
player::last_resist
int16_t last_resist[NROFATTACKS]
Definition: player.h:174
arch_to_object
object * arch_to_object(archetype *at)
Definition: arch.cpp:229
stats
Player Stats effect how well a character can survie and interact inside the crossfire world This section discusses the various stats
Definition: stats.txt:2
level
int level
Definition: readable.cpp:1550
get_rangevector
int get_rangevector(object *op1, const object *op2, rv_vector *retval, int flags)
Definition: map.cpp:2519
player::party
partylist * party
Definition: player.h:203
keyrings
@ keyrings
Definition: player.h:67
castle_read.key
key
Definition: castle_read.py:64
play_again
void play_again(object *op)
Definition: player.cpp:903
get_player
player * get_player(player *p)
Definition: player.cpp:285
MSG_TYPE_ADMIN_LOGIN
#define MSG_TYPE_ADMIN_LOGIN
Definition: newclient.h:489
FLAG_ANIMATE
#define FLAG_ANIMATE
Definition: define.h:242
MSG_TYPE_ATTRIBUTE_BAD_EFFECT_END
#define MSG_TYPE_ATTRIBUTE_BAD_EFFECT_END
Definition: newclient.h:556
Settings::balanced_stat_loss
uint8_t balanced_stat_loss
Definition: global.h:261
newclient.h
fix_luck
void fix_luck(void)
Definition: player.cpp:3906
save_player
int save_player(object *op, int flag)
Definition: login.cpp:230
ST_GET_PARTY_PASSWORD
#define ST_GET_PARTY_PASSWORD
Definition: define.h:549
object_matches_pickup_mode
int object_matches_pickup_mode(const object *item, int mode)
Definition: c_object.cpp:656
FOOD
@ FOOD
Definition: object.h:117
skill
skill
Definition: arch-handbook.txt:585
strip_endline
void strip_endline(char *buf)
Definition: utils.cpp:314
object_remove
void object_remove(object *op)
Definition: object.cpp:1828
cure_disease
int cure_disease(object *sufferer, object *caster, sstring skill)
Definition: disease.cpp:685
a
Magical Runes Runes are magical inscriptions on the dungeon which cast a spell or detonate when something steps on them Flying objects don t detonate runes Beware ! Runes are invisible most of the time They are only visible occasionally ! There are several runes which are there are some special runes which may only be called with the invoke and people may apply it to read it Maybe useful for mazes ! This rune will not nor is it ordinarily invisible Partial Visibility of they ll be visible only part of the time They have a(your level/2) chance of being visible in any given round
DOOR
@ DOOR
Definition: object.h:131
object_sum_weight
signed long object_sum_weight(object *op)
Definition: object.cpp:568
FLAG_UNPAID
#define FLAG_UNPAID
Definition: define.h:236
FLAG_NO_STRENGTH
#define FLAG_NO_STRENGTH
Definition: define.h:306
quest.state
state
Definition: quest.py:13
PU_NOTHING
#define PU_NOTHING
Definition: define.h:106
OB_TYPE_MOVE_BLOCK
#define OB_TYPE_MOVE_BLOCK(ob1, type)
Definition: define.h:432
move_player
int move_player(object *op, int dir)
Definition: player.cpp:2948
now
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 now
Definition: protocol.txt:71
remove_statbonus
void remove_statbonus(object *op)
Definition: living.cpp:845
MSG_TYPE_ATTACK_NOATTACK
#define MSG_TYPE_ATTACK_NOATTACK
Definition: newclient.h:607
object::spellarg
char * spellarg
Definition: object.h:419
make_path_to_file
void make_path_to_file(const char *filename)
Definition: porting.cpp:164
DRINK
@ DRINK
Definition: object.h:162
PU_CURSED
#define PU_CURSED
Definition: define.h:144
find_player_partial_name
player * find_player_partial_name(const char *plname)
Definition: player.cpp:114
roll_stats
void roll_stats(object *op)
Definition: player.cpp:1062
ATNR_COLD
#define ATNR_COLD
Definition: attack.h:53
object::nrof
uint32_t nrof
Definition: object.h:342
FLAG_XRAYS
#define FLAG_XRAYS
Definition: define.h:300
player::socket
socket_struct * socket
Definition: player.h:107
mapstruct::next
mapstruct * next
Definition: map.h:315
living::grace
int16_t grace
Definition: living.h:44
add_player
player * add_player(socket_struct *ns, int flags)
Definition: player.cpp:460
say.item
dictionary item
Definition: say.py:149
query_base_name
void query_base_name(const object *op, int plural, char *buf, size_t size)
Definition: item.cpp:693
object::stats
living stats
Definition: object.h:378
treasure
Definition: treasure.h:63
player::title
char title[BIG_NAME]
Definition: player.h:184
object::more
object * more
Definition: object.h:303
get_attr_value
int8_t get_attr_value(const living *stats, int attr)
Definition: living.cpp:313
drain_rod_charge
void drain_rod_charge(object *rod)
Definition: spell_util.cpp:776
range_skill
@ range_skill
Definition: player.h:35
server.h
freearr_x
short freearr_x[SIZEOFFREE]
Definition: object.cpp:299
PU_DRINK
#define PU_DRINK
Definition: define.h:116
player_attack_door
static int player_attack_door(object *op, object *door)
Definition: player.cpp:2550
bonus
Player Stats effect how well a character can survie and interact inside the crossfire world This section discusses the various what they and how they effect the player s actions Also in this section are the stat modifiers that specific classes professions bring Player and sps the current and maximum the Current and Maximum The Current Sp can go somewhat negative When Sp is negative not all spells can be and a more negative Sp makes spell casting less likey to succeed can affect Damage and how the characters as well as how often the character can attack this affects the prices when buying and selling items if this drops the player will start losing hit points wd Cleric or Dwarf sm Elf wd Fireborn ft Human ra Mage C Monk se Ninja hi Priest C Quetzalcoatl mw Swashbuckler si Thief st Viking ba Warrior or Wizard C Wraith C Class Prof Str Dex Con Wis Cha Int Pow Net Skills Enclosed are codes used for the skills above The ones in and fighting should all be pretty self explanatory For the other a brief description is for a more detailed look at the skills doc file Skill remove use magic items phys no fire cold Fireborns are supposed to be fire spirits They re closely in tune with magic and are powerful and learn magic easily Being fire they are immune to fire and and vulnerable to cold They are vulnerable to ghosthit and drain because being mostly non anything which strikes directly at the spirit hits them harder race attacktype restrictions immunities prot vuln Quetzalcoatl physical no armour fire cold Quetzalcoatl s are now born knowing the spell of burning but because of their negative wisdom bonus
Definition: stats.txt:176
INTELLIGENCE
@ INTELLIGENCE
Definition: living.h:16
player::petmode
petmode_t petmode
Definition: player.h:115
shop_contains
bool shop_contains(object *ob)
Definition: shop.cpp:1296
reputation.killer
def killer
Definition: reputation.py:13
swap_stat
static void swap_stat(object *op, int swap_second)
Definition: player.cpp:1155
TRUE
#define TRUE
Definition: compat.h:11
player::last_weapon_sp
float last_weapon_sp
Definition: player.h:156
Settings::emergency_y
uint16_t emergency_y
Definition: global.h:300
ATNR_ELECTRICITY
#define ATNR_ELECTRICITY
Definition: attack.h:52
ST_ROLL_STAT
#define ST_ROLL_STAT
Definition: define.h:543
clear_player
void clear_player(player *pl)
Definition: player.cpp:33
pets_control_golem
void pets_control_golem(object *op, int dir)
Definition: pets.cpp:630
PU_SHIELD
#define PU_SHIELD
Definition: define.h:122
SPELL
@ SPELL
Definition: object.h:219
link_player_skills
void link_player_skills(object *op)
Definition: player.cpp:287
find_key
object * find_key(object *pl, object *container, object *door)
Definition: player.cpp:2468
give_initial_items
void give_initial_items(object *pl, treasurelist *items)
Definition: player.cpp:784
living::Pow
int8_t Pow
Definition: living.h:36
update_transport_block
static void update_transport_block(object *transport, int dir)
Definition: player.cpp:2800
MSG_TYPE_APPLY
#define MSG_TYPE_APPLY
Definition: newclient.h:397
player_map_change_common
void player_map_change_common(object *op, mapstruct *const oldmap, mapstruct *const newmap)
Definition: server.cpp:278
account_char_save
void account_char_save(Account_Chars *chars)
Definition: account_char.cpp:158
object::attacktype
uint32_t attacktype
Definition: object.h:352
OUT_OF_MEMORY
@ OUT_OF_MEMORY
Definition: define.h:48
FLAG_CURSED
#define FLAG_CURSED
Definition: define.h:316
pick_up
void pick_up(object *op, object *alt)
Definition: c_object.cpp:519
rv_vector::direction
int direction
Definition: map.h:374
altar_valkyrie.pl
pl
Definition: altar_valkyrie.py:28
check_stat_bounds
void check_stat_bounds(living *stats, int8_t min_stat, int8_t max_stat)
Definition: living.cpp:354
living.h
send_query
void send_query(socket_struct *ns, uint8_t flags, const char *text)
Definition: request.cpp:728
Send_With_Handling
void Send_With_Handling(socket_struct *ns, SockList *sl)
Definition: lowlevel.cpp:444
if
if(!(yy_init))
Definition: loader.c:2626
is_true_undead
int is_true_undead(object *op)
Definition: player.cpp:3976
MSG_TYPE_ADMIN
#define MSG_TYPE_ADMIN
Definition: newclient.h:391
rv_vector::distance
unsigned int distance
Definition: map.h:371
player::bowtype
bowtype_t bowtype
Definition: player.h:114
SPELLBOOK
@ SPELLBOOK
Definition: object.h:208
Settings::confdir
const char * confdir
Definition: global.h:247
NUM_STATS
@ NUM_STATS
Definition: living.h:18
MSG_TYPE_ITEM_INFO
#define MSG_TYPE_ITEM_INFO
Definition: newclient.h:634
Settings::not_permadeth
uint8_t not_permadeth
Definition: global.h:262
apply_race_and_class
int apply_race_and_class(object *op, archetype *race, archetype *opclass, living *stats)
Definition: player.cpp:1479
FOR_INV_PREPARE
#define FOR_INV_PREPARE(op_, it_)
Definition: define.h:670
MAX_FOOD
static const int32_t MAX_FOOD
Definition: define.h:461
living::hp
int16_t hp
Definition: living.h:40
living::luck
int8_t luck
Definition: living.h:39
drain_wand_charge
void drain_wand_charge(object *wand)
Definition: spell_util.cpp:786
FORCE
@ FORCE
Definition: object.h:229
playername_ok
int playername_ok(const char *cp)
Definition: player.cpp:257
account_char_remove
void account_char_remove(Account_Chars *chars, const char *pl_name)
Definition: account_char.cpp:313
EXIT_ALT_Y
#define EXIT_ALT_Y(xyz)
Definition: define.h:444
object.h
player::ticks_played
uint32_t ticks_played
Definition: player.h:222
player::rejoin_party
party_rejoin_mode rejoin_party
Definition: player.h:210
events_execute_global_event
void events_execute_global_event(int eventcode,...)
Definition: events.cpp:27
CONSTITUTION
@ CONSTITUTION
Definition: living.h:13
object_can_merge
int object_can_merge(object *ob1, object *ob2)
Definition: object.cpp:433
llevDebug
@ llevDebug
Definition: logger.h:13
FLAG_LIFESAVE
#define FLAG_LIFESAVE
Definition: define.h:305
MONEY
@ MONEY
Definition: object.h:142
NRSPELLPATHS
#define NRSPELLPATHS
Definition: spells.h:40
is_valid_types_gen.type
list type
Definition: is_valid_types_gen.py:25
FLAG_IS_FLOOR
#define FLAG_IS_FLOOR
Definition: define.h:302
i18n
const char * i18n(const object *who, const char *code)
Definition: languages.cpp:42
P_BLOCKSVIEW
#define P_BLOCKSVIEW
Definition: map.h:225
FORCE_NAME
#define FORCE_NAME
Definition: spells.h:169
living::Con
int8_t Con
Definition: living.h:36
FLAG_IDENTIFIED
#define FLAG_IDENTIFIED
Definition: define.h:261
living::Str
int8_t Str
Definition: living.h:36
skill_attack
void skill_attack(object *tmp, object *pl, int dir, const char *string, object *skill)
Definition: skill_util.cpp:1270
Settings::emergency_mapname
char * emergency_mapname
Definition: global.h:299
PU_DEBUG
#define PU_DEBUG
Definition: define.h:108
dragon_attune.force
force
Definition: dragon_attune.py:45
CS_QUERY_HIDEINPUT
#define CS_QUERY_HIDEINPUT
Definition: newclient.h:57
hide
uint32 hide
Definition: arch-handbook.txt:572
PU_SKILLSCROLL
#define PU_SKILLSCROLL
Definition: define.h:136
level
Definition: level.py:1