Atrinik Server  4.0
map.c
Go to the documentation of this file.
1 /*************************************************************************
2  * Atrinik, a Multiplayer Online Role Playing Game *
3  * *
4  * Copyright (C) 2009-2014 Alex Tokar and Atrinik Development Team *
5  * *
6  * Fork from Crossfire (Multiplayer game for X-windows). *
7  * *
8  * This program is free software; you can redistribute it and/or modify *
9  * it under the terms of the GNU General Public License as published by *
10  * the Free Software Foundation; either version 2 of the License, or *
11  * (at your option) any later version. *
12  * *
13  * This program is distributed in the hope that it will be useful, *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16  * GNU General Public License for more details. *
17  * *
18  * You should have received a copy of the GNU General Public License *
19  * along with this program; if not, write to the Free Software *
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
21  * *
22  * The author can be reached at admin@atrinik.org *
23  ************************************************************************/
24 
30 #include <global.h>
31 #include <loader.h>
32 #include <toolkit/string.h>
33 #include <arch.h>
34 #include <player.h>
35 #include <object.h>
36 #include <exit.h>
37 #include <door.h>
38 #include <check_inv.h>
39 #include <magic_mirror.h>
40 #include <object_methods.h>
41 #include <toolkit/path.h>
42 
43 int global_darkness_table[MAX_DARKNESS + 1] = {
44  0, 20, 40, 80, 160, 320, 640, 1280
45 };
46 
53  TILED_SOUTH, /* TILED_NORTH */
54  TILED_WEST, /* TILED_EAST */
55  TILED_NORTH, /* TILED_SOUTH */
56  TILED_EAST, /* TILED_WEST */
57  TILED_SOUTHWEST, /* TILED_NORTHEAST */
58  TILED_NORTHWEST, /* TILED_SOUTHEAST */
59  TILED_NORTHEAST, /* TILED_SOUTHWEST */
60  TILED_SOUTHEAST, /* TILED_NORTHWEST */
61  TILED_DOWN, /* TILED_UP */
62  TILED_UP, /* TILED_DOWN */
63 };
64 
65 static int map_tiled_coords[TILED_NUM][3] = {
66  {0, -1, 0},
67  {1, 0, 0},
68  {0, 1, 0},
69  {-1, 0, 0},
70  {1, -1, 0},
71  {1, 1, 0},
72  {-1, 1, 0},
73  {-1, -1, 0},
74  {0, 0, 1},
75  {0, 0, -1},
76 };
77 
78 static mempool_struct *pool_map;
79 static uint32_t map_count;
80 
81 #define DEBUG_OLDFLAGS 0
82 
83 static void load_objects(mapstruct *m, FILE *fp, int mapflags);
84 static void save_objects(mapstruct *m, FILE *fp, FILE *fp2);
85 static void allocate_map(mapstruct *m);
86 static void free_all_objects(mapstruct *m);
87 
89 static void map_debugger(mapstruct *map, char *buf, size_t size)
90 {
91  snprintf(buf, size, "count: %d", map->count);
92 
93  if (map->name != NULL) {
94  snprintfcat(buf, size, " name: %s", map->name);
95  }
96 
97  if (map->path != NULL) {
98  snprintfcat(buf, size, " path: %s", map->path);
99  }
100 }
101 
103 static bool map_validator(mapstruct *map)
104 {
105  return map->count != 0;
106 }
107 
109 static void map_constructor(mapstruct *map)
110 {
111  DL_APPEND(first_map, map);
112 
113  map->in_memory = MAP_SWAPPED;
114 
115  /* The maps used to pick up default x and y values from the
116  * map archetype. Mimic that behavior. */
117  MAP_WIDTH(map) = 16;
118  MAP_HEIGHT(map) = 16;
119  MAP_RESET_TIMEOUT(map) = 0;
122 
123  MAP_ENTER_X(map) = 0;
124  MAP_ENTER_Y(map) = 0;
125 
126  map->count = ++map_count;
127 }
128 
130 static void map_destructor(mapstruct *map)
131 {
132  if (!map->global_removed) {
133  DL_DELETE(first_map, map);
134  }
135 
136  /* tmpname can still be needed if the map is swapped out, so we don't
137  * do it in free_map(). */
140 
141  /* Invalidate references to the map. */
142  map->count = 0;
143 }
144 
148 void map_init(void)
149 {
150  map_count = 0;
151  pool_map = mempool_create("maps", 10, sizeof(mapstruct),
152  MEMPOOL_ALLOW_FREEING, NULL, NULL,
153  (chunk_constructor) map_constructor,
154  (chunk_destructor) map_destructor);
155  mempool_set_debugger(pool_map, (chunk_debugger) map_debugger);
156  mempool_set_validator(pool_map, (chunk_validator) map_validator);
157 }
158 
168 static inline mapstruct *load_and_link_tiled_map(mapstruct *orig_map, int tile_num)
169 {
170  mapstruct *map;
171 
172  if (orig_map->tile_path[tile_num] == NULL) {
173  return NULL;
174  }
175 
176  map = ready_map_name(orig_map->tile_path[tile_num], orig_map,
177  MAP_NAME_SHARED | (MAP_UNIQUE(orig_map) ? MAP_PLAYER_UNIQUE : 0));
178 
179  if (map == NULL) {
180  log_error("Failed to load map: %s (tile: #%d), for: %s",
181  orig_map->tile_path[tile_num], tile_num, orig_map->path);
182  return NULL;
183  }
184 
185  if (orig_map->tile_map[tile_num] == NULL) {
186  orig_map->tile_map[tile_num] = map;
187  map->tile_map[map_tiled_reverse[tile_num]] = orig_map;
188  } else if (map != orig_map->tile_map[tile_num]) {
189  log_error("Failed to connect map %s with tile #%d (%s).",
190  orig_map->tile_path[tile_num], tile_num, orig_map->path);
191  FREE_AND_CLEAR_HASH(orig_map->tile_path[tile_num]);
192  delete_map(map);
193  return NULL;
194  }
195 
196  return map;
197 }
198 
219 static int relative_tile_position_rec(mapstruct *map1, mapstruct *map2, int *x, int *y, int *z, uint32_t id, int level, int flags)
220 {
221  int i;
222  map_exit_t *exit;
223  mapstruct *m;
224 
225  if (map1 == map2) {
226  return 1;
227  }
228 
229  if (level <= 0) {
230  return 0;
231  }
232 
233  level--;
234  map1->traversed = id;
235 
236  DL_FOREACH(map1->exits, exit)
237  {
238  m = exit_get_destination(exit->obj, NULL, NULL, false);
239 
240  if (m == NULL) {
241  continue;
242  }
243 
244  if (m->traversed != id && (m == map2 || relative_tile_position_rec(m,
245  map2, x, y, z, id, flags,
246  flags & RV_RECURSIVE_SEARCH ? level : 1))) {
247  *z += 1;
248  return 1;
249  }
250  }
251 
252  if (flags & RV_RECURSIVE_SEARCH) {
253  /* Depth-first search for the destination map */
254  for (i = 0; i < TILED_NUM; i++) {
255  if (map1->tile_path[i]) {
256  if (map1->tile_map[i] == NULL ||
257  map1->tile_map[i]->in_memory != MAP_IN_MEMORY) {
258  if (flags & RV_NO_LOAD || !load_and_link_tiled_map(map1,
259  i)) {
260  continue;
261  }
262  }
263 
264  if (map1->tile_map[i]->traversed != id && (map1->tile_map[i] ==
265  map2 || relative_tile_position_rec(map1->tile_map[i],
266  map2, x, y, z, id, level, flags))) {
267  switch (i) {
268  case TILED_NORTH:
269  *y -= MAP_HEIGHT(map1->tile_map[i]);
270  return 1;
271 
272  case TILED_EAST:
273  *x += MAP_WIDTH(map1);
274  return 1;
275 
276  case TILED_SOUTH:
277  *y += MAP_HEIGHT(map1);
278  return 1;
279 
280  case TILED_WEST:
281  *x -= MAP_WIDTH(map1->tile_map[i]);
282  return 1;
283 
284  case TILED_NORTHEAST:
285  *y -= MAP_HEIGHT(map1->tile_map[i]);
286  *x += MAP_WIDTH(map1);
287  return 1;
288 
289  case TILED_SOUTHEAST:
290  *y += MAP_HEIGHT(map1);
291  *x += MAP_WIDTH(map1);
292  return 1;
293 
294  case TILED_SOUTHWEST:
295  *y += MAP_HEIGHT(map1);
296  *x -= MAP_WIDTH(map1->tile_map[i]);
297  return 1;
298 
299  case TILED_NORTHWEST:
300  *y -= MAP_HEIGHT(map1->tile_map[i]);
301  *x -= MAP_WIDTH(map1->tile_map[i]);
302  return 1;
303 
304  case TILED_UP:
305  *z += 1;
306  return 1;
307 
308  case TILED_DOWN:
309  *z -= 1;
310  return 1;
311  }
312  }
313  }
314  }
315  }
316 
317  return 0;
318 }
319 
348 static int relative_tile_position(mapstruct *map1, mapstruct *map2, int *x, int *y, int *z, int flags)
349 {
350  static uint32_t traversal_id = 0;
351 
352  /* Save some time in the simplest cases ( very similar to on_same_map() )*/
353  if (map1 == NULL || map2 == NULL) {
354  return 0;
355  }
356 
357  if (map1 == map2) {
358  return 1;
359  }
360 
361  /* Avoid overflow of traversal_id */
362  if (traversal_id == 4294967295U) {
363  mapstruct *m;
364 
365  LOG(DEBUG, "resetting traversal id");
366 
367  DL_FOREACH(first_map, m)
368  {
369  m->traversed = 0;
370  }
371 
372  traversal_id = 0;
373  }
374 
375  /* Recursive search */
376  return relative_tile_position_rec(map1, map2, x, y, z, ++traversal_id, 2,
377  flags);
378 }
379 
390 {
391  mapstruct *map;
392 
393  if (!name || !*name) {
394  return NULL;
395  }
396 
397  if (*name != '/' && *name != '.') {
398  LOG(DEBUG, "Found map name without starting '/' or '.' (%s)", name);
399  return NULL;
400  }
401 
402  DL_FOREACH(first_map, map)
403  {
404  if (name == map->path) {
405  break;
406  }
407  }
408 
409  return map;
410 }
411 
422 char *create_pathname(const char *name)
423 {
424  static char buf[MAX_BUF];
425 
426  if (*name == '/') {
427  snprintf(buf, sizeof(buf), "%s%s", settings.mapspath, name);
428  } else {
429  snprintf(buf, sizeof(buf), "%s/%s", settings.mapspath, name);
430  }
431 
432  return buf;
433 }
434 
445 static char *create_items_path(shstr *s)
446 {
447  static char buf[MAX_BUF];
448  char *t;
449 
450  if (*s == '/') {
451  s++;
452  }
453 
454  snprintf(buf, sizeof(buf), "%s/unique-items/", settings.datapath);
455 
456  for (t = buf + strlen(buf); *s; s++, t++) {
457  if (*s == '/') {
458  *t = '@';
459  } else {
460  *t = *s;
461  }
462  }
463 
464  *t = '\0';
465  return buf;
466 }
467 
486 int wall(mapstruct *m, int x, int y)
487 {
488  if (!(m = get_map_from_coord(m, &x, &y))) {
489  return (P_BLOCKSVIEW | P_NO_PASS | P_OUT_OF_MAP);
490  }
491 
492  return (GET_MAP_FLAGS(m, x, y) & (P_DOOR_CLOSED | P_PLAYER_ONLY | P_NO_PASS | P_PASS_THRU));
493 }
494 
507 int blocks_view(mapstruct *m, int x, int y)
508 {
509  mapstruct *nm;
510 
511  if (!(nm = get_map_from_coord(m, &x, &y))) {
512  return (P_BLOCKSVIEW | P_NO_PASS | P_OUT_OF_MAP);
513  }
514 
515  return (GET_MAP_FLAGS(nm, x, y) & P_BLOCKSVIEW);
516 }
517 
529 int blocks_magic(mapstruct *m, int x, int y)
530 {
531  if (!(m = get_map_from_coord(m, &x, &y))) {
533  }
534 
535  return (GET_MAP_FLAGS(m, x, y) & P_NO_MAGIC) || (GET_MAP_SPACE_PTR(m, x, y)->extra_flags & MSP_EXTRA_NO_MAGIC);
536 }
537 
555 static bool
556 blocked_self (object *op, mapstruct *m, int x, int y)
557 {
558  HARD_ASSERT(op != NULL);
559  HARD_ASSERT(m != NULL);
560 
561  op = HEAD(op);
562 
563  if (op->more == NULL) {
564  return false;
565  }
566 
567  for (object *tmp = op->more; tmp != NULL; tmp = tmp->more) {
568  int x2 = op->x + tmp->arch->clone.x;
569  int y2 = op->y + tmp->arch->clone.y;
570  mapstruct *m2 = get_map_from_coord2(op->map, &x2, &y2);
571  SOFT_ASSERT_RC(m2 != NULL, false, "Map is NULL");
572 
573  if (m2 == m && x2 == x && y2 == y) {
574  return true;
575  }
576  }
577 
578  return false;
579 }
580 
598 int blocked(object *op, mapstruct *m, int x, int y, int terrain)
599 {
600  int flags;
601  MapSpace *msp;
602 
603  flags = (msp = GET_MAP_SPACE_PTR(m, x, y))->flags;
604 
605  /* Flying objects can move over various terrains. */
606  if (op && QUERY_FLAG(op, FLAG_FLYING)) {
609  }
610 
611  /* First, look at the terrain. If we don't have a valid terrain flag,
612  * this is forbidden to enter. */
613  if (msp->move_flags & ~terrain) {
614  return flags | P_NO_TERRAIN;
615  }
616 
617  /* If the tile is either no_pass or has a closed door, and it's either
618  * not allowed to pass_thru this tile or the object doesn't have
619  * can_pass_thru, then handle no_pass/doors. */
620  if (flags & (P_NO_PASS | P_DOOR_CLOSED) && (!(flags & P_PASS_THRU) ||
621  op == NULL || !QUERY_FLAG(op, FLAG_CAN_PASS_THRU))) {
622  /* If the tile is either no_pass or a closed door that we cannot open,
623  * then we cannot enter the tile. */
624  if (flags & P_NO_PASS || (flags & P_DOOR_CLOSED &&
625  !door_try_open(op, m, x, y, true))) {
626  return flags;
627  }
628  }
629 
630  /* Below code deals specifically with object pointers, so if we don't have
631  * one, just return here. */
632  if (op == NULL) {
633  return 0;
634  }
635 
636  /* It's forbidden for other living objects to enter tiles with a monster. */
637  if (flags & P_IS_MONSTER && IS_LIVE(op) && !blocked_self(op, m, x, y)) {
638  return flags;
639  }
640 
641  /* Only other players can move onto tiles with a player, unless it's a PvP
642  * area, in which case they can't. */
643  if (flags & P_IS_PLAYER && IS_LIVE(op) && (op->type != PLAYER ||
644  (m->map_flags & MAP_FLAG_PVP && !(flags & P_NO_PVP) &&
645  !(msp->extra_flags & MSP_EXTRA_NO_PVP)))) {
646  return flags;
647  }
648 
649  /* Only players can move onto player-only tiles. */
650  if (flags & P_PLAYER_ONLY && op->type != PLAYER) {
651  return flags;
652  }
653 
654  /* Inventory checker, see if it blocks passage. */
655  if (flags & P_CHECK_INV && blocked_tile(op, m, x, y)) {
656  return flags;
657  }
658 
659  return 0;
660 }
661 
676 int blocked_tile(object *op, mapstruct *m, int x, int y)
677 {
678  object *tmp;
679 
680  for (tmp = GET_MAP_OB(m, x, y); tmp; tmp = tmp->above) {
681  /* This must be before the checks below. Code for inventory checkers. */
682  if (tmp->type == CHECK_INV && tmp->last_grace) {
683  if (QUERY_FLAG(tmp, FLAG_XRAYS) && op->type != PLAYER) {
684  continue;
685  }
686 
687  /* If last_sp is set, the player/monster needs an object,
688  * so we check for it. If they don't have it, they can't
689  * pass through this space. */
690  if (tmp->last_sp) {
691  if (check_inv(tmp, op) == NULL) {
692  return 1;
693  }
694 
695  continue;
696  } else {
697  /* In this case, the player must not have the object -
698  * if they do, they can't pass through. */
699 
700  if (check_inv(tmp, op) != NULL) {
701  return 1;
702  }
703 
704  continue;
705  }
706  }
707  }
708 
709  return 0;
710 }
711 
728 int arch_blocked(struct archetype *at, object *op, mapstruct *m, int x, int y)
729 {
730  archetype_t *tmp;
731  mapstruct *mt;
732  int xt, yt, t;
733 
734  if (op) {
735  t = op->terrain_flag;
736  } else {
737  t = TERRAIN_ALL;
738  }
739 
740  if (at == NULL) {
741  if (!(m = get_map_from_coord(m, &x, &y))) {
742  return P_OUT_OF_MAP;
743  }
744 
745  return blocked(op, m, x, y, t);
746  }
747 
748  for (tmp = at; tmp; tmp = tmp->more) {
749  xt = x + tmp->clone.x;
750  yt = y + tmp->clone.y;
751 
752  if (!(mt = get_map_from_coord(m, &xt, &yt))) {
753  return P_OUT_OF_MAP;
754  }
755 
756  if ((xt = blocked(op, mt, xt, yt, t))) {
757  return xt;
758  }
759  }
760 
761  return 0;
762 }
763 
774 static void load_objects(mapstruct *m, FILE *fp, int mapflags)
775 {
776  object *op = object_get();
777  /* To handle buttons correctly */
778  op->map = m;
779 
780  void *buffer = create_loader_buffer(fp);
781 
782  int rc;
783  while ((rc = load_object_buffer(buffer, op, mapflags)) != LL_EOF) {
784  if (rc == LL_MORE) {
785  LOG(ERROR, "Encountered tail object: %s", object_get_str(op));
786  continue;
787  }
788 
789  /* If the archetype for the object is null, means that we
790  * got an invalid object. Don't do anything with it - the game
791  * will not be able to do anything with it either. */
792  if (op->arch == NULL) {
793  LOG(ERROR, "Object without an archetype: %s", object_get_str(op));
794  continue;
795  }
796 
797  /* Do some safety for containers */
798  if (op->type == CONTAINER) {
799  /* Used for containers as link to players viewing it */
800  op->attacked_by = NULL;
801  op->attacked_by_count = 0;
802  object_weight_sum(op);
803  }
804 
805  if (op->type == MONSTER) {
807  }
808 
809  /* Important pre set for the animation/face of a object */
811  SET_ANIMATION(op, (NUM_ANIMATIONS(op) / NUM_FACINGS(op)) * op->direction + op->state);
812  }
813 
815 
816  if (QUERY_FLAG(op, FLAG_AUTO_APPLY)) {
817  object_auto_apply(op);
818  } else if ((mapflags & MAP_ORIGINAL) && op->randomitems) {
819  /* For fresh maps, create treasures */
820  treasure_generate(op->randomitems, op, op->level ? op->level : m->difficulty, op->type != TREASURE ? GT_APPLY : 0);
821  }
822 
823  op = object_get();
824  op->map = m;
825  }
826 
827  delete_loader_buffer(buffer);
828  object_destroy(op);
829 
832 }
833 
843 static void save_objects(mapstruct *m, FILE *fp, FILE *fp2)
844 {
845  int x, y;
846  object *ob, *next, *head, *tmp, *owner;
847  uint8_t unique;
848 
849  for (x = 0; x < MAP_WIDTH(m); x++) {
850  for (y = 0; y < MAP_HEIGHT(m); y++) {
851  unique = 0;
852 
853  for (ob = GET_MAP_OB(m, x, y); ob; ob = next) {
854  next = ob->above;
855 
856  if (ob->type == PLAYER) {
857  continue;
858  }
859 
860  head = HEAD(ob);
861 
862  if (QUERY_FLAG(head, FLAG_NO_SAVE)) {
863  object_remove(head, 0);
864  object_destroy(head);
865  continue;
866  } else if (QUERY_FLAG(head, FLAG_SPAWN_MOB)) {
867  /* Try to find the spawn point information. */
868  for (tmp = head->inv; tmp; tmp = tmp->below) {
869  if (tmp->type == SPAWN_POINT_INFO) {
870  if (OBJECT_VALID(tmp->owner, tmp->ownercount) && tmp->owner->type == SPAWN_POINT) {
871  tmp->owner->last_sp = -1;
872  tmp->owner->speed_left += 1.0f;
873  tmp->owner->enemy = NULL;
874  }
875  } else if (tmp->type == ARROW &&
876  tmp->attacked_by_count != 0) {
877  tmp->x = head->x;
878  tmp->y = head->y;
879 
880  if (unique || QUERY_FLAG(tmp, FLAG_UNIQUE)) {
881  object_save(tmp, fp2);
882  } else {
883  object_save(tmp, fp);
884  }
885  }
886  }
887 
888  object_remove(head, 0);
889  object_destroy(head);
890  continue;
891  } else if (head->type == SPAWN_POINT) {
892  if (OBJECT_VALID(head->enemy, head->enemy_count)) {
893  head->last_sp = -1;
894  head->speed_left += 1.0f;
895  head->enemy = NULL;
896  }
897  }
898 
899  /* Do not save tail parts. */
900  if (ob->head) {
901  continue;
902  }
903 
904  owner = object_owner(head);
905 
906  if (owner) {
907  object_owner_clear(head);
908  }
909 
910  if (QUERY_FLAG(head, FLAG_IS_FLOOR) && QUERY_FLAG(head, FLAG_UNIQUE)) {
911  unique = 1;
912  }
913 
914  if (unique || QUERY_FLAG(head, FLAG_UNIQUE)) {
915  object_save(head, fp2);
916  } else {
917  object_save(head, fp);
918  }
919  }
920  }
921  }
922 }
923 
933 void set_map_darkness(mapstruct *m, int value)
934 {
935  if (value < 0 || value > MAX_DARKNESS) {
936  value = MAX_DARKNESS;
937  }
938 
939  MAP_DARKNESS(m) = (int32_t) value;
940  m->light_value = (int32_t) global_darkness_table[value];
941 }
942 
949 {
950  return mempool_get(pool_map);
951 }
952 
959 static void allocate_map(mapstruct *m)
960 {
961  SOFT_ASSERT(m->spaces == NULL, "Map spaces are not NULL: %s", m->path);
962  SOFT_ASSERT(m->buttons == NULL, "Buttons are not NULL: %s", m->path);
963  SOFT_ASSERT(m->bitmap == NULL, "Bitmap is not NULL: %s", m->path);
964  SOFT_ASSERT(m->path_nodes == NULL, "Path nodes are not NULL: %s", m->path);
965 
966  m->in_memory = MAP_LOADING;
967 
968  m->spaces = ecalloc(1, MAP_WIDTH(m) * MAP_HEIGHT(m) * sizeof(MapSpace));
969  m->bitmap = emalloc(((MAP_WIDTH(m) + 31) / 32) * MAP_HEIGHT(m) *
970  sizeof(*m->bitmap));
971  m->path_nodes = emalloc(MAP_WIDTH(m) * MAP_HEIGHT(m) * sizeof(*m->path_nodes));
972 }
973 
985 mapstruct *get_empty_map(int sizex, int sizey)
986 {
987  mapstruct *m = get_linked_map();
988  m->width = sizex;
989  m->height = sizey;
990  allocate_map(m);
991 
993 
994  return m;
995 }
996 
997 void map_set_tile(mapstruct *m, int tile, const char *pathname)
998 {
999  char *path;
1000  shstr *path_sh;
1001  mapstruct *neighbor;
1002 
1003  HARD_ASSERT(m != NULL);
1004  HARD_ASSERT(pathname != NULL);
1005 
1006  SOFT_ASSERT(tile >= 0 && tile < TILED_NUM, "Invalid tile: %d", tile);
1007 
1008  if (m->tile_path[tile] != NULL) {
1009  LOG(BUG, "Tile location %d duplicated (%s <-> %s)", tile, m->path,
1010  m->tile_path[tile]);
1011  FREE_AND_CLEAR_HASH(m->tile_path[tile]);
1012  }
1013 
1014  if (map_path_isabs(pathname)) {
1015  path_sh = add_string(pathname);
1016  } else {
1017  path = map_get_path(m, pathname, m->map_flags & MAP_FLAG_UNIQUE, NULL);
1018  path_sh = add_string(path);
1019  efree(path);
1020  }
1021 
1022  m->tile_path[tile] = path_sh;
1023  neighbor = has_been_loaded_sh(path_sh);
1024 
1025  /* If the neighboring map tile has been loaded, set up the map pointers. */
1026  if (neighbor != NULL && (neighbor->in_memory == MAP_IN_MEMORY ||
1027  neighbor->in_memory == MAP_LOADING)) {
1028  int dest_tile = map_tiled_reverse[tile];
1029 
1030  m->tile_map[tile] = neighbor;
1031 
1032  if (neighbor->tile_path[dest_tile] == NULL ||
1033  neighbor->tile_path[dest_tile] == m->path) {
1034  neighbor->tile_map[dest_tile] = m;
1035  }
1036  }
1037 }
1038 
1055 mapstruct *load_original_map(const char *filename, mapstruct *originator,
1056  int flags)
1057 {
1058  FILE *fp;
1059  mapstruct *m;
1060  char pathname[HUGE_BUF], split[MAX_BUF];
1061  const char *basename;
1062  size_t pos, coords_idx, coords_len;
1063  int16_t old_style_z;
1064 
1065  /* No sense in doing this all for random maps, it will all fail anyways. */
1066  if (!strncmp(filename, "/random/", 8)) {
1067  return NULL;
1068  }
1069 
1070  snprintf(pathname, sizeof(pathname), "%s%s",
1071  *filename != '/' && *filename != '.' ? "/" : "",
1072  flags & MAP_PLAYER_UNIQUE ? filename : create_pathname(filename));
1073 
1074  char *real_path = NULL;
1075  if (flags & MAP_PLAYER_UNIQUE) {
1076  real_path = path_basename(pathname);
1077  string_replace_char(real_path, "$", '/');
1078  }
1079 
1080  if (flags & MAP_PLAYER_UNIQUE && !path_exists(pathname)) {
1081  fp = fopen(create_pathname(real_path), "rb");
1082  } else {
1083  fp = fopen(pathname, "rb");
1084  }
1085 
1086  if (fp == NULL && originator == NULL) {
1087  log_error("Can't open map file %s", pathname);
1088  return NULL;
1089  }
1090 
1091  m = get_linked_map();
1092 
1093  FREE_AND_COPY_HASH(m->path, filename);
1094 
1095  if (flags & MAP_PLAYER_UNIQUE) {
1096  m->map_flags |= MAP_FLAG_UNIQUE;
1097  }
1098 
1099  if (fp != NULL && !load_map_header(m, fp)) {
1100  log_error("Failure loading map header for %s, flags=%d",
1101  filename, flags);
1102  delete_map(m);
1103  fclose(fp);
1104  return NULL;
1105  }
1106 
1107  basename = strrchr(filename, flags & MAP_PLAYER_UNIQUE ? '$' : '/');
1108  if (basename == NULL) {
1109  basename = filename;
1110  } else {
1111  basename++;
1112  }
1113 
1114  pos = coords_idx = 0;
1115  coords_len = 0;
1116  old_style_z = 0;
1117 
1118  while (string_get_word(basename, &pos, '_', split, sizeof(split), 0)) {
1119  if (strlen(split) == 1) {
1120  old_style_z = string_isdigit(split) ? atoi(split) :
1121  'a' - *split - 1;
1122  } else if (strlen(split) > 3) {
1123  /* TODO: remove this hack (avoids parsing old-style naming
1124  * convention like map_0101) */
1125  continue;
1126  }
1127 
1128  if (string_isdigit(split)) {
1129  coords_len += strlen(split) + 1;
1130  m->coords[coords_idx] = atoi(split);
1131  coords_idx++;
1132  }
1133 
1134  if (coords_idx >= arraysize(m->coords)) {
1135  break;
1136  }
1137  }
1138 
1139  if (coords_idx >= 2) {
1140  size_t i;
1141  char *cp, *cp2, path[HUGE_BUF];
1142 
1143  const char *path_cp = pathname;
1144  if (real_path != NULL) {
1145  path_cp = create_pathname(real_path);
1146  }
1147  cp = string_sub(path_cp, 0, -coords_len);
1148 
1149  for (i = 0; i < TILED_NUM; i++) {
1150  if (m->tile_path[i] != NULL) {
1151  continue;
1152  }
1153 
1154  snprintf(VS(path), "%s_%d_%d", cp,
1155  m->coords[0] + map_tiled_coords[i][0],
1156  m->coords[1] + map_tiled_coords[i][1]);
1157 
1158  if (m->coords[2] + map_tiled_coords[i][2] != 0) {
1159  snprintfcat(VS(path), "_%d",
1160  m->coords[2] + map_tiled_coords[i][2]);
1161  }
1162 
1163  if ((!(flags & MAP_NO_DYNAMIC) && ((m->coords[2] >= 0 &&
1164  i != TILED_UP && i != TILED_DOWN) || (m->coords[2] > 0 &&
1165  i != TILED_UP))) || path_exists(path)) {
1166  cp2 = path_basename(path);
1167  map_set_tile(m, i, cp2);
1168  efree(cp2);
1169  }
1170  }
1171 
1172  efree(cp);
1173  } else {
1174  m->coords[2] = old_style_z;
1175  }
1176 
1177  if (m->level_max == 0 && m->coords[2] >= 0) {
1178  m->level_max = INT8_MAX;
1179  } else if (m->level_max == 0 && m->level_min == 0) {
1180  m->level_min = m->level_max = m->coords[2];
1181  }
1182 
1183  if (fp == NULL && originator != NULL) {
1184  FREE_AND_COPY_HASH(m->name, originator->name);
1185 
1186  if (originator->bg_music != NULL) {
1187  FREE_AND_COPY_HASH(m->bg_music, originator->bg_music);
1188  }
1189 
1190  if (originator->weather != NULL) {
1191  FREE_AND_COPY_HASH(m->weather, originator->weather);
1192  }
1193 
1194  m->darkness = originator->darkness;
1195  m->difficulty = originator->difficulty;
1196  m->light_value = originator->light_value;
1197  m->width = originator->width;
1198  m->height = originator->height;
1199  m->map_flags = originator->map_flags | MAP_FLAG_NO_SAVE;
1200  }
1201 
1202  allocate_map(m);
1203 
1204  m->in_memory = MAP_LOADING;
1205 
1206  if (fp != NULL) {
1207  load_objects(m, fp, (flags & (MAP_BLOCK | MAP_STYLE)) | MAP_ORIGINAL);
1208  fclose(fp);
1209  } else {
1210  m->in_memory = MAP_IN_MEMORY;
1211  }
1212 
1213  if (!MAP_DIFFICULTY(m)) {
1214  LOG(BUG, "Map %s has difficulty 0. Changing to 1.", filename);
1215  MAP_DIFFICULTY(m) = 1;
1216  }
1217 
1218  set_map_reset_time(m);
1219 
1220  if (real_path != NULL) {
1221  efree(real_path);
1222  }
1223 
1224  return m;
1225 }
1226 
1236 {
1237  FILE *fp;
1238  char buf[HUGE_BUF];
1239 
1240  if (!m->tmpname) {
1241  LOG(BUG, "No temporary filename for map %s! Fallback to original!", m->path);
1242  snprintf(buf, sizeof(buf), "%s", m->path);
1243  delete_map(m);
1244  m = load_original_map(buf, NULL, 0);
1245  return m;
1246  }
1247 
1248  fp = fopen(m->tmpname, "rb");
1249 
1250  if (!fp) {
1251  if (!strncmp(m->path, "/random/", 8)) {
1252  return NULL;
1253  }
1254 
1255  LOG(BUG, "Can't open temporary map %s! Fallback to original!", m->tmpname);
1256  snprintf(buf, sizeof(buf), "%s", m->path);
1257  delete_map(m);
1258  m = load_original_map(buf, NULL, 0);
1259  return m;
1260  }
1261 
1262  if (!load_map_header(m, fp)) {
1263  LOG(BUG, "Error loading map header for %s (%s)! Fallback to original!", m->path, m->tmpname);
1264  snprintf(buf, sizeof(buf), "%s", m->path);
1265  delete_map(m);
1266  m = load_original_map(buf, NULL, 0);
1267  fclose(fp);
1268  return m;
1269  }
1270 
1271  allocate_map(m);
1272 
1273  m->in_memory = MAP_LOADING;
1274  load_objects (m, fp, 0);
1275  fclose(fp);
1276  return m;
1277 }
1278 
1285 {
1286  int i, j, unique = 0;
1287  object *op, *next;
1288 
1289  for (i = 0; i < MAP_WIDTH(m); i++) {
1290  for (j = 0; j < MAP_HEIGHT(m); j++) {
1291  unique = 0;
1292 
1293  for (op = GET_MAP_OB(m, i, j); op; op = next) {
1294  next = op->above;
1295 
1296  if (QUERY_FLAG(op, FLAG_IS_FLOOR) && QUERY_FLAG(op, FLAG_UNIQUE)) {
1297  unique = 1;
1298  }
1299 
1300  if (op->head == NULL && (QUERY_FLAG(op, FLAG_UNIQUE) || unique)) {
1301  if (QUERY_FLAG(op, FLAG_IS_LINKED)) {
1303  }
1304 
1305  object_remove(op, 0);
1306  object_destroy(op);
1307  }
1308  }
1309  }
1310  }
1311 }
1312 
1319 {
1320  FILE *fp;
1321  int count;
1322  char firstname[HUGE_BUF];
1323 
1324  for (count = 0; count < 10; count++) {
1325  snprintf(firstname, sizeof(firstname), "%s.v%02d", create_items_path(m->path), count);
1326  fp = fopen(firstname, "rb");
1327 
1328  if (fp != NULL) {
1329  break;
1330  }
1331  }
1332 
1333  /* If we get here, we did not find any map. */
1334  if (fp == NULL) {
1335  return;
1336  }
1337 
1338  m->in_memory = MAP_LOADING;
1339 
1340  /* If we have loaded unique items from */
1341  if (m->tmpname == NULL) {
1343  }
1344 
1345  load_objects(m, fp, 0);
1346  fclose(fp);
1347 }
1348 
1363 int new_save_map(mapstruct *m, int flag)
1364 {
1365  FILE *fp, *fp2;
1366  char filename[MAX_BUF], buf[MAX_BUF];
1367 
1368  if (flag && !*m->path) {
1369  return -1;
1370  }
1371 
1372  if (flag || MAP_UNIQUE(m)) {
1373  if (!MAP_UNIQUE(m)) {
1374  snprintf(filename, sizeof(filename), "%s", create_pathname(m->path));
1375  } else {
1376  /* This ensures we always reload from original maps */
1377  if (MAP_NOSAVE(m)) {
1378  return 0;
1379  }
1380 
1381  snprintf(filename, sizeof(filename), "%s", m->path);
1382  }
1383 
1384  path_ensure_directories(filename);
1385 
1386  fp = fopen(filename, "w");
1387  } else {
1388  if (m->tmpname == NULL) {
1389  char path[MAX_BUF];
1390  int fd;
1391 
1392  snprintf(path, sizeof(path), "%s/tmp/XXXXXX", settings.datapath);
1393  m->tmpname = estrdup(path);
1394 
1395  fd = mkstemp(m->tmpname);
1396 
1397  if (fd == -1) {
1398  LOG(ERROR, "Can't create a temporary file: %d (%s)", errno,
1399  strerror(errno));
1400  return -1;
1401  }
1402 
1403  fp = fdopen(fd, "w");
1404 
1405  if (fp == NULL) {
1406  LOG(ERROR, "Can't open file %s for saving: %d (%s)",
1407  filename, errno, strerror(errno));
1408  close(fd);
1409  return -1;
1410  }
1411  } else {
1412  fp = fopen(m->tmpname, "w");
1413  }
1414 
1415  snprintf(VS(filename), "%s", m->tmpname);
1416  }
1417 
1418  if (fp == NULL) {
1419  LOG(ERROR, "Can't open file %s for saving: %d (%s)", filename,
1420  errno, strerror(errno));
1421  return -1;
1422  }
1423 
1424  m->in_memory = MAP_SAVING;
1425 
1426  save_map_header(m, fp, flag);
1427 
1428  /* Save unique items into fp2 */
1429  fp2 = fp;
1430 
1431  if (!MAP_UNIQUE(m)) {
1432  snprintf(buf, sizeof(buf), "%s.v00", create_items_path(m->path));
1433 
1434  if ((fp2 = fopen(buf, "w")) == NULL) {
1435  LOG(BUG, "Can't open unique items file %s", buf);
1436  }
1437 
1438  save_objects(m, fp, fp2);
1439 
1440  if (fp2) {
1441  if (ftell(fp2) == 0) {
1442  fclose(fp2);
1443  unlink(buf);
1444  } else {
1445  fclose(fp2);
1446  chmod(buf, SAVE_MODE);
1447  }
1448  }
1449  } else {
1450  /* Otherwise to the same file, like apartments */
1451  save_objects(m, fp, fp);
1452  }
1453 
1454  if (fp) {
1455  fclose(fp);
1456  }
1457 
1458  chmod(filename, SAVE_MODE);
1459  return 0;
1460 }
1461 
1468 {
1469  int x, y;
1470  object *ob, *head;
1471 
1472  for (x = 0; x < MAP_WIDTH(m); x++) {
1473  for (y = 0; y < MAP_HEIGHT(m); y++) {
1474  while ((ob = GET_MAP_OB(m, x, y)) != NULL) {
1475  head = HEAD(ob);
1476 
1477  object_remove(head, 0);
1478  object_destroy(head);
1479  }
1480  }
1481  }
1482 }
1483 
1493 void free_map(mapstruct *m, int flag)
1494 {
1495  int i;
1496 
1497  if (!m->in_memory) {
1498  return;
1499  }
1500 
1502 
1503  if (m->buttons) {
1505  }
1506 
1507  if (flag && m->spaces) {
1508  free_all_objects(m);
1509  }
1510 
1515  FREE_AND_NULL_PTR(m->msg);
1516  m->buttons = NULL;
1517  m->first_light = NULL;
1518 
1519  for (i = 0; i < TILED_NUM; i++) {
1520  /* Delete the backlinks in other tiled maps to our map */
1521  if (m->tile_map[i]) {
1522  if (m->tile_map[i]->tile_map[map_tiled_reverse[i]] && m->tile_map[i]->tile_map[map_tiled_reverse[i]] != m) {
1523  LOG(BUG, "Freeing map %s linked to %s which links back to another map.", STRING_SAFE(m->path), STRING_SAFE(m->tile_map[i]->path));
1524  }
1525 
1526  m->tile_map[i]->tile_map[map_tiled_reverse[i]] = NULL;
1527  m->tile_map[i] = NULL;
1528  }
1529 
1531  }
1532 
1533  if (m->events) {
1534  map_event *tmp, *next;
1535 
1536  for (tmp = m->events; tmp; tmp = next) {
1537  next = tmp->next;
1538  map_event_free(tmp);
1539  }
1540 
1541  m->events = NULL;
1542  }
1543 
1545  FREE_AND_NULL_PTR(m->path_nodes);
1546  m->in_memory = MAP_SWAPPED;
1547 }
1548 
1556 {
1557  HARD_ASSERT(m != NULL);
1558 
1559  if (m->in_memory == MAP_IN_MEMORY) {
1560  /* Change to MAP_SAVING, even though we are not,
1561  * so that object_remove doesn't do as much work. */
1562  m->in_memory = MAP_SAVING;
1563  free_map(m, 1);
1564  } else {
1566  }
1567 
1568  mempool_return(pool_map, m);
1569 }
1570 
1584 mapstruct *ready_map_name(const char *name, mapstruct *originator, int flags)
1585 {
1586  mapstruct *m;
1587  shstr *name_sh;
1588 
1589  if (!name) {
1590  return NULL;
1591  }
1592 
1593  /* Have we been at this level before? */
1594  if (flags & MAP_NAME_SHARED) {
1595  m = has_been_loaded_sh(name);
1596  } else {
1597  /* Create a temporary shared string for the name if not explicitly given
1598  * */
1599  name_sh = add_string(name);
1600  m = has_been_loaded_sh(name_sh);
1601  free_string_shared(name_sh);
1602  }
1603 
1604  /* Map is good to go, so just return it */
1605  if (m && (m->in_memory == MAP_LOADING || m->in_memory == MAP_IN_MEMORY)) {
1606  return m;
1607  }
1608 
1609  if (!(flags & MAP_PLAYER_UNIQUE) && string_startswith(name, settings.datapath)) {
1610  flags |= MAP_PLAYER_UNIQUE;
1611  }
1612 
1613  /* Unique maps always get loaded from their original location, and never
1614  * a temp location. Likewise, if map_flush is set, or we have never loaded
1615  * this map, load it now. I removed the reset checking from here -
1616  * it seems the probability of a player trying to enter a map that should
1617  * reset but hasn't yet is quite low, and removing that makes this function
1618  * a bit cleaner (and players probably shouldn't rely on exact timing for
1619  * resets in any case - if they really care, they should use the 'maps
1620  * command. */
1621  if (!m || (flags & (MAP_FLUSH | MAP_PLAYER_UNIQUE))) {
1622  /* First visit or time to reset */
1623  if (m) {
1624  /* Doesn't make much difference */
1625  clean_tmp_map(m);
1626  delete_map(m);
1627  }
1628 
1629  /* Create and load a map */
1630  if (!(m = load_original_map(name, originator,
1631  (flags & (MAP_PLAYER_UNIQUE | MAP_NO_DYNAMIC))))) {
1632  return NULL;
1633  }
1634 
1635  /* If a player unique map, no extra unique object file to load.
1636  * if from the editor, likewise. */
1637  if (!(flags & (MAP_FLUSH | MAP_PLAYER_UNIQUE))) {
1639  }
1640  } else {
1641  /* If in this loop, we found a temporary map, so load it up. */
1642  m = load_temporary_map(m);
1643 
1644  if (m == NULL) {
1645  return NULL;
1646  }
1647 
1649 
1650  clean_tmp_map(m);
1651  m->in_memory = MAP_IN_MEMORY;
1652  }
1653 
1654  return m;
1655 }
1656 
1663 {
1664  if (m->tmpname == NULL) {
1665  return;
1666  }
1667 
1668  unlink(m->tmpname);
1669 
1670  efree(m->tmpname);
1671  m->tmpname = NULL;
1672 }
1673 
1677 void free_all_maps(void)
1678 {
1679  mapstruct *map, *tmp;
1680 
1681  DL_FOREACH_SAFE(first_map, map, tmp)
1682  {
1683  /* I think some of the callers above before it gets here set this to be
1684  * saving, but we still want to free this data */
1685  if (map->in_memory == MAP_SAVING) {
1686  map->in_memory = MAP_IN_MEMORY;
1687  }
1688 
1689  delete_map(map);
1690  }
1691 }
1692 
1705 void
1706 update_position (mapstruct *m, int x, int y)
1707 {
1708  HARD_ASSERT(m != NULL);
1709  HARD_ASSERT(GET_MAP_FLAGS(m, x, y) & P_NEED_UPDATE);
1710 
1711 #if DEBUG_OLDFLAGS
1712  int old_flags = GET_MAP_FLAGS(m, x, y) & ~P_NEED_UPDATE;
1713  int old_move_flags = GET_MAP_MOVE_FLAGS(m, x, y);
1714 #endif
1715 
1716  int flags = 0;
1717  int move_flags = 0;
1718 
1719  for (object *tmp = GET_MAP_OB(m, x, y); tmp != NULL; tmp = tmp->above) {
1720  if (QUERY_FLAG(tmp, FLAG_PLAYER_ONLY)) {
1721  flags |= P_PLAYER_ONLY;
1722  }
1723 
1724  if (tmp->type == CHECK_INV) {
1725  flags |= P_CHECK_INV;
1726  }
1727 
1728  if (QUERY_FLAG(tmp, FLAG_IS_PLAYER)) {
1729  flags |= P_IS_PLAYER;
1730  }
1731 
1732  if (QUERY_FLAG(tmp, FLAG_DOOR_CLOSED)) {
1733  flags |= P_DOOR_CLOSED;
1734  }
1735 
1736  if (QUERY_FLAG(tmp, FLAG_MONSTER)) {
1737  flags |= P_IS_MONSTER;
1738  }
1739 
1740  if (QUERY_FLAG(tmp, FLAG_NO_MAGIC)) {
1741  flags |= P_NO_MAGIC;
1742  }
1743 
1744  if (QUERY_FLAG(tmp, FLAG_BLOCKSVIEW)) {
1745  flags |= P_BLOCKSVIEW;
1746  }
1747 
1748  if (QUERY_FLAG(tmp, FLAG_WALK_ON)) {
1749  flags |= P_WALK_ON;
1750  }
1751 
1752  if (QUERY_FLAG(tmp, FLAG_WALK_OFF)) {
1753  flags |= P_WALK_OFF;
1754  }
1755 
1756  if (QUERY_FLAG(tmp, FLAG_FLY_ON)) {
1757  flags |= P_FLY_ON;
1758  }
1759 
1760  if (QUERY_FLAG(tmp, FLAG_FLY_OFF)) {
1761  flags |= P_FLY_OFF;
1762  }
1763 
1764  if (QUERY_FLAG(tmp, FLAG_NO_PASS)) {
1765  flags |= P_NO_PASS;
1766  }
1767 
1768  if (QUERY_FLAG(tmp, FLAG_IS_FLOOR)) {
1769  move_flags |= tmp->terrain_type;
1770  }
1771 
1772  if (QUERY_FLAG(tmp, FLAG_NO_PVP)) {
1773  flags |= P_NO_PVP;
1774  }
1775 
1776  if (tmp->type == MAGIC_MIRROR) {
1777  flags |= P_MAGIC_MIRROR;
1778  }
1779 
1780  if (tmp->type == EXIT) {
1781  flags |= P_IS_EXIT;
1782  }
1783 
1784  if (QUERY_FLAG(tmp, FLAG_OUTDOOR)) {
1785  flags |= P_OUTDOOR;
1786  }
1787 
1788  if (flags & (P_NO_PASS | P_DOOR_CLOSED)) {
1789  if (!QUERY_FLAG(tmp, FLAG_PASS_THRU)) {
1790  flags &= ~P_PASS_THRU;
1791  } else {
1792  flags |= P_PASS_THRU;
1793  }
1794  }
1795  }
1796 
1797 #if DEBUG_OLDFLAGS
1798  if (flags == old_flags && move_flags == old_move_flags) {
1799  LOG(DEVEL,
1800  "New flags (%x, %x) are the same as old ones (%x, %x): %s %d,%d",
1801  flags, move_flags, old_flags, old_move_flags, m->path, x, y);
1802  }
1803 #endif
1804 
1805  SET_MAP_FLAGS(m, x, y, flags);
1806  SET_MAP_MOVE_FLAGS(m, x, y, move_flags);
1807 }
1808 
1815 {
1816  uint32_t timeout = MAP_RESET_TIMEOUT(map);
1817 
1818  if (timeout == 0) {
1819  timeout = MAP_DEFAULTRESET;
1820  }
1821 
1822  if (timeout >= MAP_MAXRESET) {
1823  timeout = MAP_MAXRESET;
1824  }
1825 
1826  MAP_WHEN_RESET(map) = seconds() + timeout;
1827 }
1828 
1839 {
1840  HARD_ASSERT(m != NULL);
1841 
1842  SOFT_ASSERT_RC(tiled >= 0 && tiled < TILED_NUM, NULL, "Invalid tile: %d",
1843  tiled);
1844 
1845  if (m->tile_map[tiled] == NULL ||
1846  m->tile_map[tiled]->in_memory != MAP_IN_MEMORY) {
1847  if (!load_and_link_tiled_map(m, tiled)) {
1848  return NULL;
1849  }
1850  }
1851 
1852  return m->tile_map[tiled];
1853 }
1854 
1870 {
1871  /* m should never be null, but if a tiled map fails to load below, it
1872  * could happen. */
1873  if (!m) {
1874  return NULL;
1875  }
1876 
1877  /* Simple case - coordinates are within this local map. */
1878  if (*x >= 0 && *x < MAP_WIDTH(m) && *y >= 0 && *y < MAP_HEIGHT(m)) {
1879  return m;
1880  }
1881 
1882  /* West, Northwest or Southwest (3, 7 or 6) */
1883  if (*x < 0) {
1884  /* Northwest */
1885  if (*y < 0) {
1886  if (!m->tile_map[7] || m->tile_map[7]->in_memory != MAP_IN_MEMORY) {
1887  if (!load_and_link_tiled_map(m, 7)) {
1888  return NULL;
1889  }
1890  }
1891 
1892  *y += MAP_HEIGHT(m->tile_map[7]);
1893  *x += MAP_WIDTH(m->tile_map[7]);
1894  return get_map_from_coord(m->tile_map[7], x, y);
1895  }
1896 
1897  /* Southwest */
1898  if (*y >= MAP_HEIGHT(m)) {
1899  if (!m->tile_map[6] || m->tile_map[6]->in_memory != MAP_IN_MEMORY) {
1900  if (!load_and_link_tiled_map(m, 6)) {
1901  return NULL;
1902  }
1903  }
1904 
1905  *y -= MAP_HEIGHT(m);
1906  *x += MAP_WIDTH(m->tile_map[6]);
1907  return get_map_from_coord(m->tile_map[6], x, y);
1908  }
1909 
1910  /* West */
1911  if (!m->tile_map[3] || m->tile_map[3]->in_memory != MAP_IN_MEMORY) {
1912  if (!load_and_link_tiled_map(m, 3)) {
1913  return NULL;
1914  }
1915  }
1916 
1917  *x += MAP_WIDTH(m->tile_map[3]);
1918  return get_map_from_coord(m->tile_map[3], x, y);
1919  }
1920 
1921  /* East, Northeast or Southeast (1, 4 or 5) */
1922  if (*x >= MAP_WIDTH(m)) {
1923  /* Northeast */
1924  if (*y < 0) {
1925  if (!m->tile_map[4] || m->tile_map[4]->in_memory != MAP_IN_MEMORY) {
1926  if (!load_and_link_tiled_map(m, 4)) {
1927  return NULL;
1928  }
1929  }
1930 
1931  *y += MAP_HEIGHT(m->tile_map[4]);
1932  *x -= MAP_WIDTH(m);
1933  return get_map_from_coord(m->tile_map[4], x, y);
1934  }
1935 
1936  /* Southeast */
1937  if (*y >= MAP_HEIGHT(m)) {
1938  if (!m->tile_map[5] || m->tile_map[5]->in_memory != MAP_IN_MEMORY) {
1939  if (!load_and_link_tiled_map(m, 5)) {
1940  return NULL;
1941  }
1942  }
1943 
1944  *y -= MAP_HEIGHT(m);
1945  *x -= MAP_WIDTH(m);
1946  return get_map_from_coord(m->tile_map[5], x, y);
1947  }
1948 
1949  /* East */
1950  if (!m->tile_map[1] || m->tile_map[1]->in_memory != MAP_IN_MEMORY) {
1951  if (!load_and_link_tiled_map(m, 1)) {
1952  return NULL;
1953  }
1954  }
1955 
1956  *x -= MAP_WIDTH(m);
1957  return get_map_from_coord(m->tile_map[1], x, y);
1958  }
1959 
1960  /* Because we have tested x above, we don't need to check for
1961  * Northwest, Southwest, Northeast and Northwest here again. */
1962  if (*y < 0) {
1963  if (!m->tile_map[0] || m->tile_map[0]->in_memory != MAP_IN_MEMORY) {
1964  if (!load_and_link_tiled_map(m, 0)) {
1965  return NULL;
1966  }
1967  }
1968 
1969  *y += MAP_HEIGHT(m->tile_map[0]);
1970  return get_map_from_coord(m->tile_map[0], x, y);
1971  }
1972 
1973  if (*y >= MAP_HEIGHT(m)) {
1974  if (!m->tile_map[2] || m->tile_map[2]->in_memory != MAP_IN_MEMORY) {
1975  if (!load_and_link_tiled_map(m, 2)) {
1976  return NULL;
1977  }
1978  }
1979 
1980  *y -= MAP_HEIGHT(m);
1981  return get_map_from_coord(m->tile_map[2], x, y);
1982  }
1983 
1984  return NULL;
1985 }
1986 
2001 {
2002  if (!m) {
2003  *x = 0;
2004  return NULL;
2005  }
2006 
2007  /* Simple case - coordinates are within this local map. */
2008  if (*x >= 0 && *x < MAP_WIDTH(m) && *y >= 0 && *y < MAP_HEIGHT(m)) {
2009  return m;
2010  }
2011 
2012  /* West, Northwest or Southwest (3, 7 or 6) */
2013  if (*x < 0) {
2014  /* Northwest */
2015  if (*y < 0) {
2016  if (!m->tile_path[7]) {
2017  *x = 0;
2018  return NULL;
2019  }
2020 
2021  if (!m->tile_map[7] || m->tile_map[7]->in_memory != MAP_IN_MEMORY) {
2022  *x = -1;
2023  return NULL;
2024  }
2025 
2026  *y += MAP_HEIGHT(m->tile_map[7]);
2027  *x += MAP_WIDTH(m->tile_map[7]);
2028 
2029  return get_map_from_coord2(m->tile_map[7], x, y);
2030  }
2031 
2032  /* Southwest */
2033  if (*y >= MAP_HEIGHT(m)) {
2034  if (!m->tile_path[6]) {
2035  *x = 0;
2036  return NULL;
2037  }
2038 
2039  if (!m->tile_map[6] || m->tile_map[6]->in_memory != MAP_IN_MEMORY) {
2040  *x = -1;
2041  return NULL;
2042  }
2043 
2044  *y -= MAP_HEIGHT(m);
2045  *x += MAP_WIDTH(m->tile_map[6]);
2046 
2047  return get_map_from_coord2(m->tile_map[6], x, y);
2048  }
2049 
2050  /* West */
2051  if (!m->tile_path[3]) {
2052  *x = 0;
2053  return NULL;
2054  }
2055 
2056  if (!m->tile_map[3] || m->tile_map[3]->in_memory != MAP_IN_MEMORY) {
2057  *x = -1;
2058  return NULL;
2059  }
2060 
2061  *x += MAP_WIDTH(m->tile_map[3]);
2062  return get_map_from_coord2(m->tile_map[3], x, y);
2063  }
2064 
2065  /* East, Northeast or Southeast (1, 4 or 5) */
2066  if (*x >= MAP_WIDTH(m)) {
2067  /* Northeast */
2068  if (*y < 0) {
2069  if (!m->tile_path[4]) {
2070  *x = 0;
2071  return NULL;
2072  }
2073 
2074  if (!m->tile_map[4] || m->tile_map[4]->in_memory != MAP_IN_MEMORY) {
2075  *x = -1;
2076  return NULL;
2077  }
2078 
2079  *y += MAP_HEIGHT(m->tile_map[4]);
2080  *x -= MAP_WIDTH(m);
2081 
2082  return get_map_from_coord2(m->tile_map[4], x, y);
2083  }
2084 
2085  /* Southeast */
2086  if (*y >= MAP_HEIGHT(m)) {
2087  if (!m->tile_path[5]) {
2088  *x = 0;
2089  return NULL;
2090  }
2091 
2092  if (!m->tile_map[5] || m->tile_map[5]->in_memory != MAP_IN_MEMORY) {
2093  *x = -1;
2094  return NULL;
2095  }
2096 
2097  *y -= MAP_HEIGHT(m);
2098  *x -= MAP_WIDTH(m);
2099 
2100  return get_map_from_coord2(m->tile_map[5], x, y);
2101  }
2102 
2103  /* East */
2104  if (!m->tile_path[1]) {
2105  *x = 0;
2106  return NULL;
2107  }
2108 
2109  if (!m->tile_map[1] || m->tile_map[1]->in_memory != MAP_IN_MEMORY) {
2110  *x = -1;
2111  return NULL;
2112  }
2113 
2114  *x -= MAP_WIDTH(m);
2115  return get_map_from_coord2(m->tile_map[1], x, y);
2116  }
2117 
2118  /* Because we have tested x above, we don't need to check for
2119  * Northwest, Southwest, Northeast and Northwest here again. */
2120  if (*y < 0) {
2121  if (!m->tile_path[0]) {
2122  *x = 0;
2123  return NULL;
2124  }
2125 
2126  if (!m->tile_map[0] || m->tile_map[0]->in_memory != MAP_IN_MEMORY) {
2127  *x = -1;
2128  return NULL;
2129  }
2130 
2131  *y += MAP_HEIGHT(m->tile_map[0]);
2132 
2133  return get_map_from_coord2(m->tile_map[0], x, y);
2134  }
2135 
2136  if (*y >= MAP_HEIGHT(m)) {
2137  if (!m->tile_path[2]) {
2138  *x = 0;
2139  return NULL;
2140  }
2141 
2142  if (!m->tile_map[2] || m->tile_map[2]->in_memory != MAP_IN_MEMORY) {
2143  *x = -1;
2144  return NULL;
2145  }
2146 
2147  *y -= MAP_HEIGHT(m);
2148  return get_map_from_coord2(m->tile_map[2], x, y);
2149  }
2150 
2151  *x = 0;
2152  return NULL;
2153 }
2154 
2165 static int
2167 {
2168  int q;
2169  if (!y) {
2170  q = -300 * x;
2171  } else {
2172  q = x * 100 / y;
2173  }
2174 
2175  if (y > 0) {
2176  if (q < -242) {
2177  return EAST;
2178  }
2179 
2180  if (q < -41) {
2181  return NORTHEAST;
2182  }
2183 
2184  if (q < 41) {
2185  return NORTH;
2186  }
2187 
2188  if (q < 242) {
2189  return NORTHWEST;
2190  }
2191 
2192  return WEST;
2193  }
2194 
2195  if (q < -242) {
2196  return WEST;
2197  }
2198 
2199  if (q < -41) {
2200  return SOUTHWEST;
2201  }
2202 
2203  if (q < 41) {
2204  return SOUTH;
2205  }
2206 
2207  if (q < 242) {
2208  return SOUTHEAST;
2209  }
2210 
2211  return EAST;
2212 }
2213 
2238 int get_rangevector(object *op1, object *op2, rv_vector *retval, int flags)
2239 {
2240  if (!get_rangevector_from_mapcoords(op1->map, op1->x, op1->y, op2->map, op2->x, op2->y, retval, flags | RV_NO_DISTANCE)) {
2241  return 0;
2242  }
2243 
2244  retval->part = op1;
2245 
2246  /* If this is multipart, find the closest part now */
2247  if (!(flags & RV_IGNORE_MULTIPART) && op1->more) {
2248  object *tmp, *best = NULL;
2249  int best_distance = retval->distance_x * retval->distance_x + retval->distance_y * retval->distance_y, tmpi;
2250 
2251  /* we just take the offset of the piece to head to figure
2252  * distance instead of doing all that work above again
2253  * since the distance fields we set above are positive in the
2254  * same axis as is used for multipart objects, the simply arithmetic
2255  * below works. */
2256  for (tmp = op1->more; tmp; tmp = tmp->more) {
2257  tmpi = (retval->distance_x - tmp->arch->clone.x) * (retval->distance_x - tmp->arch->clone.x) + (retval->distance_y - tmp->arch->clone.y) * (retval->distance_y - tmp->arch->clone.y);
2258 
2259  if (tmpi < best_distance) {
2260  best_distance = tmpi;
2261  best = tmp;
2262  }
2263  }
2264 
2265  if (best) {
2266  retval->distance_x -= best->arch->clone.x;
2267  retval->distance_y -= best->arch->clone.y;
2268  retval->part = best;
2269  }
2270  }
2271 
2272  retval->distance = isqrt(retval->distance_x * retval->distance_x + retval->distance_y * retval->distance_y);
2273  retval->direction = coords_distance_to_dir(-retval->distance_x, -retval->distance_y);
2274 
2275  return 1;
2276 }
2277 
2297 int get_rangevector_from_mapcoords(mapstruct *map1, int x, int y, mapstruct *map2, int x2, int y2, rv_vector *retval, int flags)
2298 {
2299  retval->part = NULL;
2300  retval->distance_z = 0;
2301 
2302  if (map1 == map2) {
2303  retval->distance_x = x2 - x;
2304  retval->distance_y = y2 - y;
2305  } else if (map1->tile_map[TILED_NORTH] == map2) {
2306  retval->distance_x = x2 - x;
2307  retval->distance_y = -(y + (MAP_HEIGHT(map2) - y2));
2308  } else if (map1->tile_map[TILED_EAST] == map2) {
2309  retval->distance_y = y2 - y;
2310  retval->distance_x = (MAP_WIDTH(map1) - x) + x2;
2311  } else if (map1->tile_map[TILED_SOUTH] == map2) {
2312  retval->distance_x = x2 - x;
2313  retval->distance_y = (MAP_HEIGHT(map1) - y) + y2;
2314  } else if (map1->tile_map[TILED_WEST] == map2) {
2315  retval->distance_y = y2 - y;
2316  retval->distance_x = -(x + (MAP_WIDTH(map2) - x2));
2317  } else if (map1->tile_map[TILED_NORTHEAST] == map2) {
2318  retval->distance_y = -(y + (MAP_HEIGHT(map2) - y2));
2319  retval->distance_x = (MAP_WIDTH(map1) - x) + x2;
2320  } else if (map1->tile_map[TILED_SOUTHEAST] == map2) {
2321  retval->distance_x = (MAP_WIDTH(map1) - x) + x2;
2322  retval->distance_y = (MAP_HEIGHT(map1) - y) + y2;
2323  } else if (map1->tile_map[TILED_SOUTHWEST] == map2) {
2324  retval->distance_y = (MAP_HEIGHT(map1) - y) + y2;
2325  retval->distance_x = -(x + (MAP_WIDTH(map2) - x2));
2326  } else if (map1->tile_map[TILED_NORTHWEST] == map2) {
2327  retval->distance_x = -(x + (MAP_WIDTH(map2) - x2));
2328  retval->distance_y = -(y + (MAP_HEIGHT(map2) - y2));
2329  } else if (map1->tile_map[TILED_UP] == map2) {
2330  retval->distance_x = x2 - x;
2331  retval->distance_y = y2 - y;
2332  retval->distance_z = 1;
2333  } else if (map1->tile_map[TILED_DOWN] == map2) {
2334  retval->distance_x = x2 - x;
2335  retval->distance_y = y2 - y;
2336  retval->distance_z = -1;
2337  } else {
2338  retval->distance_x = x2;
2339  retval->distance_y = y2;
2340 
2341  if (!(flags & RV_RECURSIVE_SEARCH)) {
2342  flags |= RV_NO_LOAD;
2343  }
2344 
2345  if (!relative_tile_position(map1, map2, &retval->distance_x,
2346  &retval->distance_y, &retval->distance_z, flags)) {
2347  return 0;
2348  }
2349 
2350  retval->distance_x -= x;
2351  retval->distance_y -= y;
2352  }
2353 
2354  switch (flags & (RV_EUCLIDIAN_DISTANCE | RV_DIAGONAL_DISTANCE)) {
2355  case RV_MANHATTAN_DISTANCE:
2356  retval->distance = abs(retval->distance_x) + abs(retval->distance_y);
2357  break;
2358 
2359  case RV_EUCLIDIAN_DISTANCE:
2360  retval->distance = isqrt(retval->distance_x * retval->distance_x + retval->distance_y * retval->distance_y);
2361  break;
2362 
2363  case RV_DIAGONAL_DISTANCE:
2364  retval->distance = MAX(abs(retval->distance_x), abs(retval->distance_y));
2365  break;
2366 
2367  /* No distance calc */
2368  case RV_NO_DISTANCE:
2369  return 1;
2370  }
2371 
2372  retval->direction = coords_distance_to_dir(-retval->distance_x, -retval->distance_y);
2373  return 1;
2374 }
2375 
2376 static int on_same_map_exits(mapstruct *map1, mapstruct *map2)
2377 {
2378  map_exit_t *exit;
2379  mapstruct *m;
2380  int i;
2381 
2382  DL_FOREACH(map1->exits, exit)
2383  {
2384  m = exit_get_destination(exit->obj, NULL, NULL, false);
2385 
2386  if (m == NULL) {
2387  continue;
2388  }
2389 
2390  if (m == map2) {
2391  return 1;
2392  }
2393 
2394  for (i = 0; i < TILED_NUM_DIR; i++) {
2395  if (m->tile_map[i] == map2) {
2396  return 1;
2397  }
2398  }
2399  }
2400 
2401  return 0;
2402 }
2403 
2414 int on_same_map(object *op1, object *op2)
2415 {
2416  int i;
2417 
2418  if (op1->map == NULL || op2->map == NULL) {
2419  return 0;
2420  }
2421 
2422  if (op1->map == op2->map) {
2423  return 1;
2424  }
2425 
2426  for (i = 0; i < TILED_NUM; i++) {
2427  if (op1->map->tile_map[i] == NULL ||
2428  op1->map->tile_map[i]->in_memory != MAP_IN_MEMORY) {
2429  continue;
2430  }
2431 
2432  if (op1->map->tile_map[i] == op2->map) {
2433  return 1;
2434  }
2435 
2436  if (on_same_map_exits(op1->map->tile_map[i], op2->map)) {
2437  return 1;
2438  }
2439  }
2440 
2441  if (on_same_map_exits(op1->map, op2->map)) {
2442  return 1;
2443  }
2444 
2445  return 0;
2446 }
2447 
2456 {
2457  object *tmp;
2458  int count;
2459 
2460  for (count = 0, tmp = m->player_first; tmp; tmp = CONTR(tmp)->map_above) {
2461  count++;
2462  }
2463 
2464  return count;
2465 }
2466 
2479 int wall_blocked(mapstruct *m, int x, int y)
2480 {
2481  int r;
2482 
2483  if (!(m = get_map_from_coord(m, &x, &y))) {
2484  return 1;
2485  }
2486 
2487  r = GET_MAP_FLAGS(m, x, y) & (P_NO_PASS | P_PASS_THRU);
2488 
2489  return r;
2490 }
2491 
2492 int map_get_darkness(mapstruct *m, int x, int y, object **mirror)
2493 {
2494  MapSpace *msp;
2495  uint8_t outdoor;
2496  int darkness;
2497 
2498  if (mirror) {
2499  *mirror = NULL;
2500  }
2501 
2502  msp = GET_MAP_SPACE_PTR(m, x, y);
2503  outdoor = MAP_OUTDOORS(m) || (msp->map_info && OBJECT_VALID(msp->map_info, msp->map_info_count) && msp->map_info->item_power == -2);
2504 
2505  if (((outdoor && !(msp->flags & P_OUTDOOR)) || (!outdoor && msp->flags & P_OUTDOOR)) && (!msp->map_info || !OBJECT_VALID(msp->map_info, msp->map_info_count) || msp->map_info->item_power < 0)) {
2506  darkness = msp->light_value + global_darkness_table[world_darkness];
2507  } else {
2508  /* Check if map info object bound to this tile has a darkness. */
2509  if (msp->map_info && OBJECT_VALID(msp->map_info, msp->map_info_count) && msp->map_info->item_power != -1) {
2510  int dark_value;
2511 
2512  dark_value = msp->map_info->item_power;
2513 
2514  if (dark_value < 0 || dark_value > MAX_DARKNESS) {
2515  dark_value = MAX_DARKNESS;
2516  }
2517 
2518  darkness = global_darkness_table[dark_value] + msp->light_value;
2519  } else {
2520  darkness = m->light_value + msp->light_value;
2521  }
2522  }
2523 
2524  if (msp->flags & P_MAGIC_MIRROR) {
2525  object *tmp;
2526  magic_mirror_struct *m_data;
2527  mapstruct *mirror_map;
2528 
2529  FOR_MAP_LAYER_BEGIN(m, x, y, LAYER_SYS, -1, tmp)
2530  {
2531  if (tmp->type == MAGIC_MIRROR) {
2532  if (mirror) {
2533  *mirror = tmp;
2534  }
2535 
2536  m_data = MMIRROR(tmp);
2537 
2538  if (m_data && (mirror_map = magic_mirror_get_map(tmp)) && !OUT_OF_MAP(mirror_map, m_data->x, m_data->y)) {
2539  MapSpace *mirror_msp = GET_MAP_SPACE_PTR(mirror_map, m_data->x, m_data->y);
2540 
2541  if ((MAP_OUTDOORS(mirror_map) && !(mirror_msp->flags & P_OUTDOOR)) || (!MAP_OUTDOORS(mirror_map) && mirror_msp->flags & P_OUTDOOR)) {
2542  darkness = mirror_msp->light_value + global_darkness_table[world_darkness];
2543  } else {
2544  darkness = mirror_map->light_value + mirror_msp->light_value;
2545  }
2546  }
2547 
2548  break;
2549  }
2550  }
2552  }
2553 
2554  return darkness;
2555 }
2556 
2557 int map_path_isabs(const char *path)
2558 {
2559  if (path == NULL) {
2560  return 0;
2561  }
2562 
2563  if (*path == '/' || string_startswith(path, settings.datapath)) {
2564  return 1;
2565  }
2566 
2567  return 0;
2568 }
2569 
2570 char *map_get_path(mapstruct *m, const char *path, uint8_t unique, const char *name)
2571 {
2572  char *path_tmp, *ret;
2573 
2574  path_tmp = NULL;
2575 
2576  if (m != NULL && MAP_UNIQUE(m)) {
2577  if (path && map_path_isabs(path)) {
2578  if (unique) {
2579  char *dir, *cp;
2580 
2581  dir = path_dirname(m->path);
2582  cp = estrdup(path);
2583  string_replace_char(cp, "/", '$');
2584 
2585  ret = path_join(dir, cp);
2586 
2587  efree(cp);
2588  efree(dir);
2589  } else {
2590  return estrdup(path);
2591  }
2592  } else {
2593  char *file, *filedir, *joined;
2594 
2595  /* Demangle the original map path, and get the original
2596  * directory the map was in. */
2597  file = path_basename(m->path);
2598  string_replace_char(file, "$", '/');
2599  filedir = path_dirname(file);
2600 
2601  if (!path) {
2602  path = path_tmp = path_basename(file);
2603  }
2604 
2605  if (unique) {
2606  char *newpath, *dir;
2607 
2608  /* Construct the new path. */
2609  joined = path_join(filedir, path);
2610  newpath = path_normalize(joined);
2611  string_replace_char(newpath, "/", '$');
2612 
2613  /* We need the data directory the map is in. */
2614  dir = path_dirname(m->path);
2615 
2616  /* Construct the path pointing inside the data directory. */
2617  ret = path_join(dir, newpath);
2618 
2619  efree(newpath);
2620  efree(dir);
2621  } else {
2622  joined = path_join(filedir, path);
2623  ret = path_normalize(joined);
2624  }
2625 
2626  efree(joined);
2627  efree(filedir);
2628  efree(file);
2629  }
2630  } else {
2631  if (path && map_path_isabs(path)) {
2632  if (unique && name) {
2633  char *cp;
2634 
2635  cp = estrdup(path);
2636  string_replace_char(cp, "/", '$');
2637 
2638  ret = player_make_path(name, cp);
2639 
2640  efree(cp);
2641  } else {
2642  return estrdup(path);
2643  }
2644  } else if (m != NULL) {
2645  char *filedir, *joined;
2646 
2647  filedir = path_dirname(m->path);
2648 
2649  if (!path) {
2650  path = path_tmp = path_basename(m->path);
2651  }
2652 
2653  if (unique && name) {
2654  char *newpath;
2655 
2656  /* Construct the new path. */
2657  joined = path_join(filedir, path);
2658  newpath = path_normalize(joined);
2659  string_replace_char(newpath, "/", '$');
2660 
2661  ret = player_make_path(name, newpath);
2662 
2663  efree(newpath);
2664  } else {
2665  joined = path_join(filedir, path);
2666  ret = path_normalize(joined);
2667  }
2668 
2669  efree(joined);
2670  efree(filedir);
2671  } else {
2672  return estrdup(EMERGENCY_MAPPATH);
2673  }
2674  }
2675 
2676  if (path_tmp) {
2677  efree(path_tmp);
2678  }
2679 
2680  return ret;
2681 }
2682 
2692 {
2693  object *tmp, *next, **players;
2694  size_t players_num, i;
2695  shstr *path;
2696  int flags;
2697 
2698  /* Cannot reset no-save unique maps or random maps. */
2699  if (m == NULL || MAP_NOSAVE(m) || strncmp(m->path, "/random/", 8) == 0) {
2700  return NULL;
2701  }
2702 
2703  players = NULL;
2704  players_num = 0;
2705 
2706  for (tmp = m->player_first; tmp; tmp = next) {
2707  next = CONTR(tmp)->map_above;
2708 
2709  leave_map(tmp);
2710  players = erealloc(players, sizeof(*players) * (players_num + 1));
2711  players[players_num] = tmp;
2712  players_num++;
2713  }
2714 
2715  m->reset_time = seconds();
2717  /* Store the path, so we can load it after swapping is done. */
2718  path = add_refcount(m->path);
2719  flags = MAP_NAME_SHARED | (MAP_UNIQUE(m) ? MAP_PLAYER_UNIQUE : 0);
2720 
2721  clean_tmp_map(m);
2722 
2723  if (m->in_memory == MAP_IN_MEMORY) {
2724  swap_map(m, 1);
2725  }
2726 
2727  m = ready_map_name(path, NULL, flags);
2728  free_string_shared(path);
2729 
2730  for (i = 0; i < players_num; i++) {
2731  object_insert_map(players[i], m, NULL, INS_NO_MERGE);
2732  }
2733 
2734  if (players) {
2735  efree(players);
2736  }
2737 
2738  return m;
2739 }
2740 
2758 static int map_redraw_internal(mapstruct *tiled, mapstruct *map, int x, int y,
2759  int layer, int sub_layer)
2760 {
2761  object *pl;
2762  rv_vector rv;
2763  int ax, ay, layer_start, layer_end, sub_layer_start, sub_layer_end,
2764  socket_layer;
2765 
2766  if (layer == -1) {
2767  layer_start = LAYER_FLOOR;
2768  layer_end = NUM_LAYERS;
2769  } else {
2770  layer_start = layer_end = layer;
2771  }
2772 
2773  if (sub_layer == -1) {
2774  sub_layer_start = 0;
2775  sub_layer_end = NUM_SUB_LAYERS - 1;
2776  } else {
2777  sub_layer_start = sub_layer_end = sub_layer;
2778  }
2779 
2780  for (pl = tiled->player_first; pl != NULL; pl = CONTR(pl)->map_above) {
2781  if (!get_rangevector_from_mapcoords(pl->map, pl->x, pl->y, map, x, y,
2782  &rv, RV_NO_DISTANCE)) {
2783  continue;
2784  }
2785 
2786  ax = CONTR(pl)->cs->mapx_2 + rv.distance_x;
2787  ay = CONTR(pl)->cs->mapy_2 + rv.distance_y;
2788 
2789  if (ax < 0 || ax >= CONTR(pl)->cs->mapx ||
2790  ay < 0 || ay >= CONTR(pl)->cs->mapy) {
2791  continue;
2792  }
2793 
2794  for (layer = layer_start; layer <= layer_end; layer++) {
2795  for (sub_layer = sub_layer_start; sub_layer <= sub_layer_end;
2796  sub_layer++) {
2797  socket_layer = NUM_LAYERS * sub_layer + layer - 1;
2798 
2799  CONTR(pl)->cs->lastmap.cells[ax][ay].faces[socket_layer] = 0;
2800  }
2801  }
2802  }
2803 
2804  return 0;
2805 }
2806 
2824 void map_redraw(mapstruct *m, int x, int y, int layer, int sub_layer)
2825 {
2826  HARD_ASSERT(m != NULL);
2827 
2828  SOFT_ASSERT(x >= 0 && x < m->width, "Invalid X coordinate: %d", x);
2829  SOFT_ASSERT(y >= 0 && y < m->height, "Invalid Y coordinate: %d", y);
2830  SOFT_ASSERT(layer >= -1 && layer <= NUM_LAYERS, "Invalid layer: %d", layer);
2831  SOFT_ASSERT(sub_layer >= -1 && sub_layer < NUM_SUB_LAYERS,
2832  "Invalid sub-layer: %d", sub_layer);
2833 
2834  MAP_TILES_WALK_START(m, map_redraw_internal, x, y, layer, sub_layer)
2835  {
2836  }
2838 }
2839 
2855 object *
2856 map_find_arch (mapstruct *m, int x, int y, archetype_t *at)
2857 {
2858  HARD_ASSERT(m != NULL);
2859  HARD_ASSERT(at != NULL);
2860 
2861  m = get_map_from_coord(m, &x, &y);
2862  if (m == NULL) {
2863  return NULL;
2864  }
2865 
2866  for (object *tmp = GET_MAP_OB(m, x, y); tmp != NULL; tmp = tmp->above) {
2867  if (tmp->arch == at) {
2868  return tmp;
2869  }
2870  }
2871 
2872  return NULL;
2873 }
2874 
2890 object *
2891 map_find_type (mapstruct *m, int x, int y, uint8_t type)
2892 {
2893  HARD_ASSERT(m != NULL);
2894 
2895  m = get_map_from_coord(m, &x, &y);
2896  if (m == NULL) {
2897  return NULL;
2898  }
2899 
2900  for (object *tmp = GET_MAP_OB(m, x, y); tmp != NULL; tmp = tmp->above) {
2901  if (tmp->type == type) {
2902  return tmp;
2903  }
2904  }
2905 
2906  return NULL;
2907 }
2908 
2941 int
2943  int x,
2944  int y,
2945  int start,
2946  int stop,
2947  archetype_t *at,
2948  object *op)
2949 {
2950  HARD_ASSERT(m != NULL);
2951  HARD_ASSERT(at != NULL);
2952 
2953  SOFT_ASSERT_RC(start >= 0 && start < SIZEOFFREE,
2954  -1,
2955  "Invalid start value: %d", start);
2956  SOFT_ASSERT_RC(stop >= 0 && stop < SIZEOFFREE,
2957  -1,
2958  "Invalid stop value: %d", start);
2959  SOFT_ASSERT_RC(stop > start,
2960  -1,
2961  "Stop (%d) is not higher than start (%d)",
2962  stop, start);
2963 
2964  int num_altern = 0;
2965  static int altern[SIZEOFFREE];
2966  for (int i = start; i <= stop; i++) {
2967  if (!arch_blocked(at,
2968  op,
2969  m,
2970  x + freearr_x[i],
2971  y + freearr_y[i])) {
2972  altern[num_altern++] = i;
2973  } else if (wall(m,
2974  x + freearr_x[i],
2975  y + freearr_y[i]) &&
2976  maxfree[i] < stop) {
2977  stop = maxfree[i];
2978  }
2979  }
2980 
2981  if (num_altern == 0) {
2982  return -1;
2983  }
2984 
2985  return altern[rndm(0, num_altern - 1)];
2986 }
2987 
3008 int
3010  int x,
3011  int y,
3012  archetype_t *at,
3013  object *op)
3014 {
3015  HARD_ASSERT(m != NULL);
3016  HARD_ASSERT(at != NULL);
3017 
3018  for (int i = 0; i <= SIZEOFFREE3; i++) {
3019  if (!arch_blocked(at, op, m, x + freearr_x[i], y + freearr_y[i])) {
3020  return i;
3021  }
3022  }
3023 
3024  return -1;
3025 }
int wall(mapstruct *m, int x, int y)
Definition: map.c:486
int wall_blocked(mapstruct *m, int x, int y)
Definition: map.c:2479
#define FLAG_FLY_OFF
Definition: define.h:972
#define FREE_AND_COPY_HASH(_sv_, _nv_)
Definition: global.h:100
void object_destroy(object *ob)
Definition: object.c:1441
#define MONSTER
Definition: define.h:353
uint32_t traversed
Definition: map.h:630
tag_t count
Unique identifier for the map.
Definition: map.h:686
#define TILED_NUM_DIR
Definition: global.h:210
MapSpace * spaces
Definition: map.h:574
#define EXIT
Definition: define.h:312
tag_t ownercount
Definition: object.h:219
void update_position(mapstruct *m, int x, int y)
Definition: map.c:1706
#define TERRAIN_FIREWALK
Definition: define.h:684
#define MAP_IN_MEMORY
Definition: map.h:170
int blocks_magic(mapstruct *m, int x, int y)
Definition: map.c:529
shstr * name
Definition: map.h:553
long seconds(void)
Definition: time.c:338
#define TILED_EAST
Definition: global.h:189
int direction
Definition: map.h:787
#define FLAG_SPAWN_MOB
Definition: define.h:1279
static void load_unique_objects(mapstruct *m)
Definition: map.c:1318
char datapath[MAX_BUF]
Definition: global.h:343
#define FREE_AND_NULL_PTR(_xyz_)
Definition: global.h:303
tag_t attacked_by_count
Definition: object.h:222
unsigned int distance
Definition: map.h:775
tag_t enemy_count
Definition: object.h:216
#define RV_NO_DISTANCE
Definition: map.h:813
#define NUM_FACINGS(ob)
Definition: global.h:300
Definition: map.h:504
int darkness
Definition: map.h:638
#define FLAG_IS_LINKED
Definition: define.h:1150
#define FLAG_UNIQUE
Definition: define.h:1066
#define P_NEED_UPDATE
Definition: map.h:307
#define SOUTH
Definition: map.h:845
static int map_redraw_internal(mapstruct *tiled, mapstruct *map, int x, int y, int layer, int sub_layer)
Definition: map.c:2758
mapstruct * get_linked_map(void)
Definition: map.c:948
uint8_t type
One of operation types.
Definition: sound_ambient.c:45
object * object_get(void)
Definition: object.c:993
#define TILED_UP
Definition: global.h:203
#define MAP_WIDTH(m)
Definition: map.h:120
void treasure_generate(treasure_list_t *treasure_list, object *op, int difficulty, int flags)
Definition: treasure.c:1641
static void map_debugger(mapstruct *map, char *buf, size_t size)
Definition: map.c:89
object * map_find_type(mapstruct *m, int x, int y, uint8_t type)
Definition: map.c:2891
static bool map_validator(mapstruct *map)
Definition: map.c:103
#define FLAG_ANIMATE
Definition: define.h:912
#define FLAG_NO_MAGIC
Definition: define.h:1032
int get_rangevector_from_mapcoords(mapstruct *map1, int x, int y, mapstruct *map2, int x2, int y2, rv_vector *retval, int flags)
Definition: map.c:2297
#define MAP_ENTER_X(m)
Definition: map.h:129
#define MAP_FLAG_UNIQUE
Definition: map.h:400
shstr * path
Definition: map.h:568
int32_t light_value
Definition: map.h:375
double speed_left
Definition: object.h:472
uint32_t in_memory
Definition: map.h:627
#define EMERGENCY_MAPPATH
Definition: config.h:134
#define MAP_DEFAULTRESET
Definition: config.h:127
#define SET_ANIMATION(ob, newanim)
Definition: global.h:282
#define MAP_MAXRESET
Definition: config.h:125
#define RV_EUCLIDIAN_DISTANCE
Definition: map.h:805
struct treasure_list * randomitems
Definition: object.h:231
char * create_pathname(const char *name)
Definition: map.c:422
int distance_x
Definition: map.h:778
MapSpace * first_light
Definition: map.h:577
#define FLAG_FLY_ON
Definition: define.h:968
uint32_t reset_time
Definition: map.h:608
#define TREASURE
Definition: define.h:134
#define P_NO_PASS
Definition: map.h:254
mapstruct * get_map_from_coord2(mapstruct *m, int *x, int *y)
Definition: map.c:2000
void connection_object_remove(object *op)
Definition: connection.c:91
int map_free_spot_first(mapstruct *m, int x, int y, archetype_t *at, object *op)
Definition: map.c:3009
int16_t last_sp
Definition: object.h:313
#define LAYER_FLOOR
Definition: map.h:49
mapstruct * get_map_from_tiled(mapstruct *m, int tiled)
Definition: map.c:1838
#define FLAG_PLAYER_ONLY
Definition: define.h:1297
void set_map_darkness(mapstruct *m, int value)
Definition: map.c:933
#define TERRAIN_WATERWALK
Definition: define.h:680
#define P_OUT_OF_MAP
Definition: map.h:305
int height
Definition: map.h:653
struct obj * above
Definition: object.h:120
uint32_t object_weight_sum(object *op)
Definition: object.c:523
#define OUT_OF_MAP(M, X, Y)
Definition: map.h:240
void map_redraw(mapstruct *m, int x, int y, int layer, int sub_layer)
Definition: map.c:2824
#define P_FLY_ON
Definition: map.h:296
#define MAP_TILES_WALK_START(_m, _fnc,...)
Definition: map.h:733
#define IS_LIVE(op)
Definition: define.h:841
void leave_map(object *op)
Definition: main.c:107
#define PLAYER
Definition: define.h:122
#define RV_IGNORE_MULTIPART
Definition: map.h:819
#define P_FLY_OFF
Definition: map.h:294
#define WEST
Definition: map.h:849
void free_map(mapstruct *m, int flag)
Definition: map.c:1493
static char * create_items_path(shstr *s)
Definition: map.c:445
#define P_PASS_THRU
Definition: map.h:288
#define MAP_WHEN_RESET(m)
Definition: map.h:84
#define RV_MANHATTAN_DISTANCE
Definition: map.h:801
#define FLAG_IS_TURNABLE
Definition: define.h:960
#define MMIRROR(ob)
Definition: object.h:678
#define P_WALK_OFF
Definition: map.h:292
mapstruct * get_empty_map(int sizex, int sizey)
Definition: map.c:985
#define FLAG_IS_FLOOR
Definition: define.h:1118
#define QUERY_FLAG(xyz, p)
Definition: define.h:761
#define MAP_PLAYER_UNIQUE
Definition: map.h:149
struct archetype * arch
Definition: object.h:225
struct obj * enemy
Definition: object.h:196
#define MAGIC_MIRROR
Definition: define.h:206
void object_remove(object *op, int flags)
Definition: object.c:1623
#define TILED_SOUTHWEST
Definition: global.h:199
static void delete_unique_items(mapstruct *m)
Definition: map.c:1284
#define TILED_NORTH
Definition: global.h:187
#define GT_APPLY
Definition: treasure.h:61
#define RV_NO_LOAD
Definition: map.h:829
#define P_MAGIC_MIRROR
Definition: map.h:298
#define SPAWN_POINT
Definition: define.h:357
#define LAYER_SYS
Definition: map.h:47
#define MAP_FLAG_FIXED_RTIME
Definition: map.h:405
static int coords_distance_to_dir(int x, int y)
Definition: map.c:2166
#define SPAWN_POINT_INFO
Definition: define.h:369
int distance_y
Definition: map.h:781
#define NORTH
Definition: map.h:837
#define P_BLOCKSVIEW
Definition: map.h:250
#define TILED_SOUTH
Definition: global.h:191
int16_t y
Definition: object.h:276
static mempool_struct * pool_map
Map structures pool.
Definition: map.c:78
static int fd
Definition: statistics.c:43
#define P_DOOR_CLOSED
Definition: map.h:272
#define MAP_DEFAULTTIMEOUT
Definition: config.h:108
#define FLAG_OUTDOOR
Definition: define.h:1062
#define LL_MORE
Object has more parts that need loading.
Definition: loader.h:39
void object_auto_apply(object *op)
#define P_IS_PLAYER
Definition: map.h:256
const char * object_get_str(const object *op)
Definition: object.c:3151
#define TILED_NORTHEAST
Definition: global.h:195
#define MAP_SWAPPED
Definition: map.h:172
int8_t direction
Definition: object.h:350
Definition: arch.h:40
int new_save_map(mapstruct *m, int flag)
Definition: map.c:1363
#define RV_RECURSIVE_SEARCH
Definition: map.h:825
uint32_t map_flags
Definition: map.h:605
object * map_info
Definition: map.h:345
struct sound_ambient_match * next
Next match rule in a linked list.
Definition: sound_ambient.c:39
#define EAST
Definition: map.h:841
struct archetype * more
Next part of a linked object.
Definition: arch.h:42
struct mapdef * map
Definition: object.h:139
shstr * bg_music
Definition: map.h:556
#define LL_EOF
End of file reached.
Definition: loader.h:38
#define P_IS_EXIT
Definition: map.h:260
char * msg
Definition: map.h:565
#define FLAG_WALK_OFF
Definition: define.h:964
#define MAP_FLAG_PVP
Definition: map.h:431
objectlink * buttons
Definition: map.h:588
int get_rangevector(object *op1, object *op2, rv_vector *retval, int flags)
Definition: map.c:2238
static int relative_tile_position_rec(mapstruct *map1, mapstruct *map2, int *x, int *y, int *z, uint32_t id, int level, int flags)
Definition: map.c:219
shstr * tile_path[TILED_NUM]
Definition: map.h:571
#define SIZEOFFREE3
Definition: define.h:658
#define P_OUTDOOR
Definition: map.h:300
#define MAP_FLUSH
Definition: map.h:144
uint8_t state
Definition: object.h:344
struct obj * below
Definition: object.h:114
#define CONTAINER
Definition: define.h:493
#define INS_NO_WALK_ON
Definition: object.h:570
void object_save(const object *op, FILE *fp)
Definition: object.c:3305
int16_t last_grace
Definition: object.h:316
#define P_NO_MAGIC
Definition: map.h:252
struct mapdef * tile_map[TILED_NUM]
Definition: map.h:550
#define FLAG_CAN_PASS_THRU
Definition: define.h:1058
static mapstruct * load_and_link_tiled_map(mapstruct *orig_map, int tile_num)
Definition: map.c:168
#define MAP_SAVING
Definition: map.h:176
#define SOUTHEAST
Definition: map.h:843
int players_on_map(mapstruct *m)
Definition: map.c:2455
int flags
Definition: map.h:381
#define MAP_HEIGHT(m)
Definition: map.h:122
int map_tiled_reverse[TILED_NUM]
Definition: map.c:52
int8_t item_power
Definition: object.h:442
struct map_event * next
Definition: map.h:506
static void load_objects(mapstruct *m, FILE *fp, int mapflags)
Definition: map.c:774
static void free_all_objects(mapstruct *m)
Definition: map.c:1467
#define MAP_BLOCK
Definition: map.h:151
#define ARROW
Definition: define.h:170
void swap_map(mapstruct *map, int force_flag)
Definition: swap.c:128
#define FLAG_FLYING
Definition: define.h:918
void living_update_monster(object *op)
Definition: living.c:1174
mapstruct * get_map_from_coord(mapstruct *m, int *x, int *y)
Definition: map.c:1869
#define TILED_SOUTHEAST
Definition: global.h:197
int blocked_tile(object *op, mapstruct *m, int x, int y)
Definition: map.c:676
#define NUM_LAYERS
Definition: map.h:67
#define FOR_MAP_LAYER_END
Definition: define.h:1657
shstr * weather
Definition: map.h:559
#define HEAD(op)
Definition: object.h:657
#define MSP_EXTRA_NO_MAGIC
Definition: map.h:324
int map_free_spot(mapstruct *m, int x, int y, int start, int stop, archetype_t *at, object *op)
Definition: map.c:2942
#define MAP_ENTER_Y(m)
Definition: map.h:131
static void map_constructor(mapstruct *map)
Definition: map.c:109
#define MAP_NO_DYNAMIC
Definition: map.h:161
#define TILED_NUM
Definition: global.h:208
#define P_PLAYER_ONLY
Definition: map.h:267
#define FLAG_DOOR_CLOSED
Definition: define.h:1314
void set_map_reset_time(mapstruct *map)
Definition: map.c:1814
int16_t x
Definition: object.h:273
#define NUM_SUB_LAYERS
Definition: map.h:71
#define FOR_MAP_LAYER_BEGIN(_m, _x, _y, _layer, _sub_layer, _obj)
Definition: define.h:1630
object * check_inv(object *op, object *ob)
Definition: check_inv.c:49
#define TILED_DOWN
Definition: global.h:205
object * object_insert_map(object *op, mapstruct *m, object *originator, int flag)
Definition: object.c:1741
#define SIZEOFFREE
Definition: define.h:660
int world_darkness
Definition: init.c:61
char mapspath[MAX_BUF]
Definition: global.h:348
struct settings_struct settings
Definition: init.c:55
char * tmpname
Definition: map.h:562
#define FLAG_NO_SAVE
Definition: define.h:1343
#define MAP_TIMEOUT(m)
Definition: map.h:90
#define FLAG_NO_PVP
Definition: define.h:1166
#define TILED_NORTHWEST
Definition: global.h:201
#define FLAG_BLOCKSVIEW
Definition: define.h:1008
void remove_light_source_list(mapstruct *map)
Definition: light.c:424
struct map_event * events
Definition: map.h:583
#define MAP_NAME_SHARED
Definition: map.h:157
int difficulty
Definition: map.h:650
#define MAP_RESET_TIMEOUT(m)
Definition: map.h:86
#define INS_NO_MERGE
Definition: object.h:564
#define RV_DIAGONAL_DISTANCE
Definition: map.h:809
struct obj * owner
Definition: object.h:207
bool global_removed
If true, the map was removed from the global list.
Definition: map.h:684
#define FLAG_XRAYS
Definition: define.h:1110
bool door_try_open(object *op, mapstruct *m, int x, int y, bool test)
Definition: door.c:231
void clean_tmp_map(mapstruct *m)
Definition: map.c:1662
void free_all_maps(void)
Definition: map.c:1677
#define NUM_ANIMATIONS(ob)
Definition: global.h:298
int8_t level_max
Maximum level offset that is part of this map.
Definition: map.h:682
tag_t map_info_count
Definition: map.h:360
mapstruct * map_force_reset(mapstruct *m)
Definition: map.c:2691
int distance_z
Definition: map.h:784
int light_value
Definition: map.h:644
static void map_destructor(mapstruct *map)
Definition: map.c:130
#define P_WALK_ON
Definition: map.h:290
#define MAP_OUTDOORS(m)
Definition: map.h:94
#define P_CHECK_INV
Definition: map.h:276
int freearr_x[SIZEOFFREE]
Definition: object.c:84
uint8_t type
Definition: object.h:360
object * object_owner(object *op)
Definition: object.c:857
int on_same_map(object *op1, object *op2)
Definition: map.c:2414
static mapstruct * load_temporary_map(mapstruct *m)
Definition: map.c:1235
#define SAVE_MODE
Definition: config.h:155
#define MAP_UNIQUE(m)
Definition: map.h:96
uint8_t extra_flags
Definition: map.h:390
uint16_t terrain_flag
Definition: object.h:301
#define MAP_DIFFICULTY(m)
Definition: map.h:88
object * part
Definition: map.h:790
static void save_objects(mapstruct *m, FILE *fp, FILE *fp2)
Definition: map.c:843
int maxfree[SIZEOFFREE]
Definition: object.c:114
#define MAP_FLAG_NO_SAVE
Definition: map.h:433
#define FLAG_IS_PLAYER
Definition: define.h:1267
mapstruct * load_original_map(const char *filename, mapstruct *originator, int flags)
Definition: map.c:1055
void check_light_source_list(mapstruct *map)
Definition: light.c:324
int arch_blocked(struct archetype *at, object *op, mapstruct *m, int x, int y)
Definition: map.c:728
#define FREE_AND_CLEAR_HASH(_nv_)
Definition: global.h:130
#define FLAG_MONSTER
Definition: define.h:922
object * player_first
Definition: map.h:594
#define OBJECT_VALID(_ob_, _count_)
Definition: object.h:548
#define MAP_DEFAULT_DARKNESS
Definition: map.h:442
int16_t coords[3]
X, Y and Z coordinates.
Definition: map.h:678
#define NORTHEAST
Definition: map.h:839
#define TERRAIN_AIRBREATH
Definition: define.h:678
#define MAP_NOSAVE(m)
Definition: map.h:100
#define SOUTHWEST
Definition: map.h:847
#define TILED_WEST
Definition: global.h:193
struct obj * inv
Definition: object.h:123
void object_owner_clear(object *op)
Definition: object.c:741
struct obj * head
Definition: object.h:136
#define P_IS_MONSTER
Definition: map.h:258
static int relative_tile_position(mapstruct *map1, mapstruct *map2, int *x, int *y, int *z, int flags)
Definition: map.c:348
#define MAX_DARKNESS
Definition: map.h:37
#define FLAG_WALK_ON
Definition: define.h:904
#define FLAG_NO_PASS
Definition: define.h:908
int blocks_view(mapstruct *m, int x, int y)
Definition: map.c:507
#define P_NO_TERRAIN
Definition: map.h:312
void map_init(void)
Definition: map.c:148
#define MAP_DARKNESS(m)
Definition: map.h:118
#define MAP_TILES_WALK_END
Definition: map.h:766
#define FLAG_PASS_THRU
Definition: define.h:1054
#define MAP_STYLE
Definition: map.h:153
uint16_t move_flags
Definition: map.h:387
#define NORTHWEST
Definition: map.h:851
void delete_map(mapstruct *m)
Definition: map.c:1555
Definition: map.h:536
#define P_NO_PVP
Definition: map.h:280
Definition: map.h:517
object * map_find_arch(mapstruct *m, int x, int y, archetype_t *at)
Definition: map.c:2856
#define FLAG_AUTO_APPLY
Definition: define.h:938
int width
Definition: map.h:656
#define MAP_ORIGINAL
Definition: map.h:159
mapstruct * exit_get_destination(object *op, int *x, int *y, bool do_load)
Definition: exit.c:383
#define CHECK_INV
Definition: define.h:308
int blocked(object *op, mapstruct *m, int x, int y, int terrain)
Definition: map.c:598
#define TERRAIN_ALL
Definition: define.h:692
int8_t level
Definition: object.h:347
int freearr_y[SIZEOFFREE]
Definition: object.c:99
static bool blocked_self(object *op, mapstruct *m, int x, int y)
Definition: map.c:556
static void allocate_map(mapstruct *m)
Definition: map.c:959
mapstruct * first_map
Definition: main.c:59
struct obj * more
Definition: object.h:133
uint32_t * bitmap
Definition: map.h:597
#define MAP_LOADING
Definition: map.h:174
object clone
An object from which to do object_copy().
Definition: arch.h:47
int load_object_buffer(void *buffer, object *op, int map_flags)
Definition: object.c:48633
mapstruct * has_been_loaded_sh(shstr *name)
Definition: map.c:389
void map_event_free(map_event *tmp)
Definition: plugins.c:392
#define STRING_SAFE(__string__)
Definition: global.h:260
mapstruct * ready_map_name(const char *name, mapstruct *originator, int flags)
Definition: map.c:1584
mapstruct * magic_mirror_get_map(object *op)
Definition: magic_mirror.c:121
struct obj * attacked_by
Definition: object.h:199
#define MSP_EXTRA_NO_PVP
Definition: map.h:322
int8_t level_min
Minimum level offset that is part of this map.
Definition: map.h:680
#define TERRAIN_CLOUDWALK
Definition: define.h:688