Atrinik Client  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 
32 #include <global.h>
33 #include <region_map.h>
34 #include <toolkit/packet.h>
35 #include <toolkit/string.h>
36 #include <toolkit/bresenham.h>
37 #include <toolkit/clioptions.h>
38 #include <toolkit/path.h>
39 
43 static struct MapCell *cells;
47 static int map_width;
51 static int map_height;
55 static SDL_Surface *zoomed = NULL;
59 static map_anim_t *first_anim = NULL;
60 
65 
70 
74 static int old_map_mouse_x = -1, old_map_mouse_y = -1;
78 static bool map_show_mouse = false;
83 static int right_click_ticks = -1;
84 
88 static bool tiles_debug = false;
89 
90 static int get_top_floor_height(struct MapCell *cell, int sub_layer);
91 
96 "Enable map tiles debugging (shows tile coordinates).";
98 static bool
100  char **errmsg)
101 {
102  tiles_debug = true;
103  return true;
104 }
105 
109 void load_mapdef_dat(void)
110 {
111  FILE *stream;
112  int i, ii, x, y, d[32];
113  char line[MAX_BUF];
114 
115  clioption_t *cli;
116  CLIOPTIONS_CREATE(cli, tiles_debug, "Enable map tiles debugging");
117 
118  stream = path_fopen(ARCHDEF_FILE, "r");
119 
120  if (stream == NULL) {
121  LOG(BUG, "Can't open file %s", ARCHDEF_FILE);
122  return;
123  }
124 
125  for (i = 0; i < 16; i++) {
126  if (!fgets(line, 255, stream)) {
127  break;
128  }
129 
130  sscanf(line,
131  "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d "
132  "%d %d %d %d %d %d %d %d %d %d %d %d %d %d", &x, &y, &d[0],
133  &d[1], &d[2], &d[3], &d[4], &d[5], &d[6], &d[7], &d[8], &d[9],
134  &d[10], &d[11], &d[12], &d[13], &d[14], &d[15], &d[16], &d[17],
135  &d[18], &d[19], &d[20], &d[21], &d[22], &d[23], &d[24], &d[25],
136  &d[26], &d[27], &d[28], &d[29], &d[30], &d[31]);
137  MultiArchs[i].xlen = x;
138  MultiArchs[i].ylen = y;
139 
140  for (ii = 0; ii < 16; ii++) {
141  MultiArchs[i].part[ii].xoff = d[ii * 2];
142  MultiArchs[i].part[ii].yoff = d[ii * 2 + 1];
143  }
144  }
145 
146  fclose(stream);
147 }
148 
154 void clear_map(bool hard)
155 {
156  size_t cells_size;
157 
158  /* Cache the map width and height. */
161 
162  cells_size = sizeof(*cells) * map_width * MAP_FOW_SIZE * map_height *
163  MAP_FOW_SIZE;
164 
165  if (cells == NULL) {
166  cells = emalloc(cells_size);
167  }
168 
169  memset(cells, 0, cells_size);
171  map_anims_clear();
172 
173  if (hard) {
174  region_map_reset(MapData.region_map);
175  MapData.region_name[0] = '\0';
176  MapData.region_longname[0] = '\0';
177  MapData.region_has_map = false;
178  }
179 }
180 
189 void
190 map_update_size (int w, int h)
191 {
192  int old_w = map_width;
193  int old_h = map_height;
194 
195  if (w != 0) {
196  map_width = w;
197  }
198 
199  if (h != 0) {
200  map_height = h;
201  }
202 
203  display_mapscroll((old_w - map_width) * (MAP_FOW_SIZE / 2),
204  (old_h - map_height) * (MAP_FOW_SIZE / 2),
205  old_w * MAP_FOW_SIZE,
206  old_h * MAP_FOW_SIZE);
207 }
208 
220 void display_mapscroll(int dx, int dy, int old_w, int old_h)
221 {
222  int x, y, w, h;
223  struct MapCell *cells_old;
224 
225  w = map_width * MAP_FOW_SIZE;
226  h = map_height * MAP_FOW_SIZE;
227 
228  if (old_w == 0) {
229  old_w = w;
230  }
231 
232  if (old_h == 0) {
233  old_h = h;
234  }
235 
236  cells_old = cells;
237  cells = emalloc(sizeof(*cells) * w * h);
238 
239  for (x = 0; x < w; x++) {
240  for (y = 0; y < h; y++) {
241  if (x + dx < 0 || x + dx >= old_w || y + dy < 0 ||
242  y + dy >= old_h) {
243  memset(&(cells[y * w + x]), 0, sizeof(struct MapCell));
244  } else {
245  memcpy(&(cells[y * w + x]),
246  &(cells_old[(y + dy) * old_w + x + dx]),
247  sizeof(struct MapCell));
248  }
249 
250  if (x < map_width * (MAP_FOW_SIZE / 2) ||
251  x >= map_width + map_width * (MAP_FOW_SIZE / 2) ||
252  y < map_height * (MAP_FOW_SIZE / 2) ||
253  y >= map_height + map_height * (MAP_FOW_SIZE / 2)) {
254  cells[y * w + x].fow = 1;
255  }
256  }
257  }
258 
259  efree(cells_old);
260 
261  sound_ambient_mapcroll(dx, dy);
262  map_anims_mapscroll(dx, dy);
263  cpl.target_object_index = 0;
264 }
265 
271 void update_map_name(const char *name)
272 {
273  snprintf(MapData.name_new, sizeof(MapData.name_new), "%s", name);
274 }
275 
281 void update_map_weather(const char *weather)
282 {
283  effect_start(weather);
284 }
285 
289 void update_map_height_diff(uint8_t height_diff)
290 {
291  MapData.height_diff = height_diff;
292 }
293 
299 void update_map_region_name(const char *region_name)
300 {
301  if (strcmp(MapData.region_name, region_name) == 0) {
302  return;
303  }
304 
305  snprintf(VS(MapData.region_name), "%s", region_name);
306  region_map_update(MapData.region_map, region_name);
307 }
308 
314 void update_map_region_longname(const char *region_longname)
315 {
316  snprintf(VS(MapData.region_longname), "%s", region_longname);
317 }
318 
324 void update_map_path(const char *map_path)
325 {
326  snprintf(VS(MapData.map_path), "%s", map_path);
327 }
328 
337 void map_update_in_building(uint8_t in_building)
338 {
339  if (in_building && !MapData.in_building) {
340  int x, y;
341  struct MapCell *cell;
342  int layer, sub_layer;
343 
344  for (x = 0; x < map_width * MAP_FOW_SIZE; x++) {
345  for (y = 0; y < map_height * MAP_FOW_SIZE; y++) {
346  cell = MAP_CELL_GET(x, y);
347 
348  if (!cell->fow) {
349  continue;
350  }
351 
352  for (sub_layer = MapData.player_sub_layer + 1;
353  sub_layer < NUM_SUB_LAYERS; sub_layer++) {
354  for (layer = LAYER_FLOOR; layer <= NUM_LAYERS; layer++) {
355  cell->faces[GET_MAP_LAYER(layer, sub_layer)] = 0;
356  }
357  }
358  }
359  }
360  }
361 
362  MapData.in_building = in_building;
363 }
364 
371 {
372  struct MapCell *cell;
373  int direction;
374 
375  cell = MAP_CELL_GET_MIDDLE(map_width - (map_width / 2) - 1,
376  map_height - (map_height / 2) - 1);
377 
378  direction = cell->anim_facing[GET_MAP_LAYER(LAYER_LIVING,
379  MapData.player_sub_layer)];
380 
381  if (direction == 0) {
382  return 1;
383  }
384 
385  return direction - 1;
386 }
387 
393 void map_get_real_coords(int *x, int *y)
394 {
395  *x = MapData.posx - (map_width / 2);
396  *y = MapData.posy - (map_height / 2);
397 }
398 
410 void init_map_data(int xl, int yl, int px, int py)
411 {
412  if (xl != -1) {
413  MapData.xlen = xl;
414  }
415 
416  if (yl != -1) {
417  MapData.ylen = yl;
418  }
419 
420  if (px != -1) {
421  MapData.posx = px;
422  }
423 
424  if (py != -1) {
425  MapData.posy = py;
426  }
427 
428  if (xl > 0) {
429  clear_map(false);
430  }
431 }
432 
433 #define MAX_STRETCH 8
434 #define MAX_STRETCH_DIAG 12
435 
451 static int calc_map_cell_height(int x, int y, int w, int h, int sub_layer,
452  int my_height)
453 {
454  if (x >= 0 && x < w && y >= 0 && y < h) {
455  bool is_building_wall = false;
456 
457  for (int i = 1; i < NUM_SUB_LAYERS; i++) {
458  if (cells[y * w + x].faces[GET_MAP_LAYER(LAYER_EFFECT, i)] != 0 &&
459  cells[y * w + x].height[GET_MAP_LAYER(LAYER_FLOOR, i)]
460  != 0) {
461  is_building_wall = true;
462  break;
463  }
464  }
465 
466  if (my_height < 0 && (sub_layer != 0 || is_building_wall)) {
467  for (sub_layer = NUM_SUB_LAYERS - 1; sub_layer >= 0; sub_layer--) {
468  int height = cells[y * w + x].height[GET_MAP_LAYER(LAYER_FLOOR,
469  sub_layer)];
470 
471  if (height != 0) {
472  return height;
473  }
474  }
475 
476  return 0;
477  }
478 
479  return cells[y * w + x].height[GET_MAP_LAYER(LAYER_FLOOR, sub_layer)];
480  }
481 
482  return 0;
483 }
484 
498 static void align_tile_stretch(int x, int y, int w, int h, int sub_layer)
499 {
500  int top, bottom, right, left, min_ht;
501  int32_t stretch;
502  int nw_height, n_height, ne_height, sw_height, s_height, se_height,
503  w_height, e_height, my_height;
504 
505  if (x < 0 || y < 0 || x >= w || y >= h) {
506  return;
507  }
508 
509  my_height = calc_map_cell_height(x, y, w, h, sub_layer, 0);
510  nw_height = calc_map_cell_height(x - 1, y - 1, w, h, sub_layer, my_height);
511  n_height = calc_map_cell_height(x, y - 1, w, h, sub_layer, my_height);
512  ne_height = calc_map_cell_height(x + 1, y - 1, w, h, sub_layer, my_height);
513  sw_height = calc_map_cell_height(x - 1, y + 1, w, h, sub_layer, my_height);
514  s_height = calc_map_cell_height(x, y + 1, w, h, sub_layer, my_height);
515  se_height = calc_map_cell_height(x + 1, y + 1, w, h, sub_layer, my_height);
516  w_height = calc_map_cell_height(x - 1, y, w, h, sub_layer, my_height);
517  e_height = calc_map_cell_height(x + 1, y, w, h, sub_layer, my_height);
518 
519  if (abs(my_height - e_height) > MAX_STRETCH) {
520  e_height = my_height;
521  }
522 
523  if (abs(my_height - se_height) > MAX_STRETCH_DIAG) {
524  se_height = my_height;
525  }
526 
527  if (abs(my_height - s_height) > MAX_STRETCH) {
528  s_height = my_height;
529  }
530 
531  if (abs(my_height - sw_height) > MAX_STRETCH_DIAG) {
532  sw_height = my_height;
533  }
534 
535  if (abs(my_height - w_height) > MAX_STRETCH) {
536  w_height = my_height;
537  }
538 
539  if (abs(my_height - nw_height) > MAX_STRETCH_DIAG) {
540  nw_height = my_height;
541  }
542 
543  if (abs(my_height - n_height) > MAX_STRETCH) {
544  n_height = my_height;
545  }
546 
547  if (abs(my_height - ne_height) > MAX_STRETCH_DIAG) {
548  ne_height = my_height;
549  }
550 
551  top = MAX(w_height, nw_height);
552  top = MAX(top, n_height);
553  top = MAX(top, my_height);
554 
555  bottom = MAX(s_height, se_height);
556  bottom = MAX(bottom, e_height);
557  bottom = MAX(bottom, my_height);
558 
559  right = MAX(n_height, ne_height);
560  right = MAX(right, e_height);
561  right = MAX(right, my_height);
562 
563  left = MAX(w_height, sw_height);
564  left = MAX(left, s_height);
565  left = MAX(left, my_height);
566 
567  min_ht = MIN(top, bottom);
568  min_ht = MIN(min_ht, left);
569  min_ht = MIN(min_ht, right);
570  min_ht = MIN(min_ht, my_height);
571 
572  if (my_height < 0 && left == 0 && right == 0 && top == 0 && bottom == 0) {
573  int top2 = MIN(w_height, nw_height);
574  top2 = MIN(top2, n_height);
575  top2 = MIN(top2, my_height);
576 
577  int bottom2 = MIN(s_height, se_height);
578  bottom2 = MIN(bottom2, e_height);
579  bottom2 = MIN(bottom2, my_height);
580 
581  int right2 = MIN(n_height, ne_height);
582  right2 = MIN(right2, e_height);
583  right2 = MIN(right2, my_height);
584 
585  int left2 = MIN(w_height, sw_height);
586  left2 = MIN(left2, s_height);
587  left2 = MIN(left2, my_height);
588 
589  top = top2 - top;
590  bottom = bottom2 - bottom;
591  right = right2 - right;
592  left = left2 - left;
593 
594  min_ht = MIN(top, bottom);
595  min_ht = MIN(min_ht, left);
596  min_ht = MIN(min_ht, right);
597  min_ht = MIN(min_ht, my_height);
598 
599  min_ht = abs(min_ht);
600  top = abs(top);
601  bottom = abs(bottom);
602  left = abs(left);
603  right = abs(right);
604  }
605 
606  /* Normalize these... */
607  top -= min_ht;
608  bottom -= min_ht;
609  left -= min_ht;
610  right -= min_ht;
611 
612  stretch = abs(bottom) + (abs(left) << 8) + (abs(right) << 16) + (abs(top) << 24);
613  cells[y * w + x].stretch[sub_layer] = stretch;
614 }
615 
625 {
626  int xoff, yoff, w, h, x, y, sub_layer;
627 
628  xoff = map_width * (MAP_FOW_SIZE / 2);
629  yoff = map_height * (MAP_FOW_SIZE / 2);
630  w = map_width * MAP_FOW_SIZE;
631  h = map_height * MAP_FOW_SIZE;
632 
633  for (x = xoff; x < xoff + map_width; x++) {
634  for (y = yoff; y < yoff + map_height; y++) {
635  for (sub_layer = 0; sub_layer < NUM_SUB_LAYERS; sub_layer++) {
636  align_tile_stretch(x, y, w, h, sub_layer);
637  }
638  }
639  }
640 }
641 
675 void map_set_data(int x, int y, int layer, int16_t face,
676  uint8_t quick_pos, uint8_t obj_flags, const char *name,
677  const char *name_color, int16_t height, uint8_t probe, int16_t zoom_x,
678  int16_t zoom_y, int16_t align, uint8_t draw_double, uint8_t alpha,
679  int16_t rotate, uint8_t infravision, uint32_t target_object_count,
680  uint8_t target_is_friend, uint8_t anim_speed, uint8_t anim_facing,
681  uint8_t anim_flags, uint8_t anim_state, uint8_t priority,
682  uint8_t secondpass, const char *glow, uint8_t glow_speed)
683 {
684  struct MapCell *cell;
685  int sub_layer;
686 
687  cell = MAP_CELL_GET_MIDDLE(x, y);
688  sub_layer = layer / NUM_LAYERS;
689 
690  if (cell->fow) {
691  int i;
692 
693  cell->fow = 0;
694 
695  for (i = 0; i < NUM_REAL_LAYERS; i++) {
696  cell->faces[i] = 0;
697  cell->flags[i] = 0;
698  cell->quick_pos[i] = 0;
699  cell->height[i] = 0;
700  cell->zoom_x[i] = 0;
701  cell->zoom_y[i] = 0;
702  cell->align[i] = 0;
703  cell->rotate[i] = 0;
704  cell->infravision[i] = 0;
705  cell->anim_last[i] = 0;
706  cell->anim_speed[i] = 0;
707  cell->anim_facing[i] = 0;
708  cell->anim_state[i] = 0;
709  }
710 
711  for (i = 0; i < NUM_SUB_LAYERS; i++) {
712  cell->anim_flags[i] = 0;
713  cell->priority[i] = 0;
714  cell->secondpass[i] = 0;
715  }
716  }
717 
718  if (anim_speed != 0 && cell->faces[layer] != face) {
719  cell->anim_state[layer] = 0;
720  }
721 
722  cell->priority[sub_layer] |= priority << (((layer % NUM_LAYERS) + 1) - 1);
723  cell->secondpass[sub_layer] |= secondpass << (((layer % NUM_LAYERS) + 1) -
724  1);
725 
726  cell->faces[layer] = face;
727  cell->flags[layer] = obj_flags;
728 
729  cell->probe[layer] = probe;
730  cell->quick_pos[layer] = quick_pos;
731 
732  snprintf(VS(cell->pcolor[layer]), "%s", name_color);
733  snprintf(VS(cell->pname[layer]), "%s", name);
734  snprintf(VS(cell->glow[layer]), "%s", glow);
735 
736  cell->height[layer] = height;
737  cell->zoom_x[layer] = zoom_x;
738  cell->zoom_y[layer] = zoom_y;
739  cell->align[layer] = align;
740  cell->draw_double[layer] = draw_double;
741  cell->alpha[layer] = alpha;
742  cell->rotate[layer] = rotate;
743  cell->infravision[layer] = infravision;
744  cell->glow_speed[layer] = glow_speed;
745 
746  if (cell->target_object_count[layer] != target_object_count ||
747  cell->target_is_friend[layer] != target_is_friend) {
748  cpl.target_object_index = 0;
749  }
750 
752  cell->target_is_friend[layer] = target_is_friend;
753 
754  cell->anim_speed[layer] = anim_speed;
755  cell->anim_facing[layer] = anim_facing;
756 
757  if (((layer % NUM_LAYERS) + 1) == LAYER_LIVING) {
758  if (anim_flags & ANIM_FLAG_ATTACKING &&
759  !(cell->anim_flags[sub_layer] & ANIM_FLAG_ATTACKING)) {
760  cell->anim_state[layer] = 0;
761  } else if (anim_flags & ANIM_FLAG_MOVING &&
762  !(cell->anim_flags[sub_layer] & ANIM_FLAG_MOVING)) {
763  cell->anim_state[layer] = anim_state;
764  }
765 
766  cell->anim_flags[sub_layer] = anim_flags;
767  }
768 
769  if (anim_speed != 0) {
771  } else {
772  image_request_face(face);
773  }
774 }
775 
786 void map_clear_cell(int x, int y)
787 {
788  struct MapCell *cell;
789  int layer;
790 
791  cell = MAP_CELL_GET_MIDDLE(x, y);
792  cell->fow = 1;
793 
794  for (layer = 0; layer < NUM_REAL_LAYERS; layer++) {
795  cell->probe[layer] = 0;
796  cell->target_object_count[layer] = 0;
797  cell->target_is_friend[layer] = 0;
798  cell->pname[layer][0] = '\0';
799  }
800 }
801 
813 void map_set_darkness(int x, int y, int sub_layer, uint8_t darkness)
814 {
815  struct MapCell *cell;
816 
817  cell = MAP_CELL_GET_MIDDLE(x, y);
818  cell->darkness[sub_layer] = darkness;
819 }
820 
830 static int get_top_floor_height(struct MapCell *cell, int sub_layer)
831 {
832  int16_t height;
833 
834  height = cell->height[GET_MAP_LAYER(LAYER_FLOOR, sub_layer)];
835 
836  return MAX(0, height);
837 }
838 
839 static void map_animate_object(struct MapCell *cell, int layer)
840 {
841  Animations *animation;
842 
843  if (cell->faces[layer] == 0 || cell->anim_speed[layer] == 0 ||
844  cell->anim_facing[layer] == 0) {
845  return;
846  }
847 
848  animation = &animations[cell->faces[layer]];
849 
850  if (animation->num_animations == 0) {
851  return;
852  }
853 
854  if (!(cell->flags[layer] & FFLAG_SLEEP) &&
855  !(cell->flags[layer] & FFLAG_PARALYZED)) {
856  cell->anim_state[layer]++;
857  map_redraw_flag = 1;
858  }
859 
860  /* If beyond drawable states, reset */
861  if (cell->anim_state[layer] >=
862  animation->num_animations / animation->facings) {
863  cell->anim_state[layer] = 0;
864  }
865 }
866 
867 void map_animate(void)
868 {
869  int x, y, layer;
870  struct MapCell *cell;
871 
872  for (x = 0; x < map_width; x++) {
873  for (y = 0; y < map_height; y++) {
874  cell = MAP_CELL_GET_MIDDLE(x, y);
875 
876  if (cell->fow) {
877  continue;
878  }
879 
880  for (layer = 0; layer < NUM_REAL_LAYERS; layer++) {
881  if (cell->glow_speed[layer] > 1) {
882  cell->glow_state[layer]++;
883  map_redraw_flag = 1;
884 
885  if (cell->glow_state[layer] > cell->glow_speed[layer]) {
886  cell->glow_state[layer] = 0;
887  }
888  }
889 
890  if (cell->anim_speed[layer] == 0) {
891  continue;
892  }
893 
894  if (cell->anim_last[layer] >= cell->anim_speed[layer]) {
895  map_animate_object(cell, layer);
896  cell->anim_last[layer] = 1;
897  } else {
898  cell->anim_last[layer]++;
899  }
900  }
901  }
902  }
903 }
904 
905 static uint16_t map_object_get_face(struct MapCell *cell, int layer)
906 {
907  int sub_layer, dir, state;
908  Animations *animation;
909 
910  if (cell->anim_speed[layer] == 0) {
911  return cell->faces[layer];
912  }
913 
914  animation = &animations[cell->faces[layer]];
915 
916  if (animation->num_animations == 0) {
917  return cell->faces[layer];
918  }
919 
920  sub_layer = layer / NUM_LAYERS;
921  dir = cell->anim_facing[layer] - 1;
922  state = 0;
923 
924  if (animation->facings == 9) {
925  state = dir * (animation->num_animations / 9);
926  } else if (animation->facings >= 25) {
927  if (cell->anim_flags[sub_layer] & ANIM_FLAG_ATTACKING) {
928  dir += 16;
929  } else if (cell->anim_flags[sub_layer] & ANIM_FLAG_MOVING) {
930  dir += 8;
931  }
932 
933  state = dir * (animation->num_animations / animation->facings);
934  }
935 
936  return animation->faces[cell->anim_state[layer] + state];
937 }
938 
945 typedef struct map_render_data {
946  int16_t x;
947  int16_t y;
948 
949  int16_t midx;
950  int16_t midy;
951 
952  int32_t xpos;
953  int32_t ypos;
955 
956  struct MapCell *cell;
958  SDL_Rect *tiles;
959 
960  size_t tiles_num;
961 
962  SDL_Rect target_rect;
963 
964  uint8_t layer;
965  uint8_t sub_layer;
966  uint8_t alpha_forced;
967  uint8_t target_layer;
969 
978 static void
979 draw_map_object (SDL_Surface *surface, map_render_data_t *data)
980 {
981  HARD_ASSERT(surface != NULL);
982  HARD_ASSERT(data != NULL);
983 
984  uint8_t map_layer = GET_MAP_LAYER(data->layer, data->sub_layer);
985  uint16_t face = map_object_get_face(data->cell, map_layer);
986  if (face == 0 || face >= MAX_FACE_TILES) {
987  return;
988  }
989 
990  sprite_struct *face_sprite = FaceList[face].sprite;
991  if (face_sprite == NULL) {
992  return;
993  }
994 
995  /* When rendering on the map surface, avoid rendering the object
996  * when it's too high up and either in the FoW or the map has the
997  * "height difference" feature enabled. */
998  if (surface == cur_widget[MAP_ID]->surface &&
999  (data->cell->fow || MapData.height_diff) &&
1000  abs(get_top_floor_height(data->cell, data->sub_layer) -
1002  return;
1003  }
1004 
1005  int bitmap_h = face_sprite->bitmap->h;
1006  int bitmap_w = face_sprite->bitmap->w;
1007 
1008  sprite_effects_t effects = {0};
1009  effects.rotate = data->cell->rotate[map_layer];
1010  effects.zoom_x = data->cell->zoom_x[map_layer];
1011  effects.zoom_y = data->cell->zoom_y[map_layer];
1012 
1013  if (effects.rotate != 0) {
1014  rotozoomSurfaceSizeXY(bitmap_w,
1015  bitmap_h,
1016  effects.rotate,
1017  effects.zoom_x != 0 ? effects.zoom_x / 100.0 :
1018  1.0,
1019  effects.zoom_y != 0 ? effects.zoom_y / 100.0 :
1020  1.0,
1021  &bitmap_w,
1022  &bitmap_h);
1023  } else if ((effects.zoom_x != 0 && effects.zoom_x != 100) ||
1024  (effects.zoom_y != 0 && effects.zoom_y != 100)) {
1025  zoomSurfaceSize(bitmap_w,
1026  bitmap_h,
1027  effects.zoom_x != 0 ? effects.zoom_x / 100.0 : 1.0,
1028  effects.zoom_y != 0 ? effects.zoom_y / 100.0 : 1.0,
1029  &bitmap_w,
1030  &bitmap_h);
1031  }
1032 
1033  int xlen;
1034  int xoff;
1035  int yl;
1036  int xl;
1037  /* Multi-part object? */
1038  if (data->cell->quick_pos[map_layer]) {
1039  uint8_t mnr = data->cell->quick_pos[map_layer];
1040  uint8_t mid = mnr >> 4;
1041  mnr &= 0x0f;
1042  xlen = MultiArchs[mid].xlen;
1043  yl = data->ypos - MultiArchs[mid].part[mnr].yoff +
1044  MultiArchs[mid].ylen - bitmap_h;
1045 
1046  /* Center overlapping X borders */
1047  xl = 0;
1048  if (bitmap_w > MultiArchs[mid].xlen) {
1049  xl = (MultiArchs[mid].xlen - bitmap_w) >> 1;
1050  }
1051 
1052  xoff = data->xpos - MultiArchs[mid].part[mnr].xoff;
1053  xl += xoff;
1054  } else {
1055  /* Calculate offsets */
1056  xlen = MAP_TILE_POS_XOFF;
1057  yl = (data->ypos + MAP_TILE_POS_YOFF) - bitmap_h;
1058  xoff = xl = data->xpos;
1059 
1060  if (bitmap_w > MAP_TILE_POS_XOFF) {
1061  xl -= (bitmap_w - MAP_TILE_POS_XOFF) / 2;
1062  }
1063  }
1064 
1065  xl += data->cell->align[map_layer];
1066 
1067  snprintf(VS(effects.glow), "%s", data->cell->glow[map_layer]);
1068  effects.glow_speed = data->cell->glow_speed[map_layer];
1069  effects.glow_state = data->cell->glow_state[map_layer];
1070 
1071  if (effect_has_overlay()) {
1072  BIT_SET(effects.flags, SPRITE_FLAG_EFFECTS);
1073  }
1074 
1075  if (data->cell->fow) {
1076  BIT_SET(effects.flags, SPRITE_FLAG_FOW);
1077  } else if (data->cell->infravision[map_layer]) {
1078  BIT_SET(effects.flags, SPRITE_FLAG_RED);
1079  } else if (data->cell->flags[map_layer] & FFLAG_INVISIBLE) {
1080  BIT_SET(effects.flags, SPRITE_FLAG_GRAY);
1081  } else {
1082  BIT_SET(effects.flags, SPRITE_FLAG_DARK);
1083  }
1084 
1085  if (surface != cur_widget[MAP_ID]->surface) {
1086  BITMASK_CLEAR(effects.flags,
1087  BIT_MASK(SPRITE_FLAG_RED) | BIT_MASK(SPRITE_FLAG_FOW));
1088  BIT_SET(effects.flags, SPRITE_FLAG_DARK);
1089  }
1090 
1091  if (BIT_QUERY(effects.flags, SPRITE_FLAG_DARK)) {
1092  effects.dark_level = 7 - data->cell->darkness[data->sub_layer] / 30;
1093  }
1094 
1095  effects.alpha = data->cell->alpha[map_layer];
1096 
1097  if (data->alpha_forced != 0) {
1098  if (effects.alpha != 0) {
1099  effects.alpha = MIN(effects.alpha, data->alpha_forced);
1100  } else {
1101  effects.alpha = data->alpha_forced;
1102  }
1103  }
1104 
1105  /* Stretch floor and floor mask layers. */
1106  if (data->layer <= LAYER_FMASK) {
1107  effects.stretch = data->cell->stretch[data->sub_layer];
1108  }
1109 
1110  if (data->layer == LAYER_LIVING ||
1111  data->layer == LAYER_EFFECT ||
1112  data->layer == LAYER_ITEM ||
1113  data->layer == LAYER_ITEM2) {
1114  yl -= get_top_floor_height(data->cell, data->sub_layer);
1115  } else {
1116  yl -= data->cell->height[GET_MAP_LAYER(LAYER_FLOOR, data->sub_layer)];
1117  }
1118 
1119  yl += data->player_height_offset;
1120 
1121  /* Move the object up/down depending on its height, but only for
1122  * non-floor layers. */
1123  if (data->layer > LAYER_FLOOR) {
1124  yl -= data->cell->height[map_layer];
1125  }
1126 
1127  surface_show_effects(surface, xl, yl, NULL, face_sprite->bitmap, &effects);
1128 
1129  /* Double faces are shown twice, one above the other, when not lower
1130  * on the screen than the player. This simulates high walls without
1131  * obscuring the user's view. */
1132  if (data->cell->draw_double[map_layer]) {
1133  surface_show_effects(surface, xl, yl - 22, NULL, face_sprite->bitmap,
1134  &effects);
1135  }
1136 
1137  /* Rest of the code deals with rendering on the map widget. */
1138  if (surface != cur_widget[MAP_ID]->surface) {
1139  return;
1140  }
1141 
1142  int xoff2;
1143  if (xlen == MAP_TILE_POS_XOFF) {
1144  xoff2 = (int) (((double) xlen / 100.0) * 25.0);
1145  } else {
1146  xoff2 = (int) (((double) xlen / 100.0) * 20.0);
1147  }
1148 
1149  /* Do we have a playername? Then print it! */
1150  if (data->cell->pname[map_layer][0] != '\0' &&
1152  bool draw_name = false;
1153  char *name = data->cell->pname[map_layer];
1154 
1156  draw_name = true;
1157  } else if (setting_get_int(OPT_CAT_MAP, OPT_PLAYER_NAMES) == 2) {
1158  if (strncasecmp(name, cpl.name, strlen(name)) != 0) {
1159  draw_name = true;
1160  }
1161  } else if (setting_get_int(OPT_CAT_MAP, OPT_PLAYER_NAMES) == 3) {
1162  if (strncasecmp(name, cpl.name, strlen(name)) == 0) {
1163  draw_name = true;
1164  }
1165  }
1166 
1167  if (draw_name) {
1168  text_show(surface,
1169  FONT_SANS9,
1170  name,
1171  xoff + xoff2 + (xlen - xoff2 * 2) / 2 -
1172  text_get_width(FONT_SANS9, name, 0) / 2 - 2,
1173  yl - 24,
1174  data->cell->pcolor[map_layer],
1175  TEXT_OUTLINE,
1176  NULL);
1177  }
1178  }
1179 
1180  /* Show the status effect, if any, */
1181  if (data->cell->flags[map_layer]) {
1182  sprite_effects_t effects2 = {0};
1183  effects2.alpha = effects.alpha;
1184  effects2.stretch = effects.stretch;
1185  effects2.zoom_x = effects.zoom_x;
1186  effects2.zoom_y = effects.zoom_y;
1187  effects2.rotate = effects.rotate;
1188 
1189  if (data->cell->flags[map_layer] & FFLAG_SLEEP) {
1190  surface_show_effects(surface,
1191  xl + bitmap_w / 2,
1192  yl - 5,
1193  NULL,
1194  TEXTURE_CLIENT("sleep"),
1195  &effects2);
1196  }
1197 
1198  if (data->cell->flags[map_layer] & FFLAG_CONFUSED) {
1199  surface_show_effects(surface,
1200  xl + bitmap_w / 2 - 1,
1201  yl - 4,
1202  NULL,
1203  TEXTURE_CLIENT("confused"),
1204  &effects2);
1205  }
1206 
1207  if (data->cell->flags[map_layer] & FFLAG_SCARED) {
1208  surface_show_effects(surface,
1209  xl + bitmap_w / 2 + 10,
1210  yl - 4,
1211  NULL,
1212  TEXTURE_CLIENT("scared"),
1213  &effects2);
1214  }
1215 
1216  if (data->cell->flags[map_layer] & FFLAG_BLINDED) {
1217  surface_show_effects(surface,
1218  xl + bitmap_w / 2 + 3,
1219  yl - 6,
1220  NULL,
1221  TEXTURE_CLIENT("blind"),
1222  &effects2);
1223  }
1224 
1225  if (data->cell->flags[map_layer] & FFLAG_PARALYZED) {
1226  surface_show_effects(surface,
1227  xl + bitmap_w / 2 + 3,
1228  yl + 3,
1229  NULL,
1230  TEXTURE_CLIENT("paralyzed"),
1231  &effects2);
1232  }
1233  }
1234 
1235  if (data->layer == LAYER_FLOOR && tiles_debug) {
1236  data->tiles = erealloc(data->tiles,
1237  sizeof(*data->tiles) * ((data->tiles_num) + 1));
1238  data->tiles[data->tiles_num].x = xl;
1239  data->tiles[data->tiles_num].y = yl;
1240  data->tiles[data->tiles_num].w = data->x;
1241  data->tiles[data->tiles_num].h = data->y;
1242  data->tiles_num++;
1243  }
1244 
1245  if (!data->cell->fow &&
1246  data->cell->probe[map_layer] != 0) {
1247  data->target_cell = data->cell;
1248  data->target_layer = map_layer;
1249  data->target_rect.x = xoff + xoff2;
1250  data->target_rect.y = yl - 9;
1251  data->target_rect.w = (xlen - xoff2 * 2);
1252  data->target_rect.h = 1;
1253  }
1254 }
1255 
1270 static bool
1271 obj_is_behind_wall (int dx, int dy, int sx, int sy)
1272 {
1273  int fraction, dx2, dy2, stepx, stepy;
1274  int x = sx, y = sy;
1275  int distance_x = dx - sx;
1276  int distance_y = dy - sy;
1277 
1278  BRESENHAM_INIT(distance_x, distance_y, fraction, stepx, stepy, dx2, dy2);
1279 
1280  while (1) {
1281  if (x == dx && y == dy) {
1282  return false;
1283  }
1284 
1285  if (x < 0 || x >= map_width || y < 0 || y >= map_height) {
1286  return false;
1287  }
1288 
1289  for (int sub_layer = 0; sub_layer < NUM_SUB_LAYERS; sub_layer++) {
1290  MapCell *cell = MAP_CELL_GET_MIDDLE(x, y);
1291 
1292  if (cell->faces[GET_MAP_LAYER(LAYER_WALL, sub_layer)] != 0) {
1293  return true;
1294  }
1295  }
1296 
1297  BRESENHAM_STEP(x, y, fraction, stepx, stepy, dx2, dy2);
1298  }
1299 }
1300 
1311 static bool
1312 map_should_cull (SDL_Surface *surface,
1313  map_render_data_t *data)
1314 {
1315  /* Determine the distance of the object relative to the PC. */
1316  int distance_x = data->x - map_width * MAP_FOW_SIZE / 2;
1317  int distance_y = data->y - map_height * MAP_FOW_SIZE / 2;
1318  int distance = isqrt(distance_x * distance_x +
1319  distance_y * distance_y);
1320  if (distance > 3) {
1321  /* Too far away, no culling. */
1322  return false;
1323  }
1324 
1325  /* Must be in the southern or eastern quadrant to be culled. */
1326  if (data->x < map_width * MAP_FOW_SIZE / 2 ||
1327  data->y < map_height * MAP_FOW_SIZE / 2) {
1328  return false;
1329  }
1330 
1331  bool cull = false;
1332  int range = 2;
1333 
1334  for (int sub_layer2 = NUM_SUB_LAYERS - 1;
1335  sub_layer2 > 0;
1336  sub_layer2--) {
1337  int16_t height = data->cell->height[GET_MAP_LAYER(LAYER_EFFECT,
1338  sub_layer2)];
1339  if (height - data->player_height_offset > 50) {
1340  range = 0;
1341  }
1342  }
1343 
1344  if (range == 0) {
1345  cull = true;
1346  }
1347 
1348  for (int nx = data->x - range; nx <= data->x && !cull; nx++) {
1349  for (int ny = data->y - range; ny <= data->y && !cull; ny++) {
1350  MapCell *cell2 = MAP_CELL_GET(nx, ny);
1351 
1352  for (int sub_layer2 = 0;
1353  sub_layer2 < NUM_SUB_LAYERS;
1354  sub_layer2++) {
1355  if (cell2->secondpass[sub_layer2] & (1 << (LAYER_WALL - 1)) &&
1356  !obj_is_behind_wall(nx,
1357  ny,
1358  map_width * MAP_FOW_SIZE / 2,
1359  map_height * MAP_FOW_SIZE / 2)) {
1360  cull = true;
1361  break;
1362  }
1363  }
1364  }
1365  }
1366 
1367  return cull && range != 0;
1368 }
1369 
1382 static bool
1383 map_should_draw (SDL_Surface *surface, map_render_data_t *data)
1384 {
1385  HARD_ASSERT(surface != NULL);
1386  HARD_ASSERT(data != NULL);
1387 
1388  data->xpos = surface->w / 2 - MAP_TILE_POS_XOFF / 2 +
1389  (data->x - data->midx) * MAP_TILE_YOFF -
1390  (data->y - data->midy) * MAP_TILE_YOFF;
1391  data->ypos = surface->h / 2 - MAP_TILE_POS_YOFF / 2 +
1392  (data->x - data->midx) * MAP_TILE_XOFF +
1393  (data->y - data->midy) * MAP_TILE_XOFF;
1394 
1395  if (surface != cur_widget[MAP_ID]->surface) {
1396  data->ypos -= map_width * MAP_TILE_XOFF + map_height *
1397  MAP_TILE_XOFF * (MAP_FOW_SIZE / 2);
1398  data->ypos -= map_height * MAP_TILE_YOFF;
1399  }
1400 
1401  if (data->xpos > surface->w || data->xpos + MAP_TILE_POS_XOFF < 0 ||
1402  data->ypos + MAP_TILE_POS_YOFF < 0) {
1403  return false;
1404  }
1405 
1406  data->cell = MAP_CELL_GET(data->x, data->y);
1407 
1408  int height = 0;
1409  for (uint8_t sub_layer = 0; sub_layer < NUM_SUB_LAYERS; sub_layer++) {
1410  uint8_t map_layer = GET_MAP_LAYER(LAYER_FLOOR, sub_layer);
1411  if (data->cell->height[map_layer] > height) {
1412  height = data->cell->height[map_layer];
1413  }
1414 
1415  map_layer = GET_MAP_LAYER(LAYER_EFFECT, sub_layer);
1416  if (data->cell->height[map_layer] > height) {
1417  height = data->cell->height[map_layer];
1418  }
1419  }
1420 
1421  if (data->ypos - height > surface->h) {
1422  return false;
1423  }
1424 
1425  return true;
1426 }
1427 
1440 static void
1441 map_setup_render_data (SDL_Surface *surface,
1442  map_render_data_t *data,
1443  int *x,
1444  int *y,
1445  int *w,
1446  int *h)
1447 {
1448  HARD_ASSERT(surface != NULL);
1449  HARD_ASSERT(data != NULL);
1450 
1451  data->x = map_width - (map_width / 2) - 1;
1452  data->y = map_height - (map_height / 2) - 1;
1453  data->cell = MAP_CELL_GET_MIDDLE(data->x, data->y);
1455  MapData.player_sub_layer);
1456 
1457 
1458  if (surface == cur_widget[MAP_ID]->surface) {
1459  data->midx = map_width * MAP_FOW_SIZE / 2;
1460  data->midy = map_height * MAP_FOW_SIZE / 2;
1461 
1462  int maxw = surface->w / 2.0 / (MAP_TILE_POS_XOFF / 2.0);
1463  int maxh = surface->h / 2.0 / (MAP_TILE_POS_YOFF / 2.0);
1464  int maxtiles = MAX(maxh, maxw);
1465 
1466  if (x != NULL) {
1467  *x = data->midx - maxtiles;
1468  if (*x < 0) {
1469  *x = 0;
1470  }
1471  }
1472 
1473  if (y != NULL) {
1474  *y = data->midy - maxtiles;
1475  if (*y < 0) {
1476  *y = 0;
1477  }
1478  }
1479 
1480  if (w != NULL) {
1481  *w = data->midx + maxtiles;
1482  if (*w > map_width * MAP_FOW_SIZE) {
1483  *w = map_width * MAP_FOW_SIZE;
1484  }
1485  }
1486 
1487  if (h != NULL) {
1488  *h = data->midy + maxtiles;
1489  if (*h > map_height * MAP_FOW_SIZE) {
1490  *h = map_height * MAP_FOW_SIZE;
1491  }
1492  }
1493  } else {
1494  if (x != NULL) {
1495  *x = 0;
1496  }
1497 
1498  if (y != NULL) {
1499  *y = 0;
1500  }
1501 
1502  if (w != NULL) {
1503  *w = map_width * MAP_FOW_SIZE;
1504  }
1505 
1506  if (h != NULL) {
1507  *h = map_height * MAP_FOW_SIZE;
1508  }
1509  }
1510 
1511 }
1512 
1519 void
1520 map_draw_map (SDL_Surface *surface)
1521 {
1522  HARD_ASSERT(surface != NULL);
1523 
1524  map_render_data_t data = {0};
1525  int x, y, w, h;
1526  map_setup_render_data(surface, &data, &x, &y, &w, &h);
1527 
1528  /* Draw floor and fmasks. */
1529  for (data.x = x; data.x < w; data.x++) {
1530  for (data.y = y; data.y < h; data.y++) {
1531  if (!map_should_draw(surface, &data)) {
1532  continue;
1533  }
1534 
1535  for (data.layer = LAYER_FLOOR;
1536  data.layer <= LAYER_FMASK;
1537  data.layer++) {
1538  if (data.cell->priority[0] & (1 << (data.layer - 1))) {
1539  continue;
1540  }
1541 
1542  draw_map_object(surface, &data);
1543  }
1544  }
1545  }
1546 
1547  uint8_t floor_layer_pl = GET_MAP_LAYER(LAYER_FLOOR,
1548  MapData.player_sub_layer);
1549 
1550  /* Now draw everything else. */
1551  for (data.x = x; data.x < w; data.x++) {
1552  for (data.y = y; data.y < h; data.y++) {
1553  if (!map_should_draw(surface, &data)) {
1554  continue;
1555  }
1556 
1557  for (data.layer = LAYER_FLOOR;
1558  data.layer <= NUM_LAYERS;
1559  data.layer++) {
1560  for (data.sub_layer = 0;
1561  data.sub_layer < NUM_SUB_LAYERS;
1562  data.sub_layer++) {
1563  if (data.sub_layer == 0 &&
1564  (data.layer == LAYER_FLOOR ||
1565  data.layer == LAYER_FMASK)) {
1566  continue;
1567  }
1568 
1569  /* Skip objects on the effect layer with non-zero sub-layer
1570  * because they will be rendered later. */
1571  if (data.layer == LAYER_EFFECT && data.sub_layer != 0) {
1572  uint8_t effect_layer =
1573  GET_MAP_LAYER(LAYER_EFFECT, data.sub_layer);
1574  uint8_t floor_layer =
1575  GET_MAP_LAYER(LAYER_FLOOR,
1576  MapData.player_sub_layer);
1577  if (data.cell->height[effect_layer] >=
1578  data.cell->height[floor_layer]) {
1579  continue;
1580  }
1581  }
1582 
1583  if (data.cell->priority[data.sub_layer] &
1584  (1 << (data.layer - 1))) {
1585  continue;
1586  }
1587 
1588  draw_map_object(surface, &data);
1589  }
1590  }
1591 
1592  for (data.sub_layer = 0;
1593  data.sub_layer < NUM_SUB_LAYERS;
1594  data.sub_layer++) {
1595  uint8_t map_layer = GET_MAP_LAYER(LAYER_FLOOR,
1596  data.sub_layer);
1597  if (data.cell->height[map_layer] >
1598  data.cell->height[floor_layer_pl]) {
1599  continue;
1600  }
1601 
1602  for (data.layer = LAYER_FLOOR;
1603  data.layer <= NUM_LAYERS;
1604  data.layer++) {
1605  if (!(data.cell->priority[data.sub_layer] &
1606  (1 << (data.layer - 1)))) {
1607  continue;
1608  }
1609 
1610  if (data.layer == LAYER_EFFECT && data.sub_layer != 0) {
1611  map_layer = GET_MAP_LAYER(LAYER_EFFECT,
1612  data.sub_layer);
1613  if (data.cell->height[map_layer] >=
1614  data.cell->height[floor_layer_pl]) {
1615  continue;
1616  }
1617  }
1618 
1619  draw_map_object(surface, &data);
1620  }
1621  }
1622 
1623  for (data.layer = LAYER_FLOOR;
1624  data.layer <= NUM_LAYERS;
1625  data.layer++) {
1626  if (!(data.cell->priority[0] & (1 << (data.layer - 1)))) {
1627  continue;
1628  }
1629 
1630  draw_map_object(surface, &data);
1631  }
1632 
1633  data.layer = LAYER_EFFECT;
1634 
1635  for (data.sub_layer = NUM_SUB_LAYERS - 1;
1636  data.sub_layer >= 1;
1637  data.sub_layer--) {
1638  if (data.cell->priority[data.sub_layer] &
1639  (1 << (LAYER_EFFECT - 1))) {
1640  continue;
1641  }
1642 
1643  uint8_t map_layer = GET_MAP_LAYER(LAYER_EFFECT, data.sub_layer);
1644  if (data.cell->height[map_layer] <
1645  data.cell->height[floor_layer_pl]) {
1646  continue;
1647  }
1648 
1649  if (surface == cur_widget[MAP_ID]->surface &&
1650  map_should_cull(surface, &data)) {
1651  continue;
1652  }
1653 
1654  draw_map_object(surface, &data);
1655  }
1656 
1657  for (data.sub_layer = 0;
1658  data.sub_layer < NUM_SUB_LAYERS;
1659  data.sub_layer++) {
1660  uint8_t map_layer = GET_MAP_LAYER(LAYER_FLOOR,
1661  data.sub_layer);
1662  if (data.cell->height[map_layer] <=
1663  data.cell->height[floor_layer_pl]) {
1664  continue;
1665  }
1666 
1667  for (data.layer = LAYER_FLOOR;
1668  data.layer <= NUM_LAYERS;
1669  data.layer++) {
1670  if (!(data.cell->priority[data.sub_layer] &
1671  (1 << (data.layer - 1)))) {
1672  continue;
1673  }
1674 
1675  draw_map_object(surface, &data);
1676  }
1677  }
1678 
1679  data.layer = LAYER_EFFECT;
1680 
1681  for (data.sub_layer = NUM_SUB_LAYERS - 1;
1682  data.sub_layer >= 1;
1683  data.sub_layer--) {
1684  uint8_t map_layer = GET_MAP_LAYER(LAYER_EFFECT,
1685  data.sub_layer);
1686  if (data.cell->height[map_layer] <
1687  data.cell->height[floor_layer_pl]) {
1688  continue;
1689  }
1690 
1691  uint8_t map_layer2 = GET_MAP_LAYER(LAYER_FLOOR,
1692  data.sub_layer - 1);
1693  if (data.cell->height[map_layer] <=
1694  data.cell->height[map_layer2]) {
1695  continue;
1696  }
1697 
1698  map_layer2 = GET_MAP_LAYER(LAYER_EFFECT,
1699  data.sub_layer - 1);
1700  if (data.cell->height[map_layer] <=
1701  data.cell->height[map_layer2]) {
1702  continue;
1703  }
1704 
1705  if (surface == cur_widget[MAP_ID]->surface &&
1706  map_should_cull(surface, &data)) {
1707  continue;
1708  }
1709 
1710  draw_map_object(surface, &data);
1711  }
1712 
1713  if (data.cell->priority[0] & (1 << (LAYER_WALL - 1)) &&
1714  data.cell->height[GET_MAP_LAYER(LAYER_WALL, 0)] > 0) {
1715  data.layer = LAYER_WALL;
1716  data.sub_layer = 0;
1717  draw_map_object(surface, &data);
1718  }
1719  }
1720  }
1721 
1722  if (surface != cur_widget[MAP_ID]->surface) {
1723  return;
1724  }
1725 
1726  for (data.x = x; data.x < w; data.x++) {
1727  for (data.y = y; data.y < h; data.y++) {
1728  if (!map_should_draw(surface, &data)) {
1729  continue;
1730  }
1731 
1732  for (data.sub_layer = NUM_SUB_LAYERS - 1;
1733  data.sub_layer >= 1;
1734  data.sub_layer--) {
1735  uint8_t map_layer = GET_MAP_LAYER(LAYER_EFFECT, data.sub_layer);
1736  if (data.cell->height[map_layer] != 0 &&
1737  data.cell->faces[map_layer] != 0) {
1738  data.cell = NULL;
1739  break;
1740  }
1741  }
1742 
1743  if (data.cell == NULL) {
1744  continue;
1745  }
1746 
1747  data.layer = LAYER_LIVING;
1748  data.alpha_forced = 100;
1749 
1750  for (data.sub_layer = 0;
1751  data.sub_layer < NUM_SUB_LAYERS;
1752  data.sub_layer++) {
1753  draw_map_object(surface, &data);
1754  }
1755  }
1756  }
1757 
1758  if (data.tiles != NULL) {
1759  for (size_t i = 0; i < data.tiles_num; i++) {
1760  SDL_Rect box;
1761  box.x = data.tiles[i].x;
1762  box.y = data.tiles[i].y;
1763  box.w = MAP_TILE_POS_XOFF;
1764  box.h = MAP_TILE_POS_YOFF;
1765  text_show_format(surface,
1766  FONT("arial", 9),
1767  box.x,
1768  box.y,
1769  COLOR_WHITE,
1772  &box,
1773  "%d,%d",
1774  data.tiles[i].w,
1775  data.tiles[i].h);
1776  }
1777 
1778  efree(data.tiles);
1779  }
1780 
1781  if (data.target_cell != NULL && cpl.target_code != 0) {
1782  const char *hp_color;
1783 
1784  if (cpl.target_hp > 90) {
1785  hp_color = COLOR_GREEN;
1786  } else if (cpl.target_hp > 75) {
1787  hp_color = COLOR_DGOLD;
1788  } else if (cpl.target_hp > 50) {
1789  hp_color = COLOR_HGOLD;
1790  } else if (cpl.target_hp > 25) {
1791  hp_color = COLOR_YELLOW;
1792  } else if (cpl.target_hp > 10) {
1793  hp_color = COLOR_ORANGE;
1794  } else {
1795  hp_color = COLOR_RED;
1796  }
1797 
1799  data.target_cell->pname[data.target_layer][0] != '\0')) {
1800  text_show(surface,
1801  FONT_SANS9,
1802  cpl.target_name,
1803  data.target_rect.x + data.target_rect.w / 2 -
1804  text_get_width(FONT_SANS9, cpl.target_name, 0) / 2,
1805  data.target_rect.y - 15,
1806  cpl.target_color,
1807  TEXT_OUTLINE,
1808  NULL);
1809  }
1810 
1811  rectangle_create(surface,
1812  data.target_rect.x - 2,
1813  data.target_rect.y - 2,
1814  1,
1815  5,
1816  hp_color);
1817  rectangle_create(surface,
1818  data.target_rect.x - 2,
1819  data.target_rect.y - 2,
1820  3,
1821  1,
1822  hp_color);
1823  rectangle_create(surface,
1824  data.target_rect.x - 2,
1825  data.target_rect.y + 2,
1826  3,
1827  1,
1828  hp_color);
1829  rectangle_create(surface,
1830  data.target_rect.x + data.target_rect.w + 1,
1831  data.target_rect.y - 2,
1832  1,
1833  5,
1834  hp_color);
1835  rectangle_create(surface,
1836  data.target_rect.x + data.target_rect.w - 1,
1837  data.target_rect.y - 2,
1838  3,
1839  1,
1840  hp_color);
1841  rectangle_create(surface,
1842  data.target_rect.x + data.target_rect.w - 1,
1843  data.target_rect.y + 2,
1844  3,
1845  1,
1846  hp_color);
1847 
1848  data.target_rect.w = data.target_rect.w / 100.0 *
1849  data.target_cell->probe[data.target_layer];
1850  data.target_rect.w = MAX(1, MIN(100, data.target_rect.w));
1851  rectangle_create(surface,
1852  data.target_rect.x,
1853  data.target_rect.y,
1854  data.target_rect.w,
1855  data.target_rect.h,
1856  hp_color);
1857  }
1858 
1859 #undef CALCULATE_POSITIONS
1860 }
1861 
1871 void map_draw_one(int x, int y, SDL_Surface *surface)
1872 {
1873  map_render_data_t data = {0};
1874 
1875  map_setup_render_data(cur_widget[MAP_ID]->surface, &data,
1876  NULL, NULL, NULL, NULL);
1877 
1878  data.x = x;
1879  data.y = y;
1880  SOFT_ASSERT(map_should_draw(cur_widget[MAP_ID]->surface, &data),
1881  "map_should_draw() returned false");
1882 
1883  if (surface->w > MAP_TILE_POS_XOFF) {
1884  data.xpos -= (surface->w - MAP_TILE_POS_XOFF) / 2;
1885  }
1886 
1887  if (data.cell->faces[0] != 0) {
1888  data.ypos -= get_top_floor_height(data.cell, MapData.player_sub_layer);
1889  data.ypos += data.player_height_offset;
1890  }
1891 
1892  double zoom = setting_get_int(OPT_CAT_MAP, OPT_MAP_ZOOM) / 100.0;
1893  data.xpos *= zoom;
1894  data.ypos *= zoom;
1895 
1896  sprite_effects_t effects = {0};
1897  effects.zoom_x = 100.0 * zoom;
1898  effects.zoom_y = 100.0 * zoom;
1899 
1900  /* Outside of the "visible" area; always render as fog of war
1901  * (grayscale). */
1902  if (x < map_width * (MAP_FOW_SIZE / 2) ||
1903  x >= map_width * (MAP_FOW_SIZE / 2) + map_width ||
1904  y < map_height * (MAP_FOW_SIZE / 2) ||
1905  y >= map_height * (MAP_FOW_SIZE / 2) + map_height) {
1906  BIT_SET(effects.flags, SPRITE_FLAG_FOW);
1907  }
1908 
1910  widget_x(cur_widget[MAP_ID]) + data.xpos,
1911  widget_y(cur_widget[MAP_ID]) + data.ypos,
1912  NULL,
1913  surface,
1914  &effects);
1915 }
1916 
1925 static void
1926 send_move_path (int tx, int ty)
1927 {
1928  packet_struct *packet;
1929 
1930  if (tx < 0 || ty < 0 || tx >= map_width || ty >= map_height) {
1931  return;
1932  }
1933 
1934  packet = packet_new(SERVER_CMD_MOVE_PATH, 8, 0);
1935  packet_append_uint8(packet, tx);
1936  packet_append_uint8(packet, ty);
1937  socket_send_packet(packet);
1938 }
1939 
1949 static void send_target(int x, int y, uint32_t count)
1950 {
1951  packet_struct *packet;
1952 
1953  if ((x < 0 || y < 0 || x >= map_width || y >= map_height) &&
1954  !(x == -1 && y == -1)) {
1955  return;
1956  }
1957 
1958  packet = packet_new(SERVER_CMD_TARGET, 16, 0);
1959 
1960  if (x == -1 && y == -1) {
1961  packet_append_uint8(packet, CMD_TARGET_CLEAR);
1962  } else {
1963  packet_append_uint8(packet, CMD_TARGET_MAPXY);
1964  packet_append_uint8(packet, x);
1965  packet_append_uint8(packet, y);
1966  packet_append_uint32(packet, count);
1967  }
1968 
1969  socket_send_packet(packet);
1970 }
1971 
1981 static int map_target_cmp(const void *a, const void *b)
1982 {
1983  double x, y, x2, y2;
1984  unsigned long dist1, dist2;
1985 
1986  x = ((const map_target_struct *) a)->x - (map_width / 2.0f);
1987  y = ((const map_target_struct *) a)->y - (map_height / 2.0f);
1988 
1989  x2 = ((const map_target_struct *) b)->x - (map_width / 2.0f);
1990  y2 = ((const map_target_struct *) b)->y - (map_height / 2.0f);
1991 
1992  dist1 = isqrt(x * x + y * y);
1993  dist2 = isqrt(x2 * x2 + y2 * y2);
1994 
1995  if (dist1 < dist2) {
1996  return -1;
1997  } else if (dist1 > dist2) {
1998  return 1;
1999  } else {
2000  return 0;
2001  }
2002 }
2003 
2009 void map_target_handle(uint8_t is_friend)
2010 {
2011  int x, y, layer;
2012  struct MapCell *cell;
2013  UT_array *targets;
2014  UT_icd icd = {sizeof(map_target_struct), NULL, NULL, NULL};
2015  map_target_struct *p;
2016  uint32_t curr_target;
2017 
2018  if (cpl.target_is_friend != is_friend) {
2019  cpl.target_object_index = 0;
2020  }
2021 
2022  utarray_new(targets, &icd);
2023  curr_target = 0;
2024 
2025  for (x = 0; x < map_width; x++) {
2026  for (y = 0; y < map_height; y++) {
2027  cell = MAP_CELL_GET_MIDDLE(x, y);
2028 
2029  if (cell->fow) {
2030  continue;
2031  }
2032 
2033  for (layer = 0; layer < NUM_REAL_LAYERS; layer++) {
2034  if (cell->faces[layer] && cell->target_object_count[layer] &&
2035  cell->target_is_friend[layer] == is_friend) {
2036  map_target_struct target;
2037 
2038  target.count = cell->target_object_count[layer];
2039  target.x = x;
2040  target.y = y;
2041  utarray_push_back(targets, &target);
2042 
2043  if (cell->probe[layer] != 0) {
2044  curr_target = target.count;
2045  }
2046  }
2047  }
2048  }
2049  }
2050 
2051  utarray_sort(targets, map_target_cmp);
2052 
2053  if (cpl.target_object_index >= utarray_len(targets)) {
2054  cpl.target_object_index = 0;
2055  }
2056 
2057  if (cpl.target_object_index == 0) {
2058  p = (map_target_struct *) utarray_front(targets);
2059 
2060  if (p != NULL && p->count == curr_target) {
2061  cpl.target_object_index++;
2062  }
2063  }
2064 
2065  p = (map_target_struct *) utarray_eltptr(targets, cpl.target_object_index);
2066 
2067  if (p != NULL) {
2068  send_target(p->x, p->y, p->count);
2069  cpl.target_object_index++;
2070  } else if (cpl.target_is_friend != is_friend) {
2071  send_target(-1, -1, 0);
2072  }
2073 
2074  cpl.target_is_friend = is_friend;
2075 
2076  utarray_free(targets);
2077 }
2078 
2094 bool
2095 mouse_to_tile_coords (int mx, int my, int *tx, int *ty)
2096 {
2097  map_render_data_t data = {0};
2098  int x, y, w, h;
2099  map_setup_render_data(cur_widget[MAP_ID]->surface, &data, &x, &y, &w, &h);
2100 
2101  double zoom = setting_get_int(OPT_CAT_MAP, OPT_MAP_ZOOM) / 100.0;
2102 
2103  mx -= widget_x(cur_widget[MAP_ID]);
2104  my -= widget_y(cur_widget[MAP_ID]);
2105 
2106  for (data.x = w - 1; data.x >= x; data.x--) {
2107  for (data.y = h - 1; data.y >= y; data.y--) {
2108  if (!map_should_draw(cur_widget[MAP_ID]->surface, &data)) {
2109  continue;
2110  }
2111 
2112  if ((data.cell->fow || MapData.height_diff) &&
2113  abs(get_top_floor_height(data.cell, MapData.player_sub_layer) -
2115  continue;
2116  }
2117 
2118  data.xpos *= zoom;
2119  data.ypos *= zoom;
2120 
2121  if (data.cell->faces[0] != 0) {
2122  int height = get_top_floor_height(data.cell,
2123  MapData.player_sub_layer);
2124  data.ypos = (data.ypos - height * zoom) +
2125  data.player_height_offset * zoom;
2126  }
2127 
2128  uint32_t stretch = 0;
2129  int16_t max_height = 0;
2130 
2131  for (int sub_layer = 0; sub_layer < NUM_SUB_LAYERS; sub_layer++) {
2132  int16_t height = data.cell->height[sub_layer * NUM_LAYERS];
2133  if (height > max_height) {
2134  max_height = height;
2135  stretch = data.cell->stretch[sub_layer];
2136  }
2137  }
2138 
2139  int stretch_height = (stretch >> 24) & 0xff;
2140 
2141  /* See if this square matches our 48x24 box shape. */
2142  if (mx >= data.xpos &&
2143  mx <= data.xpos + (MAP_TILE_POS_XOFF * zoom) &&
2144  my >= data.ypos &&
2145  my <= data.ypos + (MAP_TILE_YOFF + stretch_height) * zoom) {
2146  if (tilestretcher_coords_in_tile(stretch,
2147  (mx - data.xpos) / zoom,
2148  (my - data.ypos) / zoom)) {
2149  if (tx != NULL) {
2150  *tx = data.x;
2151  }
2152 
2153  if (ty != NULL) {
2154  *ty = data.y;
2155  }
2156 
2157  return true;
2158  }
2159  }
2160  }
2161  }
2162 
2163  return false;
2164 }
2165 
2172 bool
2174 {
2175  int x, y;
2176  Uint8 state = SDL_GetMouseState(&x, &y);
2177 
2178  if ((state != (SDL_BUTTON(SDL_BUTTON_RIGHT) |
2179  SDL_BUTTON(SDL_BUTTON_LEFT)) &&
2180  state != SDL_BUTTON(SDL_BUTTON_MIDDLE))) {
2181  return false;
2182  }
2183 
2184  int tx, ty;
2185  if (!mouse_to_tile_coords(x, y, &tx, &ty)) {
2186  return false;
2187  }
2188 
2189  int rx = tx - map_width * (MAP_FOW_SIZE / 2);
2190  int ry = ty - map_height * (MAP_FOW_SIZE / 2);
2191 
2192  cpl.fire_on = 1;
2193  move_keys(dir_from_tile_coords(rx, ry));
2194  cpl.fire_on = 0;
2195  return true;
2196 }
2197 
2207 static void menu_map_walk_here(widgetdata *widget, widgetdata *menuitem,
2208  SDL_Event *event)
2209 {
2210  int tx, ty;
2211 
2212  if (mouse_to_tile_coords(cur_widget[MENU_ID]->x, cur_widget[MENU_ID]->y,
2213  &tx, &ty)) {
2214  int rx = tx - map_width * (MAP_FOW_SIZE / 2);
2215  int ry = ty - map_height * (MAP_FOW_SIZE / 2);
2216  send_move_path(rx, ry);
2217  }
2218 }
2219 
2229 static void menu_map_talk_to(widgetdata *widget, widgetdata *menuitem,
2230  SDL_Event *event)
2231 {
2232  int tx, ty;
2233 
2234  if (mouse_to_tile_coords(cur_widget[MENU_ID]->x, cur_widget[MENU_ID]->y,
2235  &tx, &ty)) {
2236  int rx = tx - map_width * (MAP_FOW_SIZE / 2);
2237  int ry = ty - map_height * (MAP_FOW_SIZE / 2);
2238  send_target(rx, ry, 0);
2239  keybind_process_command("?HELLO");
2240  }
2241 }
2242 
2244 static void widget_draw(widgetdata *widget)
2245 {
2246  static int gfx_toggle = 0;
2247  SDL_Rect box;
2248  int mx, my;
2249 
2250  if (widget->surface == NULL ||
2251  widget->surface->w != widget->w ||
2252  widget->surface->h != widget->h) {
2253  if (widget->surface != NULL) {
2254  SDL_FreeSurface(widget->surface);
2255  map_redraw_flag = 1;
2256  }
2257 
2258  widget->surface = SDL_CreateRGBSurface(get_video_flags(),
2259  widget->w,
2260  widget->h,
2261  video_get_bpp(),
2262  0,
2263  0,
2264  0,
2265  0);
2266  }
2267 
2268  /* Make sure the map widget is always the last to handle events for. */
2269  SetPriorityWidget_reverse(widget);
2270 
2271  double zoom = setting_get_int(OPT_CAT_MAP, OPT_MAP_ZOOM) / 100.0;
2272  if (widget_set_zoom(widget, zoom)) {
2273  map_redraw_flag = 1;
2274  }
2275 
2276  /* We re-create the map only when there is a change. */
2277  if (map_redraw_flag) {
2278  SDL_FillRect(widget->surface, NULL, 0);
2279  map_draw_map(widget->surface);
2280  map_redraw_flag = 0;
2282 
2283  if (setting_get_int(OPT_CAT_MAP, OPT_MAP_ZOOM) != 100) {
2284  if (zoomed) {
2285  SDL_FreeSurface(zoomed);
2286  }
2287 
2288  zoomed = zoomSurface(widget->surface,
2292  }
2293  }
2294 
2295  box.x = widget_x(widget);
2296  box.y = widget_y(widget);
2297 
2298  if (setting_get_int(OPT_CAT_MAP, OPT_MAP_ZOOM) == 100) {
2299  SDL_BlitSurface(widget->surface, NULL, ScreenSurface, &box);
2300  } else {
2301  SDL_BlitSurface(zoomed, NULL, ScreenSurface, &box);
2302  }
2303 
2304  if (map_show_mouse && widget_mouse_event.owner == cur_widget[MAP_ID]) {
2305  int tx, ty;
2306  if (!mouse_to_tile_coords(cursor_x, cursor_y, &tx, &ty)) {
2307  map_show_mouse = false;
2308  } else {
2309  map_draw_one(tx, ty, TEXTURE_CLIENT("square_highlight"));
2310  }
2311  }
2312 
2313  /* The damage numbers */
2314  map_anims_play();
2315 
2316  map_render_data_t data = {0};
2317  map_setup_render_data(widget->surface, &data, NULL, NULL, NULL, NULL);
2318 
2319  int xpos = widget_x(widget) + widget_w(widget) / 2;
2320  int ypos = widget_y(widget) + widget_h(widget) / 2;
2321  ypos -= MAP_TILE_POS_YOFF * 1.5 + 7;
2322 
2323  /* Draw warning icons above player */
2324  if ((gfx_toggle++ & 63) < 25) {
2326  double hp_percent = (double) cpl.stats.hp / cpl.stats.maxhp * 100.0;
2327  if (warn != 0 && warn >= hp_percent) {
2328  SDL_Surface *texture = TEXTURE_CLIENT("warn_hp");
2330  xpos - texture->w / 2,
2331  ypos - texture->h / 2,
2332  NULL,
2333  texture);
2334  }
2335  } else {
2337  double food_percent = (double) cpl.stats.food / 1000.0 * 100.0;
2338  if (warn != 0 && warn >= food_percent) {
2339  SDL_Surface *texture = TEXTURE_CLIENT("warn_food");
2341  xpos - texture->w / 2,
2342  ypos - texture->h / 2,
2343  NULL,
2344  texture);
2345  }
2346  }
2347 
2348  /* Process message animations */
2349  if (msg_anim.message[0] != '\0') {
2350  if ((LastTick - msg_anim.tick) < 3000) {
2351  int bmoff, y_offset;
2352  char *msg, *cp;
2353 
2354  bmoff = (int) ((50.0f / 3.0f) * ((float) (LastTick - msg_anim.tick)
2355  / 1000.0f) * ((float) (LastTick - msg_anim.tick) /
2356  1000.0f) + ((int) (150.0f * ((float) (LastTick -
2357  msg_anim.tick) / 3000.0f))));
2358  y_offset = 0;
2359  msg = estrdup(msg_anim.message);
2360 
2361  cp = strtok(msg, "\n");
2362 
2363  while (cp) {
2364  text_show(ScreenSurface, FONT_SERIF16, cp, widget_x(widget) +
2365  widget_w(widget) / 2 - text_get_width(FONT_SERIF16,
2366  cp, TEXT_OUTLINE) / 2, widget_y(widget) + 300 - bmoff +
2367  y_offset, msg_anim.color, TEXT_OUTLINE | TEXT_MARKUP,
2368  NULL);
2369  y_offset += FONT_HEIGHT(FONT_SERIF16);
2370  cp = strtok(NULL, "\n");
2371  }
2372 
2373  efree(msg);
2374  widget->redraw++;
2375  } else {
2376  msg_anim.message[0] = '\0';
2377  }
2378  }
2379 
2380  /* Holding the right mouse button for some time, create a menu. */
2381  if (SDL_GetMouseState(&mx, &my) == SDL_BUTTON(SDL_BUTTON_RIGHT) &&
2382  right_click_ticks != -1 &&
2383  SDL_GetTicks() - right_click_ticks > 500) {
2384  widgetdata *menu;
2385 
2386  menu = create_menu(mx, my, widget);
2387  add_menuitem(menu, "Walk Here", &menu_map_walk_here, MENU_NORMAL, 0);
2388  add_menuitem(menu, "Talk To NPC", &menu_map_talk_to, MENU_NORMAL, 0);
2389  widget_menu_standard_items(widget, menu);
2390  menu_finalize(menu);
2391  right_click_ticks = -1;
2392  }
2393 }
2394 
2396 static int widget_event(widgetdata *widget, SDL_Event *event)
2397 {
2398  if (!EVENT_IS_MOUSE(event)) {
2399  return 0;
2400  }
2401 
2402  /* Check if the mouse is in play field. */
2403  int tx, ty;
2404  if (!mouse_to_tile_coords(event->motion.x, event->motion.y, &tx, &ty)) {
2405  return 0;
2406  }
2407 
2408  int rx = tx - map_width * (MAP_FOW_SIZE / 2);
2409  int ry = ty - map_height * (MAP_FOW_SIZE / 2);
2410 
2411  if (event->type == SDL_MOUSEBUTTONUP) {
2412  /* Send target command if we released the right button in time;
2413  * otherwise the widget menu will be created. */
2414  if (event->button.button == SDL_BUTTON_RIGHT &&
2415  SDL_GetTicks() - right_click_ticks < 500) {
2416  send_target(rx, ry, 0);
2417  }
2418 
2419  right_click_ticks = -1;
2420  return 1;
2421  } else if (event->type == SDL_MOUSEBUTTONDOWN) {
2422  if (event->button.button == SDL_BUTTON_RIGHT) {
2423  right_click_ticks = SDL_GetTicks();
2424  } else if (SDL_GetMouseState(NULL, NULL) == SDL_BUTTON_LEFT) {
2425  /* Running */
2426 
2427  if (cpl.fire_on || cpl.run_on) {
2428  move_keys(dir_from_tile_coords(rx, ry));
2429  } else {
2430  send_move_path(rx, ry);
2431  }
2432  }
2433 
2434  return 1;
2435  } else if (event->type == SDL_MOUSEMOTION) {
2436  if (tx != old_map_mouse_x || ty != old_map_mouse_y) {
2437  old_map_mouse_x = tx;
2438  old_map_mouse_y = ty;
2439  map_show_mouse = true;
2440 
2441  return 1;
2442  }
2443  }
2444 
2445  return 0;
2446 }
2447 
2449 static void widget_background(widgetdata *widget, int draw)
2450 {
2451  if (!widget->redraw) {
2452  region_map_ready(MapData.region_map);
2453  }
2454 }
2455 
2457 static void widget_deinit(widgetdata *widget)
2458 {
2459  if (cells != NULL) {
2460  efree(cells);
2461  cells = NULL;
2462  }
2463 
2464  region_map_free(MapData.region_map);
2465  MapData.region_map = NULL;
2466 }
2467 
2472 {
2473  HARD_ASSERT(MapData.region_map == NULL);
2474 
2475  MapData.region_map = region_map_create();
2476 
2477  widget->draw_func = widget_draw;
2478  widget->event_func = widget_event;
2479  widget->background_func = widget_background;
2480  widget->deinit_func = widget_deinit;
2481  widget->menu_handle_func = NULL;
2482 
2483  SetPriorityWidget_reverse(widget);
2484 }
2485 
2501 struct map_anim *map_anims_add(int type, int mapx, int mapy, int sub_layer,
2502  int value)
2503 {
2504  map_anim_t *anim;
2505  int num_ticks;
2506 
2507  anim = ecalloc(1, sizeof(*anim));
2508 
2509  DL_APPEND(first_anim, anim);
2510 
2511  /* Type */
2512  anim->type = type;
2513 
2514  /* Map coordinates */
2515  anim->mapx = mapx + MAP_STARTX;
2516  anim->mapy = mapy + MAP_STARTY;
2517 
2518  /* Sub-layer. */
2519  anim->sub_layer = sub_layer;
2520  /* Amount of damage */
2521  anim->value = value;
2522 
2523  /* Current time in MilliSeconds */
2524  anim->start_tick = LastTick;
2525 
2526  switch (type) {
2527  case ANIM_DAMAGE:
2528  /* How many ticks to display */
2529  num_ticks = 850;
2530  anim->last_tick = anim->start_tick + num_ticks;
2531  /* 850 ticks 25 pixel move up */
2532  anim->yoff = -(25.0f / 850.0f);
2533  break;
2534 
2535  case ANIM_KILL:
2536  /* How many ticks to display */
2537  num_ticks = 850;
2538  anim->last_tick = anim->start_tick + num_ticks;
2539  /* 850 ticks 25 pixel move up */
2540  anim->yoff = -(25.0f / 850.0f);
2541  break;
2542  }
2543 
2544  return anim;
2545 }
2546 
2553 {
2554  HARD_ASSERT(anim != NULL);
2555 
2556  DL_DELETE(first_anim, anim);
2557 
2558  efree(anim);
2559 }
2560 
2569 {
2570  map_anim_t *anim;
2571  DL_FOREACH(first_anim, anim) {
2572  anim->mapx -= xoff;
2573  anim->mapy -= yoff;
2574  }
2575 }
2576 
2581 {
2582  map_anim_t *anim, *tmp;
2583  DL_FOREACH_SAFE(first_anim, anim, tmp) {
2584  maps_anims_remove(anim);
2585  }
2586 }
2587 
2591 void map_anims_play(void)
2592 {
2593  map_render_data_t data = {0};
2594  map_setup_render_data(cur_widget[MAP_ID]->surface, &data,
2595  NULL, NULL, NULL, NULL);
2596 
2597  map_anim_t *anim, *tmp;
2598  DL_FOREACH_SAFE(first_anim, anim, tmp) {
2599  /* Have we passed the last tick */
2600  if (LastTick > anim->last_tick) {
2601  maps_anims_remove(anim);
2602  continue;
2603  }
2604 
2605  data.x = anim->mapx;
2606  data.y = anim->mapy;
2607  if (!map_should_draw(cur_widget[MAP_ID]->surface, &data)) {
2608  continue;
2609  }
2610 
2611  data.xpos += MAP_TILE_POS_XOFF / 2;
2612  data.ypos -= MAP_TILE_POS_YOFF;
2613 
2614  uint32_t num_ticks = LastTick - anim->start_tick;
2615  data.ypos += num_ticks * anim->yoff;
2616  data.xpos += num_ticks * anim->xoff;
2617 
2618  char buf[32];
2619  switch (anim->type) {
2620  case ANIM_DAMAGE: {
2621  snprintf(VS(buf), "%d", abs(anim->value));
2622  int wd = text_get_width(FONT_MONO10, buf, TEXT_OUTLINE);
2623  const char *color = anim->value < 0 ? COLOR_GREEN : COLOR_ORANGE;
2624 
2626  FONT_MONO10,
2627  buf,
2628  data.xpos - wd / 2,
2629  data.ypos,
2630  color,
2631  TEXT_OUTLINE,
2632  NULL);
2633  break;
2634  }
2635 
2636  case ANIM_KILL: {
2637  snprintf(VS(buf), "%d", anim->value);
2638  int wd = text_get_width(FONT_MONO10, buf, TEXT_OUTLINE);
2639  int ht = text_get_height(FONT_MONO10, buf, 0);
2640  SDL_Surface *texture = TEXTURE_CLIENT("death");
2642  data.xpos - texture->w / 2,
2643  data.ypos - ht / 2 + 2,
2644  NULL,
2645  texture);
2646 
2648  FONT_MONO10,
2649  buf,
2650  data.xpos - wd / 2,
2651  data.ypos,
2652  COLOR_ORANGE,
2653  TEXT_OUTLINE,
2654  NULL);
2655 
2656  break;
2657  }
2658 
2659  default:
2660  LOG(ERROR, "Unknown animation type: %d", anim->type);
2661  break;
2662  }
2663  }
2664 }
2665 
2672 {
2673  return first_anim != NULL;
2674 }
char map_path[HUGE_BUF]
Definition: map.h:134
void sound_ambient_mapcroll(int xoff, int yoff)
Definition: sound.c:809
#define SPRITE_FLAG_FOW
Definition: sprite.h:72
static SDL_Surface * zoomed
Definition: map.c:55
#define FONT(font_name, font_size)
Definition: text.h:63
void surface_show_effects(SDL_Surface *surface, int x, int y, SDL_Rect *srcrect, SDL_Surface *src, const sprite_effects_t *effects)
Definition: sprite.c:830
void display_mapscroll(int dx, int dy, int old_w, int old_h)
Definition: map.c:220
static int map_target_cmp(const void *a, const void *b)
Definition: map.c:1981
int16_t zoom_x[NUM_REAL_LAYERS]
Definition: map.h:207
int widget_y(const widgetdata *widget)
Definition: widget.c:633
#define MAP_TILE_POS_YOFF
Definition: map.h:34
Definition: map.h:175
char pcolor[NUM_REAL_LAYERS][COLOR_BUF]
Definition: map.h:180
region_map_t * region_map_create(void)
Definition: region_map.c:54
static void send_target(int x, int y, uint32_t count)
Definition: map.c:1949
int value
This is the number to display.
Definition: map.h:310
int16_t midx
X index in the cells array of the middlemost cell.
Definition: map.c:949
void rectangle_create(SDL_Surface *surface, int x, int y, int w, int h, const char *color_notation)
Definition: sprite.c:1443
void adjust_tile_stretch(void)
Definition: map.c:624
int effect_start(const char *name)
Definition: effects.c:612
int32_t hp
Definition: player.h:66
void update_map_name(const char *name)
Definition: map.c:271
#define LAYER_FMASK
Definition: map.h:60
char name_new[HUGE_BUF]
Definition: map.h:122
void map_anims_mapscroll(int xoff, int yoff)
Definition: map.c:2568
uint8_t alpha
Alpha value.
Definition: sprite.h:48
SDL_Surface * bitmap
Definition: sprite.h:96
int32_t maxhp
Definition: player.h:69
Definition: map.h:304
uint8_t quick_pos[NUM_REAL_LAYERS]
Definition: map.h:183
int mapx
Map position X.
Definition: map.h:311
uint8_t flags[NUM_REAL_LAYERS]
Definition: map.h:192
struct map_anim * map_anims_add(int type, int mapx, int mapy, int sub_layer, int value)
Definition: map.c:2501
void load_mapdef_dat(void)
Definition: map.c:109
void update_map_height_diff(uint8_t height_diff)
Definition: map.c:289
uint8_t effect_has_overlay(void)
Definition: effects.c:704
int16_t rotate
Rotate value.
Definition: sprite.h:52
int16_t zoom_x
Horizontal zoom.
Definition: sprite.h:50
SDL_Surface * surface
Definition: widget.h:110
#define ANIM_DAMAGE
Definition: map.h:296
static void align_tile_stretch(int x, int y, int w, int h, int sub_layer)
Definition: map.c:498
static void map_setup_render_data(SDL_Surface *surface, map_render_data_t *data, int *x, int *y, int *w, int *h)
Definition: map.c:1441
int16_t align[NUM_REAL_LAYERS]
Definition: map.h:213
_multi_part_tile part[16]
Definition: map.h:113
struct region_map * region_map
Definition: map.h:169
void check_animation_status(int anum)
Definition: client.c:150
uint32_t get_video_flags(void)
Definition: video.c:365
#define SPRITE_FLAG_DARK
Definition: sprite.h:70
double xoff
Movement in X per tick.
Definition: map.h:314
uint32_t flags
Bit combination of Sprite drawing flags.
Definition: sprite.h:46
bool map_mouse_fire(void)
Definition: map.c:2173
int map_anims_need_redraw(void)
Definition: map.c:2671
static bool tiles_debug
Definition: map.c:88
void text_show(SDL_Surface *surface, font_struct *font, const char *text, int x, int y, const char *color_notation, uint64_t flags, SDL_Rect *box)
Definition: text.c:1983
uint8_t alpha[NUM_REAL_LAYERS]
Definition: map.h:198
struct MapCell * target_cell
Cell with the player's target.
Definition: map.c:957
#define LAYER_FLOOR
Definition: map.h:58
static int map_height
Definition: map.c:51
char target_name[MAX_BUF]
Definition: player.h:138
#define LAYER_ITEM
Definition: map.h:62
char target_hp
Definition: player.h:170
Stats stats
Definition: player.h:167
#define ANIM_KILL
Definition: map.h:298
double yoff
Movement in Y per tick.
Definition: map.h:315
#define COLOR_HGOLD
Definition: text.h:319
void map_clear_cell(int x, int y)
Definition: map.c:786
void menu_finalize(widgetdata *widget)
Definition: widget.c:2524
static int old_map_mouse_x
Definition: map.c:74
void map_set_data(int x, int y, int layer, int16_t face, uint8_t quick_pos, uint8_t obj_flags, const char *name, const char *name_color, int16_t height, uint8_t probe, int16_t zoom_x, int16_t zoom_y, int16_t align, uint8_t draw_double, uint8_t alpha, int16_t rotate, uint8_t infravision, uint32_t target_object_count, uint8_t target_is_friend, uint8_t anim_speed, uint8_t anim_facing, uint8_t anim_flags, uint8_t anim_state, uint8_t priority, uint8_t secondpass, const char *glow, uint8_t glow_speed)
Definition: map.c:675
uint8_t layer
Layer to render on.
Definition: map.c:964
#define ARCHDEF_FILE
Definition: config.h:37
uint8_t fow
Definition: map.h:247
void sound_ambient_clear(void)
Definition: sound.c:833
int32_t ypos
Y coordinate where to render.
Definition: map.c:953
uint8_t alpha_forced
Force applying the specified alpha value.
Definition: map.c:966
int xlen
Definition: map.h:139
int32_t player_height_offset
Player height offset.
Definition: map.c:954
int32_t stretch[NUM_SUB_LAYERS]
Definition: map.h:222
SDL_Surface * ScreenSurface
Definition: main.c:47
#define COLOR_ORANGE
Definition: text.h:291
char region_name[MAX_BUF]
Definition: map.h:125
#define COLOR_YELLOW
Definition: text.h:309
#define TEXT_MARKUP
Definition: text.h:224
uint32_t tick
Definition: main.h:132
uint32_t stretch
Tile stretching value.
Definition: sprite.h:49
void SetPriorityWidget_reverse(widgetdata *node)
Definition: widget.c:1902
#define TEXT_ALIGN_CENTER
Definition: text.h:230
uint8_t target_is_friend[NUM_REAL_LAYERS]
Definition: map.h:232
#define MAX_FACE_TILES
Definition: config.h:52
int16_t x
X index in the cells array.
Definition: map.c:946
#define SPRITE_FLAG_RED
Definition: sprite.h:74
void update_map_region_longname(const char *region_longname)
Definition: map.c:314
#define COLOR_RED
Definition: text.h:295
static void menu_map_talk_to(widgetdata *widget, widgetdata *menuitem, SDL_Event *event)
Definition: map.c:2229
uint8_t player_sub_layer
Definition: map.h:164
_mapdata MapData
Definition: map.c:64
#define COLOR_GREEN
Definition: text.h:297
char message[MAX_BUF]
Definition: main.h:129
bool mouse_to_tile_coords(int mx, int my, int *tx, int *ty)
Definition: map.c:2095
int height_diff
Definition: map.h:154
_multi_part_obj MultiArchs[16]
Definition: map.c:69
int xlen
Definition: map.h:107
int video_get_bpp(void)
Definition: video.c:334
bool region_map_ready(region_map_t *region_map)
Definition: region_map.c:200
void map_target_handle(uint8_t is_friend)
Definition: map.c:2009
static bool clioptions_option_tiles_debug(const char *arg, char **errmsg)
Definition: map.c:99
uint8_t fire_on
Definition: player.h:155
int type
Type of the animation, one of Animation types.
Definition: map.h:308
int widget_h(const widgetdata *widget)
Definition: widget.c:665
void image_request_face(int pnum)
Definition: image.c:402
static map_anim_t * first_anim
Definition: map.c:59
void maps_anims_remove(map_anim_t *anim)
Definition: map.c:2552
int yoff
Definition: map.h:95
uint8_t target_layer
Target's layer.
Definition: map.c:967
static void widget_draw(widgetdata *widget)
Definition: map.c:2244
int ylen
Definition: map.h:142
int text_get_height(font_struct *font, const char *text, uint64_t flags)
Definition: text.c:2364
int16_t y
Y index in the cells array.
Definition: map.c:947
#define MAP_TILE_YOFF
Definition: map.h:49
uint32_t last_tick
This is the end-tick.
Definition: map.h:318
void update_map_path(const char *map_path)
Definition: map.c:324
Client_Player cpl
Definition: client.c:50
SDL_Rect target_rect
Coordinate information for player's target.
Definition: map.c:962
_face_struct FaceList[MAX_FACE_TILES]
Definition: main.c:77
int posx
Definition: map.h:145
int16_t height[NUM_REAL_LAYERS]
Definition: map.h:204
uint8_t infravision[NUM_REAL_LAYERS]
Definition: map.h:219
#define LAYER_LIVING
Definition: map.h:68
int in_building
Definition: map.h:159
int xoff
Definition: map.h:92
static bool map_should_draw(SDL_Surface *surface, map_render_data_t *data)
Definition: map.c:1383
bool widget_set_zoom(widgetdata *widget, double zoom)
Definition: widget.c:681
int64_t setting_get_int(int cat, int setting)
Definition: settings.c:414
char name[40]
Definition: player.h:173
widgetdata * cur_widget[TOTAL_SUBWIDGETS]
Definition: widget.c:75
uint8_t redraw
Definition: widget.h:72
uint8_t run_on
Definition: player.h:158
void region_map_free(region_map_t *region_map)
Definition: region_map.c:93
int16_t midy
Y index in the cells array of the middlemost cell.
Definition: map.c:950
#define FONT_HEIGHT(font)
Definition: text.h:327
int target_code
Definition: player.h:132
#define NUM_LAYERS
Definition: map.h:76
char pname[NUM_REAL_LAYERS][64]
Definition: map.h:177
static bool obj_is_behind_wall(int dx, int dy, int sx, int sy)
Definition: map.c:1271
int16_t rotate[NUM_REAL_LAYERS]
Definition: map.h:216
void map_update_in_building(uint8_t in_building)
Definition: map.c:337
#define MAP_TILE_XOFF
Definition: map.h:46
char color[COLOR_BUF]
Definition: main.h:135
uint8_t darkness[NUM_SUB_LAYERS]
Definition: map.h:189
struct MapCell * cell
Cell that is being rendered.
Definition: map.c:956
uint8_t dark_level
Dark level.
Definition: sprite.h:47
#define NUM_SUB_LAYERS
Definition: map.h:80
int16_t zoom_y[NUM_REAL_LAYERS]
Definition: map.h:210
void region_map_reset(region_map_t *region_map)
Definition: region_map.c:110
#define LAYER_ITEM2
Definition: map.h:64
size_t tiles_num
Number of tiles.
Definition: map.c:960
#define MAP_TILE_POS_XOFF
Definition: map.h:40
void widget_map_init(widgetdata *widget)
Definition: map.c:2471
#define SPRITE_FLAG_EFFECTS
Definition: sprite.h:78
#define TEXT_OUTLINE
Definition: text.h:255
struct map_render_data map_render_data_t
int sub_layer
Sub-layer the damage is happening on.
Definition: map.h:309
bool region_has_map
Definition: map.h:128
uint8_t sub_layer
Sub-layer to render on.
Definition: map.c:965
static int right_click_ticks
Definition: map.c:83
void map_get_real_coords(int *x, int *y)
Definition: map.c:393
widgetdata * create_menu(int x, int y, widgetdata *owner)
Definition: widget.c:2428
int widget_x(const widgetdata *widget)
Definition: widget.c:615
static int widget_event(widgetdata *widget, SDL_Event *event)
Definition: map.c:2396
int16_t food
Definition: player.h:81
int tilestretcher_coords_in_tile(uint32_t stretch, int x, int y)
#define LAYER_EFFECT
Definition: map.h:70
#define NUM_REAL_LAYERS
Definition: map.h:84
int ylen
Definition: map.h:110
static bool map_show_mouse
Definition: map.c:78
struct msg_anim_struct msg_anim
Definition: main.c:80
void update_map_weather(const char *weather)
Definition: map.c:281
int w
Definition: widget.h:54
int h
Definition: widget.h:57
uint8_t probe[NUM_REAL_LAYERS]
Definition: map.h:186
void map_set_darkness(int x, int y, int sub_layer, uint8_t darkness)
Definition: map.c:813
#define SPRITE_FLAG_GRAY
Definition: sprite.h:76
uint32_t LastTick
Definition: main.c:57
void map_update_size(int w, int h)
Definition: map.c:190
int text_get_width(font_struct *font, const char *text, uint64_t flags)
Definition: text.c:2328
static int calc_map_cell_height(int x, int y, int w, int h, int sub_layer, int my_height)
Definition: map.c:451
void surface_show(SDL_Surface *surface, int x, int y, SDL_Rect *srcrect, SDL_Surface *src)
Definition: sprite.c:761
static void menu_map_walk_here(widgetdata *widget, widgetdata *menuitem, SDL_Event *event)
Definition: map.c:2207
uint32_t start_tick
The time we started this anim.
Definition: map.h:317
int map_get_player_direction(void)
Definition: map.c:370
int posy
Definition: map.h:148
int16_t faces[NUM_REAL_LAYERS]
Definition: map.h:201
#define MAP_FOW_SIZE
Definition: config.h:65
int16_t zoom_y
Vertical zoom.
Definition: sprite.h:51
void effect_sprites_play(void)
Definition: effects.c:445
static void send_move_path(int tx, int ty)
Definition: map.c:1926
static bool map_should_cull(SDL_Surface *surface, map_render_data_t *data)
Definition: map.c:1312
widgetdata * owner
Definition: widget.h:360
int dir_from_tile_coords(int tx, int ty)
Definition: move.c:89
Definition: map.h:117
static int get_top_floor_height(struct MapCell *cell, int sub_layer)
Definition: map.c:830
char region_longname[MAX_BUF]
Definition: map.h:131
static int map_width
Definition: map.c:47
void map_draw_one(int x, int y, SDL_Surface *surface)
Definition: map.c:1871
widgetevent widget_mouse_event
Definition: widget.c:82
void map_anims_clear(void)
Definition: map.c:2580
void text_show_format(SDL_Surface *surface, font_struct *font, int x, int y, const char *color_notation, uint64_t flags, SDL_Rect *box, const char *format,...)
Definition: text.c:2289
SDL_Rect * tiles
Floor tile coordinates and IDs. Used for debugging.
Definition: map.c:958
uint32_t target_object_count[NUM_REAL_LAYERS]
Definition: map.h:227
int32_t xpos
X coordinate where to render.
Definition: map.c:952
char target_color[COLOR_BUF]
Definition: player.h:135
#define TEXT_VALIGN_CENTER
Definition: text.h:246
static const char * clioptions_option_tiles_debug_desc
Definition: map.c:95
static struct MapCell * cells
Definition: map.c:43
static void widget_deinit(widgetdata *widget)
Definition: map.c:2457
int keybind_process_command(const char *cmd)
Definition: keybind.c:553
void map_draw_map(SDL_Surface *surface)
Definition: map.c:1520
void update_map_region_name(const char *region_name)
Definition: map.c:299
int widget_w(const widgetdata *widget)
Definition: widget.c:651
void map_anims_play(void)
Definition: map.c:2591
void add_menuitem(widgetdata *menu, const char *text, void(*menu_func_ptr)(widgetdata *, widgetdata *, SDL_Event *event), int menu_type, int val)
Definition: widget.c:2451
#define COLOR_DGOLD
Definition: text.h:321
static effect_struct * effects
Definition: effects.c:36
uint8_t draw_double[NUM_REAL_LAYERS]
Definition: map.h:195
static void draw_map_object(SDL_Surface *surface, map_render_data_t *data)
Definition: map.c:979
#define LAYER_WALL
Definition: map.h:66
#define COLOR_WHITE
Definition: text.h:289
int mapy
Map position Y.
Definition: map.h:312
#define HEIGHT_MAX_RENDER
Definition: map.h:289
void init_map_data(int xl, int yl, int px, int py)
Definition: map.c:410
static void widget_background(widgetdata *widget, int draw)
Definition: map.c:2449
void clear_map(bool hard)
Definition: map.c:154