Freeciv21
Develop your civilization from humble roots to a global empire
view_map_common.cpp
Go to the documentation of this file.
1 /*__ ___ ***************************************
2 / \ / \ Copyright (c) 1996-2023 Freeciv21 and Freeciv
3 \_ \ / __/ contributors. This file is part of Freeciv21.
4  _\ \ / /__ Freeciv21 is free software: you can redistribute it
5  \___ \____/ __/ and/or modify it under the terms of the GNU General
6  \_ _/ Public License as published by the Free Software
7  | @ @ \_ Foundation, either version 3 of the License,
8  | or (at your option) any later version.
9  _/ /\ You should have received a copy of the GNU
10  /o) (o/\ \_ General Public License along with Freeciv21.
11  \_____/ / If not, see https://www.gnu.org/licenses/.
12  \____/ ********************************************************/
13 
14 /*
15  * This file contains common functions used to manipulate the GUI for the
16  * the main map view. Reused across many aspects of the client as you
17  * would expect.
18  */
19 
20 #include <array>
21 
22 #include <QCoreApplication>
23 #include <QElapsedTimer>
24 #include <QEventLoop>
25 #include <QGlobalStatic>
26 #include <QHash>
27 #include <QLoggingCategory>
28 #include <QPainter>
29 #include <QPixmap>
30 #include <QSet>
31 #include <QTimer>
32 
33 // utility
34 #include "fcintl.h"
35 #include "log.h"
36 #include "rand.h"
37 #include "support.h"
38 
39 // common
40 #include "featured_text.h"
41 #include "game.h"
42 #include "map.h"
43 #include "traderoutes.h"
44 #include "unitlist.h"
45 
46 /* client/include */
47 #include "citydlg_g.h"
48 #include "mapctrl_g.h"
49 #include "mapview_g.h"
50 
51 // client
52 #include "canvas.h"
53 #include "citydlg_common.h"
54 #include "client_main.h"
55 #include "climap.h"
56 #include "control.h"
57 #include "editor.h"
58 #include "map_updates_handler.h"
59 #include "overview_common.h"
60 #include "qtg_cxxside.h"
61 #include "tileset/tilespec.h"
62 #include "views/view_map_common.h"
64 
65 Q_LOGGING_CATEGORY(graphics_category, "freeciv.graphics")
66 
67 Q_GLOBAL_STATIC(QSet<const struct tile *>, mapdeco_highlight_set)
68 Q_GLOBAL_STATIC(QSet<const struct tile *>, mapdeco_crosshair_set)
69 
71  int line_count[DIR8_MAGIC_MAX];
72  int line_danger_count[DIR8_MAGIC_MAX];
73 };
74 
75 typedef QHash<const struct tile *, struct gotoline_counter *> gotohash;
76 Q_GLOBAL_STATIC(gotohash, mapdeco_gotoline)
77 struct view mapview;
78 
79 static void base_canvas_to_map_pos(int *map_x, int *map_y, float canvas_x,
80  float canvas_y);
81 
82 // Helper struct for drawing trade routes.
84  float x, y, width, height;
85 };
86 
87 // A trade route line might need to be drawn in two parts.
88 static const int MAX_TRADE_ROUTE_DRAW_LINES = 2;
89 Q_GLOBAL_STATIC(QElapsedTimer, anim_timer);
90 
91 void anim_delay(int milliseconds)
92 {
93  QEventLoop loop;
94  QTimer t;
95  t.connect(&t, &QTimer::timeout, &loop, &QEventLoop::quit);
96  t.start(milliseconds);
97  QCoreApplication::processEvents(QEventLoop::AllEvents, 20);
98  loop.exec();
99 }
100 
104 void refresh_tile_mapcanvas(const tile *ptile, bool full_refresh)
105 {
107  qOverload<const tile *, bool>(&freeciv::map_updates_handler::update),
108  ptile, full_refresh);
109 }
110 
114 void refresh_unit_mapcanvas(struct unit *punit, struct tile *ptile,
115  bool full_refresh)
116 {
118  qOverload<const unit *, bool>(&freeciv::map_updates_handler::update),
119  punit, full_refresh);
120 }
121 
128 void refresh_city_mapcanvas(struct city *pcity, struct tile *ptile,
129  bool full_refresh)
130 {
132  qOverload<const city *, bool>(&freeciv::map_updates_handler::update),
133  pcity, full_refresh);
134 }
135 
145 void map_to_gui_vector(const struct tileset *t, float *gui_dx, float *gui_dy,
146  int map_dx, int map_dy)
147 {
148  if (tileset_is_isometric(t)) {
149  /*
150  * Convert the map coordinates to isometric GUI
151  * coordinates. We'll make tile map(0,0) be the origin, and
152  * transform like this:
153  *
154  * 3
155  * 123 2 6
156  * 456 -> becomes -> 1 5 9
157  * 789 4 8
158  * 7
159  */
160  *gui_dx = (map_dx - map_dy) * tileset_tile_width(t) / 2;
161  *gui_dy = (map_dx + map_dy) * tileset_tile_height(t) / 2;
162  } else {
163  *gui_dx = map_dx * tileset_tile_height(t);
164  *gui_dy = map_dy * tileset_tile_width(t);
165  }
166 }
167 
174 void map_to_gui_pos(const struct tileset *t, float *gui_x, float *gui_y,
175  int map_x, int map_y)
176 {
177  /* Since the GUI origin is the same as the map origin we can just do a
178  * vector conversion. */
179  map_to_gui_vector(t, gui_x, gui_y, map_x, map_y);
180 }
181 
189 static void gui_to_map_pos(const struct tileset *t, int *map_x, int *map_y,
190  float gui_x, float gui_y)
191 {
192  const float W = tileset_tile_width(t), H = tileset_tile_height(t);
193  const float HH = tileset_hex_height(t), HW = tileset_hex_width(t);
194 
195  if (HH > 0 || HW > 0) {
196  /* To handle hexagonal cases we have to revert to a less elegant method
197  * of calculation. */
198  float x, y;
199  int dx, dy;
200  int xmult, ymult, mod, compar;
201 
203 
204  x = DIVIDE((int) gui_x, (int) W);
205  y = DIVIDE((int) gui_y, (int) H);
206  dx = gui_x - x * W;
207  dy = gui_y - y * H;
208  fc_assert(dx >= 0 && dx < W);
209  fc_assert(dy >= 0 && dy < H);
210 
211  // Now fold so we consider only one-quarter tile.
212  xmult = (dx >= W / 2) ? -1 : 1;
213  ymult = (dy >= H / 2) ? -1 : 1;
214  dx = (dx >= W / 2) ? (W - 1 - dx) : dx;
215  dy = (dy >= H / 2) ? (H - 1 - dy) : dy;
216 
217  // Next compare to see if we're across onto the next tile.
218  if (HW > 0) {
219  compar = (dx - HW / 2) * (H / 2) - (H / 2 - 1 - dy) * (W / 2 - HW);
220  } else {
221  compar = (dy - HH / 2) * (W / 2) - (W / 2 - 1 - dx) * (H / 2 - HH);
222  }
223  mod = (compar < 0) ? -1 : 0;
224 
225  *map_x = (x + y) + mod * (xmult + ymult) / 2;
226  *map_y = (y - x) + mod * (ymult - xmult) / 2;
227  } else if (tileset_is_isometric(t)) {
228  /* The basic operation here is a simple pi/4 rotation; however, we
229  * have to first scale because the tiles have different width and
230  * height. Mathematically, this looks like
231  * | 1/W 1/H | |x| |x`|
232  * | | | | -> | |
233  * |-1/W 1/H | |y| |y`|
234  *
235  * Where W is the tile width and H the height.
236  *
237  * In simple terms, this is
238  * map_x = [ x / W + y / H ]
239  * map_y = [ - x / W + y / H ]
240  * where [q] stands for integer part of q.
241  *
242  * Here the division is proper mathematical floating point division.
243  *
244  * A picture demonstrating this can be seen at
245  * http://bugs.freeciv.org/Ticket/Attachment/16782/9982/grid1.png.
246  *
247  * We have to subtract off a half-tile in the X direction before doing
248  * the transformation. This is because, although the origin of the tile
249  * is the top-left corner of the bounding box, after the transformation
250  * the top corner of the diamond-shaped tile moves into this position.
251  *
252  * The calculation is complicated somewhat because of two things: we
253  * only use integer math, and C integer division rounds toward zero
254  * instead of rounding down.
255  */
256  gui_x -= W / 2;
257  *map_x = DIVIDE((int) (gui_x * H + gui_y * W), (int) (W * H));
258  *map_y = DIVIDE((int) (gui_y * W - gui_x * H), (int) (W * H));
259  } else { // tileset_is_isometric(t)
260  /* We use DIVIDE so that we will get the correct result even
261  * for negative coordinates. */
262  *map_x = DIVIDE((int) gui_x, (int) W);
263  *map_y = DIVIDE((int) gui_y, (int) H);
264  }
265 }
266 
289 bool tile_to_canvas_pos(float *canvas_x, float *canvas_y, const tile *ptile)
290 {
291  int center_map_x, center_map_y, dx, dy, tile_x, tile_y;
292 
293  /*
294  * First we wrap the coordinates to hopefully be within the mapview
295  * window. We do this by finding the position closest to the center
296  * of the window.
297  */
298  // TODO: Cache the value of this position
299  base_canvas_to_map_pos(&center_map_x, &center_map_y, mapview.width / 2,
300  mapview.height / 2);
301  index_to_map_pos(&tile_x, &tile_y, tile_index(ptile));
302  base_map_distance_vector(&dx, &dy, center_map_x, center_map_y, tile_x,
303  tile_y);
304 
305  map_to_gui_pos(tileset, canvas_x, canvas_y, center_map_x + dx,
306  center_map_y + dy);
307  *canvas_x -= mapview.gui_x0;
308  *canvas_y -= mapview.gui_y0;
309 
310  /*
311  * Finally we clip.
312  *
313  * This check is tailored to work for both iso-view and classic view. Note
314  * that (canvas_x, canvas_y) need not be aligned to a tile boundary, and
315  * that the position is at the top-left of the NORMAL (not UNIT) tile.
316  * This checks to see if _any part_ of the tile is present on the backing
317  * store. Even if it's not visible on the canvas, if it's present on the
318  * backing store we need to draw it in case the canvas is resized.
319  */
320  return (*canvas_x > -tileset_tile_width(tileset)
321  && *canvas_x<mapview.store_width
322  && * canvas_y> - tileset_tile_height(tileset)
323  && *canvas_y < (mapview.store_height
326 }
327 
332 static void base_canvas_to_map_pos(int *map_x, int *map_y, float canvas_x,
333  float canvas_y)
334 {
335  gui_to_map_pos(tileset, map_x, map_y, canvas_x + mapview.gui_x0,
336  canvas_y + mapview.gui_y0);
337 }
338 
343 struct tile *canvas_pos_to_tile(float canvas_x, float canvas_y)
344 {
345  int map_x, map_y;
346 
347  base_canvas_to_map_pos(&map_x, &map_y, canvas_x, canvas_y);
348  if (normalize_map_pos(&(wld.map), &map_x, &map_y)) {
349  return map_pos_to_tile(&(wld.map), map_x, map_y);
350  } else {
351  return nullptr;
352  }
353 }
354 
360 struct tile *canvas_pos_to_nearest_tile(float canvas_x, float canvas_y)
361 {
362  int map_x, map_y;
363 
364  base_canvas_to_map_pos(&map_x, &map_y, canvas_x, canvas_y);
365  return nearest_real_tile(&(wld.map), map_x, map_y);
366 }
367 
372 static void normalize_gui_pos(const struct tileset *t, float *gui_x,
373  float *gui_y)
374 {
375  int map_x, map_y, nat_x, nat_y, diff_x, diff_y;
376  float gui_x0, gui_y0;
377 
378  /* Convert the (gui_x, gui_y) into a (map_x, map_y) plus a GUI offset
379  * from this tile. */
380  gui_to_map_pos(t, &map_x, &map_y, *gui_x, *gui_y);
381  map_to_gui_pos(t, &gui_x0, &gui_y0, map_x, map_y);
382  diff_x = *gui_x - gui_x0;
383  diff_y = *gui_y - gui_y0;
384 
385  /* Perform wrapping without any realness check. It's important that
386  * we wrap even if the map position is unreal, which normalize_map_pos
387  * doesn't necessarily do. */
388  MAP_TO_NATIVE_POS(&nat_x, &nat_y, map_x, map_y);
389  if (current_topo_has_flag(TF_WRAPX)) {
391  }
392  if (current_topo_has_flag(TF_WRAPY)) {
394  }
395  NATIVE_TO_MAP_POS(&map_x, &map_y, nat_x, nat_y);
396 
397  /* Now convert the wrapped map position back to a GUI position and add the
398  * offset back on. */
399  map_to_gui_pos(t, gui_x, gui_y, map_x, map_y);
400  *gui_x += diff_x;
401  *gui_y += diff_y;
402 }
403 
408 void gui_distance_vector(const struct tileset *t, float *gui_dx,
409  float *gui_dy, float gui_x0, float gui_y0,
410  float gui_x1, float gui_y1)
411 {
412  int map_x0, map_y0, map_x1, map_y1;
413  float gui_x0_base, gui_y0_base, gui_x1_base, gui_y1_base;
414  int gui_x0_diff, gui_y0_diff, gui_x1_diff, gui_y1_diff;
415  int map_dx, map_dy;
416 
417  // Make sure positions are canonical. Yes, this is the only way.
418  normalize_gui_pos(t, &gui_x0, &gui_y0);
419  normalize_gui_pos(t, &gui_x1, &gui_y1);
420 
421  /* Now we have to find the offset of each GUI position from its tile
422  * origin. This is complicated: it means converting to a map position and
423  * then back to the GUI position to find the tile origin, then subtracting
424  * to get the offset. */
425  gui_to_map_pos(t, &map_x0, &map_y0, gui_x0, gui_y0);
426  gui_to_map_pos(t, &map_x1, &map_y1, gui_x1, gui_y1);
427 
428  map_to_gui_pos(t, &gui_x0_base, &gui_y0_base, map_x0, map_y0);
429  map_to_gui_pos(t, &gui_x1_base, &gui_y1_base, map_x1, map_y1);
430 
431  gui_x0_diff = gui_x0 - gui_x0_base;
432  gui_y0_diff = gui_y0 - gui_y0_base;
433  gui_x1_diff = gui_x1 - gui_x1_base;
434  gui_y1_diff = gui_y1 - gui_y1_base;
435 
436  /* Next we find the map distance vector and convert this into a GUI
437  * vector. */
438  base_map_distance_vector(&map_dx, &map_dy, map_x0, map_y0, map_x1, map_y1);
439  map_to_gui_pos(t, gui_dx, gui_dy, map_dx, map_dy);
440 
441  /* Finally we add on the difference in offsets to retain pixel
442  * resolution. */
443  *gui_dx += gui_x1_diff - gui_x0_diff;
444  *gui_dy += gui_y1_diff - gui_y0_diff;
445 }
446 
451 static void base_set_mapview_origin(float gui_x0, float gui_y0)
452 {
453  float old_gui_x0, old_gui_y0;
454  float dx, dy;
455  const int width = mapview.width, height = mapview.height;
456  int common_x0, common_x1, common_y0, common_y1;
457  int update_x0, update_x1, update_y0, update_y1;
458 
459  /* Then update everything. This does some tricky math to avoid having
460  * to do unnecessary redraws in update_map_canvas. This makes for ugly
461  * code but speeds up the mapview by a large factor. */
462 
463  /* We need to calculate the vector of movement of the mapview. So
464  * we find the GUI distance vector and then use this to calculate
465  * the original mapview origin relative to the current position. Thus
466  * if we move one tile to the left, even if this causes GUI positions
467  * to wrap the distance vector is only one tile. */
468  normalize_gui_pos(tileset, &gui_x0, &gui_y0);
470  gui_x0, gui_y0);
471  old_gui_x0 = gui_x0 - dx;
472  old_gui_y0 = gui_y0 - dy;
473 
474  mapview.gui_x0 = gui_x0;
475  mapview.gui_y0 = gui_y0;
476 
477  /* Find the overlapping area of the new and old mapview. This is
478  * done in GUI coordinates. Note that if the GUI coordinates wrap
479  * no overlap will be found. */
480  common_x0 = MAX(old_gui_x0, gui_x0);
481  common_x1 = MIN(old_gui_x0, gui_x0) + width;
482  common_y0 = MAX(old_gui_y0, gui_y0);
483  common_y1 = MIN(old_gui_y0, gui_y0) + height;
484 
485  if (mapview.can_do_cached_drawing && common_x1 > common_x0
486  && common_y1 > common_y0) {
487  /* Do a partial redraw only. This means the area of overlap (a
488  * rectangle) is copied. Then the remaining areas (two rectangles)
489  * are updated through update_map_canvas. */
490  if (old_gui_x0 < gui_x0) {
491  update_x0 = MAX(old_gui_x0 + width, gui_x0);
492  update_x1 = gui_x0 + width;
493  } else {
494  update_x0 = gui_x0;
495  update_x1 = MIN(old_gui_x0, gui_x0 + width);
496  }
497  if (old_gui_y0 < gui_y0) {
498  update_y0 = MAX(old_gui_y0 + height, gui_y0);
499  update_y1 = gui_y0 + height;
500  } else {
501  update_y0 = gui_y0;
502  update_y1 = MIN(old_gui_y0, gui_y0 + height);
503  }
504 
505  dirty_all();
506  QPainter p(mapview.tmp_store);
507  p.drawPixmap(common_x0 - gui_x0, common_y0 - gui_y0, *mapview.store,
508  common_x0 - old_gui_x0, common_y0 - old_gui_y0,
509  common_x1 - common_x0, common_y1 - common_y0);
510  p.end();
512 
513  if (update_y1 > update_y0) {
514  update_map_canvas(0, update_y0 - gui_y0, width, update_y1 - update_y0);
515  }
516  if (update_x1 > update_x0) {
517  update_map_canvas(update_x0 - gui_x0, common_y0 - gui_y0,
518  update_x1 - update_x0, common_y1 - common_y0);
519  }
520  } else {
521  dirty_all();
523  }
524 
525  update_minimap();
526  switch (hover_state) {
527  case HOVER_GOTO:
528  case HOVER_PATROL:
529  case HOVER_CONNECT:
531  case HOVER_GOTO_SEL_TGT:
532  case HOVER_NONE:
533  case HOVER_PARADROP:
534  case HOVER_ACT_SEL_TGT:
535  case HOVER_DEBUG_TILE:
536  break;
537  };
538  if (rectangle_active) {
540  }
541 }
542 
547 static bool calc_mapview_origin(float *gui_x0, float *gui_y0)
548 {
549  float xmin, ymin, xmax, ymax;
550  int xsize, ysize;
551 
552  // Normalize (wrap) the mapview origin.
553  normalize_gui_pos(tileset, gui_x0, gui_y0);
554 
555  /* First wrap/clip the position. Wrapping is done in native positions
556  * while clipping is done in scroll (native) positions. */
557  get_mapview_scroll_window(&xmin, &ymin, &xmax, &ymax, &xsize, &ysize);
558 
559  if (!current_topo_has_flag(TF_WRAPX)) {
560  *gui_x0 = CLIP(xmin, *gui_x0, xmax - xsize);
561  }
562 
563  if (!current_topo_has_flag(TF_WRAPY)) {
564  *gui_y0 = CLIP(ymin, *gui_y0, ymax - ysize);
565  }
566 
567  return !(mapview.gui_x0 == *gui_x0 && mapview.gui_y0 == *gui_y0);
568 }
569 
573 void set_mapview_origin(float gui_x0, float gui_y0)
574 {
575  if (!calc_mapview_origin(&gui_x0, &gui_y0)) {
576  return;
577  }
578 
579  base_set_mapview_origin(gui_x0, gui_y0);
580 }
581 
608 void get_mapview_scroll_window(float *xmin, float *ymin, float *xmax,
609  float *ymax, int *xsize, int *ysize)
610 {
611  int diff;
612 
613  *xsize = mapview.width;
614  *ysize = mapview.height;
615 
617  // If the map and view line up, it's easy.
618  NATIVE_TO_MAP_POS(xmin, ymin, 0, 0);
619  map_to_gui_pos(tileset, xmin, ymin, *xmin, *ymin);
620 
621  NATIVE_TO_MAP_POS(xmax, ymax, wld.map.xsize - 1, wld.map.ysize - 1);
622  map_to_gui_pos(tileset, xmax, ymax, *xmax, *ymax);
623  *xmax += tileset_tile_width(tileset);
624  *ymax += tileset_tile_height(tileset);
625 
626  /* To be able to center on positions near the edges, we have to be
627  * allowed to scroll all the way to those edges. To allow wrapping the
628  * clipping boundary needs to extend past the edge - a half-tile in
629  * iso-view or a full tile in non-iso view. The above math already has
630  * taken care of some of this so all that's left is to fix the corner
631  * cases. */
632  if (current_topo_has_flag(TF_WRAPX)) {
633  *xmax += *xsize;
634 
635  // We need to be able to scroll a little further to the left.
636  *xmin -= tileset_tile_width(tileset);
637  }
638  if (current_topo_has_flag(TF_WRAPY)) {
639  *ymax += *ysize;
640 
641  // We need to be able to scroll a little further up.
642  *ymin -= tileset_tile_height(tileset);
643  }
644  } else {
645  /* Otherwise it's hard. Very hard. Impossible, in fact. This is just
646  * an approximation - a huge bounding box. */
647  float gui_x1, gui_y1, gui_x2, gui_y2, gui_x3, gui_y3, gui_x4, gui_y4;
648  int map_x, map_y;
649 
650  NATIVE_TO_MAP_POS(&map_x, &map_y, 0, 0);
651  map_to_gui_pos(tileset, &gui_x1, &gui_y1, map_x, map_y);
652 
653  NATIVE_TO_MAP_POS(&map_x, &map_y, wld.map.xsize - 1, 0);
654  map_to_gui_pos(tileset, &gui_x2, &gui_y2, map_x, map_y);
655 
656  NATIVE_TO_MAP_POS(&map_x, &map_y, 0, wld.map.ysize - 1);
657  map_to_gui_pos(tileset, &gui_x3, &gui_y3, map_x, map_y);
658 
659  NATIVE_TO_MAP_POS(&map_x, &map_y, wld.map.xsize - 1, wld.map.ysize - 1);
660  map_to_gui_pos(tileset, &gui_x4, &gui_y4, map_x, map_y);
661 
662  *xmin = MIN(gui_x1, MIN(gui_x2, gui_x3)) - mapview.width / 2;
663  *ymin = MIN(gui_y1, MIN(gui_y2, gui_y3)) - mapview.height / 2;
664 
665  *xmax = MAX(gui_x4, MAX(gui_x2, gui_x3)) + mapview.width / 2;
666  *ymax = MAX(gui_y4, MAX(gui_y2, gui_y3)) + mapview.height / 2;
667  }
668 
669  /* Make sure the scroll window is big enough to hold the mapview. If
670  * not scrolling will be very ugly and the GUI may become confused. */
671  diff = *xsize - (*xmax - *xmin);
672  if (diff > 0) {
673  *xmin -= diff / 2;
674  *xmax += (diff + 1) / 2;
675  }
676 
677  diff = *ysize - (*ymax - *ymin);
678  if (diff > 0) {
679  *ymin -= diff / 2;
680  *ymax += (diff + 1) / 2;
681  }
682 
683  log_debug("x: %f<-%d->%f; y: %f<-%f->%d", *xmin, *xsize, *xmax, *ymin,
684  *ymax, *ysize);
685 }
686 
690 void get_mapview_scroll_pos(int *scroll_x, int *scroll_y)
691 {
692  *scroll_x = mapview.gui_x0;
693  *scroll_y = mapview.gui_y0;
694 }
695 
700 {
702 }
703 
708 bool tile_visible_mapcanvas(struct tile *ptile)
709 {
710  float dummy_x, dummy_y; // well, it needs two pointers...
711 
712  return tile_to_canvas_pos(&dummy_x, &dummy_y, ptile);
713 }
714 
727 {
728  float canvas_x, canvas_y;
729  float xmin, ymin, xmax, ymax;
730  int xsize, ysize, scroll_x, scroll_y;
731  const int border_x =
733  : 2 * tileset_tile_width(tileset));
734  const int border_y =
738 
739  get_mapview_scroll_window(&xmin, &ymin, &xmax, &ymax, &xsize, &ysize);
740  get_mapview_scroll_pos(&scroll_x, &scroll_y);
741 
742  if (!tile_to_canvas_pos(&canvas_x, &canvas_y, ptile)) {
743  // The tile isn't visible at all.
744  return false;
745  }
746 
747  /* For each direction: if the tile is too close to the mapview border
748  * in that direction, and scrolling can get us any closer to the
749  * border, then it's a border tile. We can only really check the
750  * scrolling when the mapview window lines up with the map. */
751  if (canvas_x < border_x
752  && (!same || scroll_x > xmin || current_topo_has_flag(TF_WRAPX))) {
753  return false;
754  }
755  if (canvas_y < border_y
756  && (!same || scroll_y > ymin || current_topo_has_flag(TF_WRAPY))) {
757  return false;
758  }
759  if (canvas_x + tileset_tile_width(tileset) > mapview.width - border_x
760  && (!same || scroll_x + xsize < xmax
761  || current_topo_has_flag(TF_WRAPX))) {
762  return false;
763  }
764  if (canvas_y + tileset_tile_height(tileset) > mapview.height - border_y
765  && (!same || scroll_y + ysize < ymax
766  || current_topo_has_flag(TF_WRAPY))) {
767  return false;
768  }
769 
770  return true;
771 }
772 
776 void put_drawn_sprites(QPixmap *pcanvas, const QPoint &canvas_loc,
777  const std::vector<drawn_sprite> &sprites, bool fog,
778  bool city_unit)
779 {
780  QPainter p(pcanvas);
781  for (auto s : sprites) {
782  if (!s.sprite) {
783  // This can happen, although it should probably be avoided.
784  continue;
785  }
786  if (fog && s.foggable) {
787  // FIXME This looks rather expensive
788  QPixmap temp(s.sprite->size());
789  temp.fill(Qt::transparent);
790 
791  QPainter p2(&temp);
792  p2.setCompositionMode(QPainter::CompositionMode_Source);
793  p2.drawPixmap(0, 0, *s.sprite);
794  p2.setCompositionMode(QPainter::CompositionMode_SourceAtop);
795  p2.fillRect(temp.rect(), QColor(0, 0, 0, 110));
796  p2.end();
797 
798  p.drawPixmap(canvas_loc + s.offset, temp);
799  } else {
800  /* We avoid calling canvas_put_sprite_fogged, even though it
801  * should be a valid thing to do, because gui-gtk-2.0 didn't have
802  * a full implementation. */
803  p.setCompositionMode(QPainter::CompositionMode_SourceOver);
804  p.setOpacity(1);
805  p.drawPixmap(canvas_loc + s.offset, *s.sprite);
806  }
807  }
808  p.end();
809 }
810 
815 void put_one_element(QPixmap *pcanvas,
816  const std::unique_ptr<freeciv::layer> &layer,
817  const struct tile *ptile, const struct tile_edge *pedge,
818  const struct tile_corner *pcorner,
819  const struct unit *punit, const QPoint &canvas_loc)
820 {
821  bool city_unit = false;
822  int dummy_x, dummy_y;
823  auto sprites = layer->fill_sprite_array(ptile, pedge, pcorner, punit);
824  bool fog = (ptile && gui_options->draw_fog_of_war
826  if (punit) {
827  struct city *xcity = is_any_city_dialog_open();
828  if (xcity) {
829  if (city_base_to_city_map(&dummy_x, &dummy_y, xcity, punit->tile)) {
830  city_unit = true;
831  }
832  }
833  }
834  /*** Draw terrain and specials ***/
835  put_drawn_sprites(pcanvas, canvas_loc, sprites, fog, city_unit);
836 }
837 
842 void put_unit(const struct unit *punit, QPixmap *pcanvas,
843  const QPoint &canvas_loc)
844 {
845  auto loc = canvas_loc;
847  for (const auto &layer : tileset_get_layers(tileset)) {
848  put_one_element(pcanvas, layer, nullptr, nullptr, nullptr, punit, loc);
849  }
850 }
851 
858 void put_terrain(struct tile *ptile, QPixmap *pcanvas,
859  const QPoint &canvas_loc)
860 {
861  // Use full tile height, even for terrains.
862  auto loc = canvas_loc;
864  for (const auto &layer : tileset_get_layers(tileset)) {
865  put_one_element(pcanvas, layer, ptile, nullptr, nullptr, nullptr, loc);
866  }
867 }
868 
876 void put_unit_city_overlays(const unit *punit, QPixmap *pcanvas,
877  int canvas_x, int canvas_y,
878  const int *upkeep_cost, int happy_cost)
879 {
880  QPainter p(pcanvas);
881 
882  auto sprite = get_unit_unhappy_sprite(tileset, punit, happy_cost);
883  if (sprite) {
884  p.drawPixmap(canvas_x, canvas_y, *sprite);
885  }
886 
888  {
889  sprite = get_unit_upkeep_sprite(tileset, static_cast<Output_type_id>(o),
890  punit, upkeep_cost);
891  if (sprite) {
892  p.drawPixmap(canvas_x, canvas_y, *sprite);
893  }
894  }
896 
897  p.end();
898 }
899 
900 /*
901  * pcity->client.color_index is an index into the city_colors array.
902  * When toggle_city_color is called the city's coloration is toggled. When
903  * a city is newly colored its color is taken from color_index and
904  * color_index is moved forward one position. Each color in the array
905  * tells what color the citymap will be drawn on the mapview.
906  *
907  * This array can be added to without breaking anything elsewhere.
908  * color_index can grow without limit and even wrap around, the drawing code
909  * will take care of it.
910  */
911 static int color_index = 0;
912 
918 void toggle_city_color(struct city *pcity)
919 {
920  if (pcity->client.colored) {
921  pcity->client.colored = false;
922  } else {
923  pcity->client.colored = true;
924  pcity->client.color_index = color_index;
925  color_index++;
926  }
927 
928  refresh_city_mapcanvas(pcity, pcity->tile, true);
929 }
930 
936 void toggle_unit_color(struct unit *punit)
937 {
938  if (punit->client.colored) {
939  punit->client.colored = false;
940  } else {
941  punit->client.colored = true;
942  punit->client.color_index = color_index;
943  color_index++;
944  }
945 
946  refresh_unit_mapcanvas(punit, unit_tile(punit), true);
947 }
948 
952 void put_nuke_mushroom_pixmaps(struct tile *ptile)
953 {
954  float canvas_x, canvas_y;
955  auto mysprite = get_nuke_explode_sprite(tileset);
956 
957  /* We can't count on the return value of tile_to_canvas_pos since the
958  * sprite may span multiple tiles. */
959  (void) tile_to_canvas_pos(&canvas_x, &canvas_y, ptile);
960 
961  canvas_x += (tileset_tile_width(tileset) - mysprite->width()) / 2;
962  canvas_y += (tileset_tile_height(tileset) - mysprite->height()) / 2;
963 
964  /* Make sure everything is flushed and synced before proceeding. First
965  * we update everything to the store, but don't write this to screen.
966  * Then add the nuke graphic to the store. Finally flush everything to
967  * the screen and wait 1 second. */
969 
970  QPainter p(mapview.store);
971  p.drawPixmap(canvas_x, canvas_y, *mysprite);
972  p.end();
973  dirty_rect(canvas_x, canvas_y, mysprite->width(), mysprite->height());
974 
975  flush_dirty();
976 
977  fc_usleep(1000000);
978 
980 }
981 
985 static void put_one_tile(QPixmap *pcanvas,
986  const std::unique_ptr<freeciv::layer> &layer,
987  const tile *ptile, const QPoint &canvas_loc)
988 {
989  if (client_tile_get_known(ptile) != TILE_UNKNOWN
990  || (editor_is_active() && editor_tile_is_selected(ptile))) {
991  struct unit *punit = get_drawable_unit(tileset, ptile);
992 
993  put_one_element(pcanvas, layer, ptile, nullptr, nullptr, punit,
994  canvas_loc);
995  }
996 }
997 
1009 static int trade_route_to_canvas_lines(const struct tile *ptile1,
1010  const struct tile *ptile2,
1011  struct trade_route_line *lines)
1012 {
1013  int dx, dy;
1014 
1015  if (!ptile1 || !ptile2 || !lines) {
1016  return 0;
1017  }
1018 
1019  base_map_distance_vector(&dx, &dy, TILE_XY(ptile1), TILE_XY(ptile2));
1020  map_to_gui_pos(tileset, &lines[0].width, &lines[0].height, dx, dy);
1021 
1022  tile_to_canvas_pos(&lines[0].x, &lines[0].y, ptile1);
1023  tile_to_canvas_pos(&lines[1].x, &lines[1].y, ptile2);
1024 
1025  if (lines[1].x - lines[0].x == lines[0].width
1026  && lines[1].y - lines[0].y == lines[0].height) {
1027  return 1;
1028  }
1029 
1030  lines[1].width = -lines[0].width;
1031  lines[1].height = -lines[0].height;
1032  return 2;
1033 }
1034 
1038 static void draw_trade_route_line(const struct tile *ptile1,
1039  const struct tile *ptile2,
1040  enum color_std color)
1041 {
1043 
1044  if (!ptile1 || !ptile2) {
1045  return;
1046  }
1047 
1048  /* Order the source and destination tiles consistently
1049  * so that if a line is drawn twice it does not produce
1050  * ugly effects due to dashes not lining up. */
1051  if (tile_index(ptile2) > tile_index(ptile1)) {
1052  std::swap(ptile1, ptile2);
1053  }
1054 
1055  QPen pen;
1056  pen.setColor(get_color(tileset, color));
1057  pen.setStyle(Qt::DashLine);
1058  pen.setDashOffset(4);
1059  pen.setWidth(1);
1060 
1061  QPainter p(mapview.store);
1062  p.setPen(pen);
1063  auto line_count = trade_route_to_canvas_lines(ptile1, ptile2, lines);
1064  for (int i = 0; i < line_count; i++) {
1065  int x = lines[i].x + tileset_tile_width(tileset) / 2;
1066  int y = lines[i].y + tileset_tile_height(tileset) / 2;
1067  p.drawLine(x, y, x + lines[i].width, y + lines[i].height);
1068  }
1069  p.end();
1070 }
1071 
1075 static void draw_trade_routes_for_city(const struct city *pcity_src)
1076 {
1077  if (!pcity_src) {
1078  return;
1079  }
1080 
1081  trade_partners_iterate(pcity_src, pcity_dest)
1082  {
1083  draw_trade_route_line(city_tile(pcity_src), city_tile(pcity_dest),
1084  COLOR_MAPVIEW_TRADE_ROUTE_LINE);
1085  }
1087 }
1088 
1092 static void draw_trade_routes()
1093 {
1095  return;
1096  }
1097 
1098  if (client_is_global_observer()) {
1099  cities_iterate(pcity) { draw_trade_routes_for_city(pcity); }
1101  } else {
1102  struct player *pplayer = client_player();
1103  if (!pplayer) {
1104  return;
1105  }
1106  city_list_iterate(pplayer->cities, pcity)
1107  {
1109  }
1111  }
1112 }
1113 
1129 void update_map_canvas(int canvas_x, int canvas_y, int width, int height)
1130 {
1131  int gui_x0, gui_y0;
1132  bool full;
1133 
1134  canvas_x = MAX(canvas_x, 0);
1135  canvas_y = MAX(canvas_y, 0);
1136  width = MIN(mapview.store_width - canvas_x, width);
1137  height = MIN(mapview.store_height - canvas_y, height);
1138 
1139  gui_x0 = mapview.gui_x0 + canvas_x;
1140  gui_y0 = mapview.gui_y0 + canvas_y;
1141  full = (canvas_x == 0 && canvas_y == 0 && width == mapview.store_width
1142  && height == mapview.store_height);
1143 
1144  log_debug("update_map_canvas(pos=(%d,%d), size=(%d,%d))", canvas_x,
1145  canvas_y, width, height);
1146 
1147  /* If a full redraw is done, we just draw everything onto the canvas.
1148  * However if a partial redraw is done we draw everything onto the
1149  * tmp_canvas then copy *just* the area of update onto the canvas. */
1150  if (!full) {
1151  // Swap store and tmp_store.
1153  }
1154 
1155  /* Clear the area. This is necessary since some parts of the rectangle
1156  * may not actually have any tiles drawn on them. This will happen when
1157  * the mapview is large enough so that the tile is visible in multiple
1158  * locations. In this case it will only be drawn in one place.
1159  *
1160  * Of course it's necessary to draw to the whole area to cover up any old
1161  * drawing that was done there. */
1162  QPainter p(mapview.store);
1163  p.fillRect(canvas_x, canvas_y, width, height,
1164  get_color(tileset, COLOR_MAPVIEW_UNKNOWN));
1165  p.end();
1166 
1167  const auto rect = QRect(gui_x0, gui_y0, width,
1168  height
1170  ? (tileset_tile_height(tileset) / 2)
1171  : 0));
1172  for (const auto &layer : tileset_get_layers(tileset)) {
1173  if (layer->type() == LAYER_TILELABEL) {
1174  show_tile_labels(canvas_x, canvas_y, width, height);
1175  }
1176  if (layer->type() == LAYER_CITYBAR) {
1177  show_city_descriptions(canvas_x, canvas_y, width, height);
1178  continue;
1179  }
1180  for (auto it = freeciv::gui_rect_iterator(tileset, rect); it.next();) {
1181  const auto loc =
1182  QPoint(it.x() - mapview.gui_x0, it.y() - mapview.gui_y0);
1183 
1184  if (it.has_corner()) {
1185  put_one_element(mapview.store, layer, nullptr, nullptr, &it.corner(),
1186  nullptr, loc);
1187  }
1188  if (it.has_edge()) {
1189  put_one_element(mapview.store, layer, nullptr, &it.edge(), nullptr,
1190  nullptr, loc);
1191  }
1192  if (it.has_tile()) {
1193  put_one_tile(mapview.store, layer, it.tile(), loc);
1194  }
1195  }
1196  }
1197 
1200 
1201  /* Draw the goto lines on top of the whole thing. This is done last as
1202  * we want it completely on top.
1203  *
1204  * Note that a pixel right on the border of a tile may actually contain a
1205  * goto line from an adjacent tile. Thus we draw any extra goto lines
1206  * from adjacent tiles (if they're close enough). */
1207  const auto goto_rect =
1208  QRect(gui_x0 - GOTO_WIDTH, gui_y0 - GOTO_WIDTH, width + 2 * GOTO_WIDTH,
1209  height + 2 * GOTO_WIDTH);
1210  for (auto it = freeciv::gui_rect_iterator(tileset, goto_rect);
1211  it.next();) {
1212  if (!it.has_tile()) {
1213  continue;
1214  }
1215  adjc_dir_base_iterate(&(wld.map), it.tile(), dir)
1216  {
1217  bool safe;
1218  if (mapdeco_is_gotoline_set(it.tile(), dir, &safe)) {
1219  draw_segment(it.tile(), dir, safe);
1220  }
1221  }
1223  }
1224 
1225  if (!full) {
1226  // Swap store and tmp_store back.
1228 
1229  // And copy store to tmp_store.
1230  QPainter p(mapview.store);
1231  p.drawPixmap(canvas_x, canvas_y, *mapview.tmp_store, canvas_x, canvas_y,
1232  width, height);
1233  p.end();
1234  }
1235 
1236  dirty_rect(canvas_x, canvas_y, width, height);
1237 }
1238 
1244 {
1245  // Old comment preserved for posterity
1246  /*
1247  This function, along with unqueue_mapview_update(), helps in updating
1248  the mapview when a packet is received. Previously, we just called
1249  update_map_canvas when (for instance) a city update was received.
1250  Not only would this often end up with a lot of duplicated work, but it
1251  would also draw over the city descriptions, which would then just
1252  "disappear" from the mapview. The hack is to instead call
1253  queue_mapview_update in place of this update, and later (after all
1254  packets have been read) call unqueue_mapview_update. The functions
1255  don't track which areas of the screen need updating, rather when the
1256  unqueue is done we just update the whole visible mapqueue, and redraw
1257  the city descriptions.
1258 
1259  Using these functions, updates are done correctly, and are probably
1260  faster too. But it's a bit of a hack to insert this code into the
1261  packet-handling code.
1262  */
1263  if (can_client_change_view()) {
1266  }
1267 }
1268 
1269 /* The maximum city description width and height. This gives the dimensions
1270  * of a rectangle centered directly beneath the tile a city is on, that
1271  * contains the city description.
1272  *
1273  * These values are increased when drawing is done. This may mean that
1274  * the change (from increasing the value) won't take place until the
1275  * next redraw. */
1276 static int max_desc_width = 0, max_desc_height = 0;
1277 
1278 // Same for tile labels
1279 static QSize max_label_size = QSize();
1280 
1284 void update_city_description(struct city *pcity)
1285 {
1288 }
1289 
1293 void update_tile_label(const tile *ptile)
1294 {
1297 }
1298 
1309 static QSize show_tile_label(QPixmap *pcanvas, int canvas_x, int canvas_y,
1310  const tile *ptile)
1311 {
1312  const enum client_font FONT_TILE_LABEL = FONT_CITY_NAME; // TODO: new font
1313 #define COLOR_MAPVIEW_TILELABEL COLOR_MAPVIEW_CITYTEXT
1314 
1315  canvas_x += tileset_tile_width(tileset) / 2;
1316  canvas_y += tileset_tilelabel_offset_y(tileset);
1317 
1318  QPainter p(pcanvas);
1319  p.setFont(get_font(FONT_TILE_LABEL));
1321 
1322  auto fm = p.fontMetrics();
1323  auto rect = fm.boundingRect(ptile->label);
1324 
1325  p.drawText((canvas_x - rect.width() / 2), canvas_y + fm.ascent(),
1326  ptile->label);
1327 
1328  return rect.size();
1329 #undef COLOR_MAPVIEW_TILELABEL
1330 }
1331 
1335 void show_city_descriptions(int canvas_base_x, int canvas_base_y,
1336  int width_base, int height_base)
1337 {
1338  const int dx = max_desc_width - tileset_tile_width(tileset);
1339  const int dy = max_desc_height;
1340  const int offset_y = tileset_citybar_offset_y(tileset);
1341  int new_max_width = max_desc_width, new_max_height = max_desc_height;
1342 
1343  /* A city description is shown below the city. It has a specified
1344  * maximum width and height (although these are only estimates). Thus
1345  * we need to update some tiles above the mapview and some to the left
1346  * and right.
1347  *
1348  * /--W1--\ (W1 = tileset_tile_width(tileset))
1349  * -------- \
1350  * | CITY | H1 (H1 = tileset_tile_height(tileset))
1351  * | | /
1352  * ------------------ \
1353  * | DESCRIPTION | H2 (H2 = MAX_CITY_DESC_HEIGHT)
1354  * | | /
1355  * ------------------
1356  * \-------W2-------/ (W2 = MAX_CITY_DESC_WIDTH)
1357  *
1358  * We must draw H2 extra pixels above and (W2 - W1) / 2 extra pixels
1359  * to each side of the mapview.
1360  */
1361  const auto rect = QRect(mapview.gui_x0 + canvas_base_x - dx / 2,
1362  mapview.gui_y0 + canvas_base_y - dy,
1363  width_base + dx, height_base + dy - offset_y);
1364  for (auto it = freeciv::gui_rect_iterator(tileset, rect); it.next();) {
1365  const int canvas_x = it.x() - mapview.gui_x0;
1366  const int canvas_y = it.y() - mapview.gui_y0;
1367 
1368  if (it.has_tile() && tile_city(it.tile())) {
1369  int width = 0, height = 0;
1370  struct city *pcity = tile_city(it.tile());
1371 
1372  show_city_desc(mapview.store, canvas_x, canvas_y, pcity, &width,
1373  &height);
1374  log_debug("Drawing %s.", city_name_get(pcity));
1375 
1376  if (width > max_desc_width || height > max_desc_height) {
1377  /* The update was incomplete! We queue a new update. Note that
1378  * this is recursively queueing an update within a dequeuing of an
1379  * update. This is allowed specifically because of the code in
1380  * unqueue_mapview_updates. See that function for more. */
1381  log_debug("Re-queuing %s.", city_name_get(pcity));
1382  update_city_description(pcity);
1383  }
1384  new_max_width = MAX(width, new_max_width);
1385  new_max_height = MAX(height, new_max_height);
1386  }
1387  }
1388 
1389  /* We don't update the new max values until the end, so that the
1390  * check above to see what cities need redrawing will be complete. */
1391  max_desc_width = MAX(max_desc_width, new_max_width);
1392  max_desc_height = MAX(max_desc_height, new_max_height);
1393 }
1394 
1398 void show_tile_labels(int canvas_base_x, int canvas_base_y, int width_base,
1399  int height_base)
1400 {
1401  const int dx = max_label_size.width() - tileset_tile_width(tileset);
1402  const int dy = max_label_size.height();
1403  auto new_max_size = max_label_size;
1404 
1405  const auto rect = QRect(mapview.gui_x0 + canvas_base_x - dx / 2,
1406  mapview.gui_y0 + canvas_base_y - dy,
1407  width_base + dx, height_base + dy);
1408  for (auto it = freeciv::gui_rect_iterator(tileset, rect); it.next();) {
1409  const int canvas_x = it.x() - mapview.gui_x0;
1410  const int canvas_y = it.y() - mapview.gui_y0;
1411 
1412  if (it.has_tile() && it.tile()->label != nullptr) {
1413  const auto size =
1414  show_tile_label(mapview.store, canvas_x, canvas_y, it.tile());
1415  log_debug("Drawing label %s.", it.tile()->label);
1416 
1417  if (size.width() > max_label_size.width()
1418  || size.height() > max_label_size.height()) {
1419  /* The update was incomplete! We queue a new update. Note that
1420  * this is recursively queueing an update within a dequeuing of an
1421  * update. This is allowed because we use a queued connection. */
1422  log_debug("Re-queuing tile label %s drawing.", it.tile()->label);
1423  update_tile_label(it.tile());
1424  }
1425  new_max_size = new_max_size.expandedTo(size);
1426  }
1427  }
1428 
1429  /* We don't update the new max values until the end, so that the
1430  * check above to see what cities need redrawing will be complete. */
1431  max_label_size = new_max_size;
1432 }
1433 
1438 void draw_segment(const tile *src_tile, enum direction8 dir, bool safe)
1439 {
1440  float canvas_x, canvas_y, canvas_dx, canvas_dy;
1441 
1442  // Determine the source position of the segment.
1443  (void) tile_to_canvas_pos(&canvas_x, &canvas_y, src_tile);
1444  canvas_x += tileset_tile_width(tileset) / 2;
1445  canvas_y += tileset_tile_height(tileset) / 2;
1446 
1447  // Determine the vector of the segment.
1448  map_to_gui_vector(tileset, &canvas_dx, &canvas_dy, DIR_DX[dir],
1449  DIR_DY[dir]);
1450 
1451  // Draw the segment.
1452  QPainter p(mapview.store);
1453  p.setPen(QPen(get_color(tileset, safe ? COLOR_MAPVIEW_GOTO
1454  : COLOR_MAPVIEW_UNSAFE_GOTO),
1455  2));
1456  p.drawLine(canvas_x, canvas_y, canvas_x + canvas_dx, canvas_y + canvas_dy);
1457  p.end();
1458 
1459  /* The actual area drawn will extend beyond the base rectangle, since
1460  * the goto lines have width. */
1461  dirty_rect(MIN(canvas_x, canvas_x + canvas_dx) - GOTO_WIDTH,
1462  MIN(canvas_y, canvas_y + canvas_dy) - GOTO_WIDTH,
1463  ABS(canvas_dx) + 2 * GOTO_WIDTH,
1464  ABS(canvas_dy) + 2 * GOTO_WIDTH);
1465 
1466  /* It is possible that the mapview wraps between the source and dest
1467  * tiles. In this case they will not be next to each other; they'll be
1468  * on the opposite sides of the screen. If this happens then the dest
1469  * tile will not be updated. This is consistent with the mapview design
1470  * which fails when the size of the mapview approaches that of the map. */
1471 }
1472 
1477 void decrease_unit_hp_smooth(struct unit *punit0, int hp0,
1478  struct unit *punit1, int hp1)
1479 {
1480  struct unit *losing_unit = (hp0 == 0 ? punit0 : punit1);
1481 
1482  set_units_in_combat(punit0, punit1);
1483 
1484  /* Make sure we don't start out with fewer HP than we're supposed to
1485  * end up with (which would cause the following loop to break). */
1486  punit0->hp = MAX(punit0->hp, hp0);
1487  punit1->hp = MAX(punit1->hp, hp1);
1488 
1490 
1491  while (punit0->hp > hp0 || punit1->hp > hp1) {
1492  const int diff0 = punit0->hp - hp0, diff1 = punit1->hp - hp1;
1493 
1494  if (fc_rand(diff0 + diff1) < diff0) {
1495  punit0->hp--;
1496  refresh_unit_mapcanvas(punit0, unit_tile(punit0), false);
1497  } else {
1498  punit1->hp--;
1499  refresh_unit_mapcanvas(punit1, unit_tile(punit1), false);
1500  }
1501 
1504  }
1505 
1506  refresh_unit_mapcanvas(losing_unit, unit_tile(losing_unit), false);
1507  animate_unit_explosion(unit_tile(losing_unit));
1508 
1509  set_units_in_combat(nullptr, nullptr);
1510  refresh_unit_mapcanvas(punit0, unit_tile(punit0), true);
1511  refresh_unit_mapcanvas(punit1, unit_tile(punit1), true);
1513 }
1514 
1518 void animate_unit_explosion(const tile *location)
1519 {
1521  return;
1522  }
1523 
1524  const auto anim = get_unit_explode_animation(tileset);
1525  if (anim.empty()) {
1526  return;
1527  }
1528 
1529  float canvas_x, canvas_y;
1530  if (!tile_to_canvas_pos(&canvas_x, &canvas_y, location)) {
1531  // Hidden tile: nothing to draw
1532  return;
1533  }
1534 
1536 
1537  // Save the map without the explosion overlay
1538  const auto base_map =
1539  mapview.store->copy(canvas_x, canvas_y, tileset_tile_width(tileset),
1541 
1542  /*
1543  * Animation loop
1544  */
1545  for (const auto &sprite : anim) {
1546  QPainter p;
1547  p.begin(mapview.store);
1548 
1549  // Restore the base map
1550  p.drawPixmap(canvas_x, canvas_y, base_map);
1551 
1552  // Overlay the explosion
1553  p.drawPixmap(
1554  canvas_x + tileset_tile_width(tileset) / 2 - sprite->width() / 2,
1555  canvas_y + tileset_tile_height(tileset) / 2 - sprite->height() / 2,
1556  *sprite);
1557  p.end();
1558 
1559  // Force a repaint
1560  dirty_rect(canvas_x, canvas_y, tileset_tile_width(tileset),
1562  flush_dirty();
1563 
1564  // Wait
1566  }
1567 }
1568 
1573 void move_unit_map_canvas(struct unit *punit, struct tile *src_tile, int dx,
1574  int dy)
1575 {
1576  struct tile *dest_tile;
1577  int dest_x, dest_y, src_x, src_y;
1578  int prev_x = -1;
1579  int prev_y = -1;
1580  int tuw;
1581  int tuh;
1582 
1583  // only works for adjacent-square moves
1584  if (dx < -1 || dx > 1 || dy < -1 || dy > 1 || (dx == 0 && dy == 0)) {
1585  return;
1586  }
1587 
1588  index_to_map_pos(&src_x, &src_y, tile_index(src_tile));
1589  dest_x = src_x + dx;
1590  dest_y = src_y + dy;
1591  dest_tile = map_pos_to_tile(&(wld.map), dest_x, dest_y);
1592  if (!dest_tile) {
1593  return;
1594  }
1595 
1596  if (tile_visible_mapcanvas(src_tile)
1597  || tile_visible_mapcanvas(dest_tile)) {
1598  float start_x, start_y;
1599  float canvas_dx, canvas_dy;
1600 
1602 
1603  map_to_gui_vector(tileset, &canvas_dx, &canvas_dy, dx, dy);
1604 
1605  tile_to_canvas_pos(&start_x, &start_y, src_tile);
1607  start_y -= tileset_tile_height(tileset) / 2;
1608  start_y -=
1610  }
1611 
1612  // Bring the backing store up to date.
1614 
1615  tuw = tileset_unit_width(tileset);
1617 
1618  // Start the timer (AFTER the unqueue above).
1619  anim_timer->start();
1620 
1621  const auto timing_ms = gui_options->smooth_move_unit_msec;
1622  auto mytime = 0;
1623  do {
1624  mytime = MIN(anim_timer->elapsed(), timing_ms);
1625 
1626  auto new_x = start_x + (canvas_dx * mytime) / timing_ms;
1627  auto new_y = start_y + (canvas_dy * mytime) / timing_ms;
1628 
1629  if (new_x != prev_x || new_y != prev_y) {
1630  // Backup the canvas store to the temp store.
1631  QPainter p(mapview.tmp_store);
1632  p.drawPixmap(new_x, new_y, *mapview.store, new_x, new_y, tuw, tuh);
1633  p.end();
1634 
1635  // Draw
1636  put_unit(punit, mapview.store, QPoint(new_x, new_y));
1637  dirty_rect(new_x, new_y, tuw, tuh);
1638 
1639  // Flush.
1640  flush_dirty();
1641 
1642  // Restore the backup. It won't take effect until the next flush.
1643  p.begin(mapview.store);
1644  p.drawPixmap(new_x, new_y, *mapview.tmp_store, new_x, new_y, tuw,
1645  tuh);
1646  p.end();
1647  dirty_rect(new_x, new_y, tuw, tuh);
1648 
1649  prev_x = new_x;
1650  prev_y = new_y;
1651  } else {
1652  fc_usleep(500);
1653  }
1654  } while (mytime < timing_ms);
1655  }
1656 }
1657 
1670 struct city *find_city_or_settler_near_tile(const struct tile *ptile,
1671  struct unit **punit)
1672 {
1673  // Rule g
1674  if (tile_virtual_check(ptile)) {
1675  return nullptr;
1676  }
1677 
1678  struct city *closest_city;
1679  struct city *pcity;
1680  struct unit *closest_settler = nullptr, *best_settler = nullptr;
1681  int max_rad = rs_max_city_radius_sq();
1682 
1683  if (punit) {
1684  *punit = nullptr;
1685  }
1686 
1687  // Check if there is visible city working that tile
1688  pcity = tile_worked(ptile);
1689  if (pcity && pcity->tile) {
1690  if (nullptr == client.conn.playing
1691  || city_owner(pcity) == client.conn.playing) {
1692  // rule a
1693  return pcity;
1694  } else {
1695  // rule b
1696  return nullptr;
1697  }
1698  }
1699 
1700  // rule e
1701  closest_city = nullptr;
1702 
1703  // check within maximum (squared) city radius
1704  city_tile_iterate(max_rad, ptile, tile1)
1705  {
1706  pcity = tile_city(tile1);
1707  if (pcity
1708  && (nullptr == client.conn.playing
1709  || city_owner(pcity) == client.conn.playing)
1710  && client_city_can_work_tile(pcity, tile1)) {
1711  /*
1712  * Note, we must explicitly check if the tile is workable (with
1713  * city_can_work_tile() above), since it is possible that another
1714  * city (perhaps an UNSEEN city) may be working it!
1715  */
1716 
1717  if (mapdeco_is_highlight_set(city_tile(pcity))) {
1718  // rule c
1719  return pcity;
1720  }
1721  if (!closest_city) {
1722  closest_city = pcity;
1723  }
1724  }
1725  }
1727 
1728  // rule d
1729  if (closest_city || !punit) {
1730  return closest_city;
1731  }
1732 
1733  if (!game.scenario.prevent_new_cities) {
1734  // check within maximum (squared) city radius
1735  city_tile_iterate(max_rad, ptile, tile1)
1736  {
1737  unit_list_iterate(tile1->units, psettler)
1738  {
1739  if ((nullptr == client.conn.playing
1740  || unit_owner(psettler) == client.conn.playing)
1741  && unit_can_do_action(psettler, ACTION_FOUND_CITY)
1742  && city_can_be_built_here(unit_tile(psettler), psettler)) {
1743  if (!closest_settler) {
1744  closest_settler = psettler;
1745  }
1746  if (!best_settler && psettler->client.colored) {
1747  best_settler = psettler;
1748  }
1749  }
1750  }
1752  }
1754 
1755  if (best_settler) {
1756  // Rule e
1757  *punit = best_settler;
1758  } else if (closest_settler) {
1759  // Rule f
1760  *punit = closest_settler;
1761  }
1762  }
1763 
1764  // rule g
1765  return nullptr;
1766 }
1767 
1774 static void append_city_buycost_string(const struct city *pcity,
1775  char *buffer, int buffer_len)
1776 {
1777  if (!pcity || !buffer || buffer_len < 1) {
1778  return;
1779  }
1780 
1781  if (!gui_options->draw_city_buycost || !city_can_buy(pcity)) {
1782  return;
1783  }
1784 
1785  cat_snprintf(buffer, buffer_len, "/%d", pcity->client.buy_cost);
1786 }
1787 
1792 void get_city_mapview_production(const city *pcity, char *buffer,
1793  size_t buffer_len)
1794 {
1795  int turns;
1796 
1797  universal_name_translation(&pcity->production, buffer, buffer_len);
1798 
1799  if (city_production_has_flag(pcity, IF_GOLD)) {
1800  return;
1801  }
1802  turns = city_production_turns_to_build(pcity, true);
1803 
1804  if (999 < turns) {
1805  cat_snprintf(buffer, buffer_len, " -");
1806  } else {
1807  cat_snprintf(buffer, buffer_len, " %d", turns);
1808  }
1809 
1810  append_city_buycost_string(pcity, buffer, buffer_len);
1811 }
1812 
1819  char *trade_routes_buffer,
1820  size_t trade_routes_buffer_len,
1821  enum color_std *pcolor)
1822 {
1823  int num_trade_routes;
1824  int max_routes;
1825 
1826  if (!trade_routes_buffer || trade_routes_buffer_len <= 0) {
1827  return;
1828  }
1829 
1830  if (!pcity) {
1831  trade_routes_buffer[0] = '\0';
1832  if (pcolor) {
1833  *pcolor = COLOR_MAPVIEW_CITYTEXT;
1834  }
1835  return;
1836  }
1837 
1838  num_trade_routes = trade_route_list_size(pcity->routes);
1839  max_routes = max_trade_routes(pcity);
1840 
1841  fc_snprintf(trade_routes_buffer, trade_routes_buffer_len, "%d/%d",
1842  num_trade_routes, max_routes);
1843 
1844  if (pcolor) {
1845  if (num_trade_routes == max_routes) {
1846  *pcolor = COLOR_MAPVIEW_TRADE_ROUTES_ALL_BUILT;
1847  } else if (num_trade_routes == 0) {
1848  *pcolor = COLOR_MAPVIEW_TRADE_ROUTES_NO_BUILT;
1849  } else {
1850  *pcolor = COLOR_MAPVIEW_TRADE_ROUTES_SOME_BUILT;
1851  }
1852  }
1853 }
1854 
1855 /***************************************************************************/
1856 
1875 std::map<freeciv::map_updates_handler::update_type, QRectF> update_rects()
1876 {
1877  const double width = tileset_tile_width(tileset);
1878  const double height = tileset_tile_height(tileset);
1879  const double unit_width = tileset_unit_width(tileset);
1880  const double unit_height = tileset_unit_height(tileset);
1881  const double city_width = get_citydlg_canvas_width() + width;
1882  const double city_height = get_citydlg_canvas_height() + height;
1883 
1884  using update_type = freeciv::map_updates_handler::update_type;
1885  auto rects = std::map<update_type, QRectF>();
1886  rects[update_type::tile_single] = QRectF(0, 0, width, height);
1887  rects[update_type::tile_full] =
1888  QRectF(-width / 2, -height / 2, 2 * width, 2 * height);
1889  rects[update_type::unit] =
1890  QRectF((width - unit_width) / 2, height - unit_height, unit_width,
1891  unit_height);
1892  rects[update_type::city_description] =
1893  QRectF(-(max_desc_width - width) / 2, height, max_desc_width,
1894  max_desc_height);
1895  rects[update_type::city_map] =
1896  QRectF(-(city_width - width) / 2, -(city_height - height) / 2,
1897  city_width, city_height);
1898  rects[update_type::tile_label] =
1899  QRectF(QPointF(-(max_label_size.width() - width) / 2., height),
1900  max_label_size);
1901  return rects;
1902 }
1903 
1908 {
1909  // Emit the repaint_needed signal of all updates handlers.
1910  // Emitting a signal is simply done by calling its function.
1913 }
1914 
1919 void get_city_mapview_name_and_growth(const city *pcity, char *name_buffer,
1920  size_t name_buffer_len,
1921  char *growth_buffer,
1922  size_t growth_buffer_len,
1923  enum color_std *growth_color,
1924  enum color_std *production_color)
1925 {
1926  fc_strlcpy(name_buffer, city_name_get(pcity), name_buffer_len);
1927 
1928  *production_color = COLOR_MAPVIEW_CITYTEXT;
1929  if (nullptr == client.conn.playing
1930  || city_owner(pcity) == client.conn.playing) {
1931  int turns = city_turns_to_grow(pcity);
1932 
1933  if (turns == 0) {
1934  fc_snprintf(growth_buffer, growth_buffer_len, "X");
1935  } else if (turns == FC_INFINITY) {
1936  fc_snprintf(growth_buffer, growth_buffer_len, "-");
1937  } else {
1938  /* Negative turns means we're shrinking, but that's handled
1939  down below. */
1940  fc_snprintf(growth_buffer, growth_buffer_len, "%d", abs(turns));
1941  }
1942 
1943  if (turns <= 0) {
1944  // A blocked or shrinking city has its growth status shown in red.
1945  *growth_color = COLOR_MAPVIEW_CITYGROWTH_BLOCKED;
1946  } else {
1947  *growth_color = COLOR_MAPVIEW_CITYTEXT;
1948  }
1949 
1950  if (pcity->surplus[O_SHIELD] < 0) {
1951  *production_color = COLOR_MAPVIEW_CITYPROD_NEGATIVE;
1952  }
1953  } else {
1954  growth_buffer[0] = '\0';
1955  *growth_color = COLOR_MAPVIEW_CITYTEXT;
1956  }
1957 }
1958 
1964 {
1965  const int W = tileset_tile_width(tileset);
1966  const int H = tileset_tile_height(tileset);
1967  int w = mapview.store_width, h = mapview.store_height;
1968 
1969  /* If the mapview window is too large, cached drawing is not possible.
1970  *
1971  * BACKGROUND: cached drawing occurrs when the mapview is scrolled just
1972  * a short distance. The majority of the mapview window can simply be
1973  * copied while the newly visible areas must be drawn from scratch. This
1974  * speeds up drawing significantly, especially when using the scrollbars
1975  * or mapview sliding.
1976  *
1977  * When the mapview is larger than the map, however, some tiles may become
1978  * visible twice. In this case one instance of the tile will be drawn
1979  * while all others are drawn black. When this happens the cached drawing
1980  * system breaks since it assumes the mapview canvas is an "ideal" window
1981  * over the map. So black tiles may be scrolled from the edges of the
1982  * mapview into the center, while drawn tiles may be scrolled from the
1983  * center of the mapview out to the edges. The result is very bad.
1984  *
1985  * There are a few different ways this could be solved. One way is simply
1986  * to turn off cached drawing, which is what we do now. If the mapview
1987  * window gets to be too large, the caching is disabled. Another would
1988  * be to prevent the window from getting too large in the first place -
1989  * but because the window boundaries aren't at an even tile this would
1990  * mean the entire map could never be shown. Yet another way would be
1991  * to draw tiles more than once if they are visible in multiple locations
1992  * on the mapview.
1993  *
1994  * The logic below is complicated and determined in part by
1995  * trial-and-error. */
1996  if (!current_topo_has_flag(TF_WRAPX) && !current_topo_has_flag(TF_WRAPY)) {
1997  /* An unwrapping map: no limitation. On an unwrapping map no tile can
1998  * be visible twice so there's no problem. */
1999  return true;
2000  }
2001  if (XOR(current_topo_has_flag(TF_ISO) || current_topo_has_flag(TF_HEX),
2003  /* Non-matching. In this case the mapview does not line up with the
2004  * map's axis of wrapping. This will give very bad results for the
2005  * player!
2006  * We can never show more than half of the map.
2007  *
2008  * We divide by 4 below because we have to divide by 2 twice. The
2009  * first division by 2 is because the square must be half the size
2010  * of the (width+height). The second division by two is because for
2011  * an iso-map, NATURAL_XXX has a scale of 2, whereas for iso-view
2012  * NORMAL_TILE_XXX has a scale of 2. */
2013  return (w <= (NATURAL_WIDTH + NATURAL_HEIGHT) * W / 4
2014  && h <= (NATURAL_WIDTH + NATURAL_HEIGHT) * H / 4);
2015  } else {
2016  // Matching.
2017  const int isofactor = (tileset_is_isometric(tileset) ? 2 : 1);
2018  const int isodiff = (tileset_is_isometric(tileset) ? 6 : 2);
2019 
2020  /* Now we can use the full width and height, with the exception of a
2021  * small area on each side. */
2022  if (current_topo_has_flag(TF_WRAPX)
2023  && w > (NATURAL_WIDTH - isodiff) * W / isofactor) {
2024  return false;
2025  }
2026  if (current_topo_has_flag(TF_WRAPY)
2027  && h > (NATURAL_HEIGHT - isodiff) * H / isofactor) {
2028  return false;
2029  }
2030  return true;
2031  }
2032 }
2033 
2039 {
2040  // HACK: this must be called on a map_info packet.
2042 
2043  mapdeco_free();
2044  // Q_GLOB_STAT is allocated automatically
2045 }
2046 
2051 {
2052  for (auto *a : qAsConst(*mapdeco_gotoline)) {
2053  delete a;
2054  a = nullptr;
2055  }
2056 }
2057 
2061 bool mapdeco_is_highlight_set(const struct tile *ptile)
2062 {
2063  if (!ptile) {
2064  return false;
2065  }
2066  return mapdeco_highlight_set->contains(ptile);
2067 }
2068 
2072 void mapdeco_set_crosshair(const struct tile *ptile, bool crosshair)
2073 {
2074  bool changed;
2075 
2076  if (!ptile) {
2077  return;
2078  }
2079 
2080  changed = mapdeco_crosshair_set->contains(ptile);
2081  if (changed) {
2082  mapdeco_crosshair_set->remove(ptile);
2083  }
2084  if (crosshair) {
2085  mapdeco_crosshair_set->insert(ptile);
2086  }
2087 
2088  if (!changed) {
2089  refresh_tile_mapcanvas(ptile, false);
2090  }
2091 }
2092 
2096 bool mapdeco_is_crosshair_set(const struct tile *ptile)
2097 {
2098  if (!mapdeco_crosshair_set || !ptile) {
2099  return false;
2100  }
2101  return mapdeco_crosshair_set->contains(ptile);
2102 }
2103 
2109 {
2110  for (const auto *ptile : qAsConst(*mapdeco_crosshair_set)) {
2111  refresh_tile_mapcanvas(ptile, false);
2112  }
2113  mapdeco_crosshair_set->clear();
2114 }
2115 
2121 void mapdeco_add_gotoline(const struct tile *ptile, enum direction8 dir,
2122  bool safe)
2123 {
2124  struct gotoline_counter *pglc;
2125  const struct tile *ptile_dest;
2126  bool changed;
2127 
2128  if (!ptile || dir > direction8_max()) {
2129  return;
2130  }
2131  ptile_dest = mapstep(&(wld.map), ptile, dir);
2132  if (!ptile_dest) {
2133  return;
2134  }
2135 
2136  if (!(pglc = mapdeco_gotoline->value(ptile, nullptr))) {
2137  pglc = new gotoline_counter();
2138  mapdeco_gotoline->insert(ptile, pglc);
2139  }
2140  if (safe) {
2141  changed = (pglc->line_count[dir] < 1);
2142  pglc->line_count[dir]++;
2143  } else {
2144  changed =
2145  (pglc->line_danger_count[dir] < 1) || (pglc->line_count[dir] > 0);
2146  pglc->line_danger_count[dir]++;
2147  }
2148 
2149  if (changed) {
2150  refresh_tile_mapcanvas(ptile, false);
2151  refresh_tile_mapcanvas(ptile_dest, false);
2152  }
2153 }
2154 
2160 void mapdeco_set_gotoroute(const struct unit *punit)
2161 {
2162  const struct unit_order *porder;
2163  const struct tile *ptile;
2164  int i, ind;
2165 
2166  if (!punit || !unit_tile(punit) || !unit_has_orders(punit)
2167  || punit->orders.length < 1) {
2168  return;
2169  }
2170 
2171  ptile = unit_tile(punit);
2172 
2173  for (i = 0; ptile != nullptr && i < punit->orders.length; i++) {
2174  if (punit->orders.index + i >= punit->orders.length
2175  && !punit->orders.repeat) {
2176  break;
2177  }
2178 
2179  ind = (punit->orders.index + i) % punit->orders.length;
2180  porder = &punit->orders.list[ind];
2181  if (porder->order == ORDER_MOVE || porder->order == ORDER_ACTION_MOVE) {
2182  mapdeco_add_gotoline(ptile, porder->dir, true); // FIXME always "safe"
2183  ptile = mapstep(&(wld.map), ptile, porder->dir);
2184  } else if (porder->order == ORDER_PERFORM_ACTION) {
2185  auto tile = index_to_tile(&(wld.map), porder->target);
2186  auto dir = direction8_invalid();
2187  if (base_get_direction_for_step(&(wld.map), ptile, tile, &dir)) {
2188  mapdeco_add_gotoline(ptile, dir, true); // FIXME always "safe"
2189  }
2190  ptile = tile;
2191  }
2192  }
2193 }
2194 
2199 bool mapdeco_is_gotoline_set(const struct tile *ptile, enum direction8 dir,
2200  bool *safe)
2201 {
2202  struct gotoline_counter *pglc;
2203 
2204  if (!ptile || dir > direction8_max()) {
2205  return false;
2206  }
2207 
2208  if (!(pglc = mapdeco_gotoline->value(ptile, nullptr))) {
2209  return false;
2210  }
2211 
2212  *safe = (pglc->line_danger_count[dir] == 0);
2213  return pglc->line_count[dir] > 0 || pglc->line_danger_count[dir] > 0;
2214 }
2215 
2221 {
2222  gotohash::const_iterator i = mapdeco_gotoline->constBegin();
2223  while (i != mapdeco_gotoline->constEnd()) {
2224  refresh_tile_mapcanvas(i.key(), false);
2225  adjc_dir_iterate(&(wld.map), i.key(), ptile_dest, dir)
2226  {
2227  if (i.value()->line_count[dir] > 0
2228  || i.value()->line_danger_count[dir] > 0) {
2229  refresh_tile_mapcanvas(ptile_dest, false);
2230  }
2231  }
2233  ++i;
2234  }
2235  mapdeco_free();
2236  mapdeco_gotoline->clear();
2237 }
2238 
2242 void map_canvas_resized(int width, int height)
2243 {
2244  // Save the current center before we resize
2245  const auto center_tile = get_center_tile_mapcanvas();
2246 
2247  int tile_width = (width + tileset_tile_width(tileset) - 1)
2249  int tile_height = (height + tileset_tile_height(tileset) - 1)
2251  int full_width = tile_width * tileset_tile_width(tileset);
2252  int full_height = tile_height * tileset_tile_height(tileset);
2253 
2254  bool cache_resized = width != mapview.width || height != mapview.height;
2255 
2256  /* Since a resize is only triggered when the tile_*** changes, the canvas
2257  * width and height must include the entire backing store - otherwise
2258  * small resizings may lead to undrawn tiles. */
2259  mapview.width = width;
2260  mapview.height = height;
2261  mapview.store_width = full_width;
2262  mapview.store_height = full_height;
2263 
2264  // If the cache size has changed, resize the canvas.
2265  if (cache_resized) {
2266  if (mapview.store) {
2267  delete mapview.store;
2268  delete mapview.tmp_store;
2269  }
2270  mapview.store = new QPixmap(full_width, full_height);
2271  mapview.store->fill(get_color(tileset, COLOR_MAPVIEW_UNKNOWN));
2272 
2273  mapview.tmp_store = new QPixmap(full_width, full_height);
2274  }
2275 
2276  if (!map_is_empty() && can_client_change_view()) {
2277  if (cache_resized) {
2278  if (center_tile != nullptr) {
2279  int x_left, y_top;
2280  float gui_x, gui_y;
2281 
2282  index_to_map_pos(&x_left, &y_top, tile_index(center_tile));
2283  map_to_gui_pos(tileset, &gui_x, &gui_y, x_left, y_top);
2284 
2285  /* Put the center pixel of the tile at the exact center of the
2286  * mapview. */
2287  gui_x -= (mapview.width - tileset_tile_width(tileset)) / 2;
2288  gui_y -= (mapview.height - tileset_tile_height(tileset)) / 2;
2289 
2290  calc_mapview_origin(&gui_x, &gui_y);
2291  mapview.gui_x0 = gui_x;
2292  mapview.gui_y0 = gui_y;
2293  }
2295  update_minimap();
2296 
2297  /* Do not draw to the screen here as that could cause problems
2298  * when we are only initially setting up the view and some widgets
2299  * are not yet ready. */
2301  }
2302  }
2305 }
2306 
2311 {
2312  // Create a dummy map to make sure mapview.store is never nullptr.
2313  map_canvas_resized(1, 1);
2314  overview_init();
2315 }
2316 
2321 {
2322  delete mapview.store;
2323  delete mapview.tmp_store;
2324 }
2325 
2330 struct link_mark {
2331  enum text_link_type type; // The target type.
2332  int id; // The city or unit id, or tile index.
2333  int turn_counter; // The turn counter before it disappears.
2334 };
2335 
2336 #define SPECLIST_TAG link_mark
2337 #define SPECLIST_TYPE struct link_mark
2338 #include "speclist.h"
2339 #define link_marks_iterate(pmark) \
2340  TYPED_LIST_ITERATE(struct link_mark, link_marks, pmark)
2341 #define link_marks_iterate_end LIST_ITERATE_END
2342 
2343 static struct link_mark_list *link_marks = nullptr;
2344 
2348 static struct link_mark *link_mark_find(enum text_link_type type, int id)
2349 {
2350  link_marks_iterate(pmark)
2351  {
2352  if (pmark->type == type && pmark->id == id) {
2353  return pmark;
2354  }
2355  }
2357 
2358  return nullptr;
2359 }
2360 
2364 static struct link_mark *link_mark_new(enum text_link_type type, int id,
2365  int turns)
2366 {
2367  struct link_mark *pmark = new link_mark();
2368 
2369  pmark->type = type;
2370  pmark->id = id;
2371  pmark->turn_counter = turns;
2372 
2373  return pmark;
2374 }
2375 
2379 static void link_mark_destroy(struct link_mark *pmark) { delete pmark; }
2380 
2384 static struct tile *link_mark_tile(const struct link_mark *pmark)
2385 {
2386  switch (pmark->type) {
2387  case TLT_CITY: {
2388  struct city *pcity = game_city_by_number(pmark->id);
2389  return pcity ? pcity->tile : nullptr;
2390  }
2391  case TLT_TILE:
2392  return index_to_tile(&(wld.map), pmark->id);
2393  case TLT_UNIT: {
2394  struct unit *punit = game_unit_by_number(pmark->id);
2395  return punit ? unit_tile(punit) : nullptr;
2396  }
2397  case TLT_INVALID:
2398  fc_assert_ret_val(pmark->type != TLT_INVALID, nullptr);
2399  }
2400  return nullptr;
2401 }
2402 
2406 static QColor link_mark_color(const struct link_mark *pmark)
2407 {
2408  switch (pmark->type) {
2409  case TLT_CITY:
2410  return get_color(tileset, COLOR_MAPVIEW_CITY_LINK);
2411  case TLT_TILE:
2412  return get_color(tileset, COLOR_MAPVIEW_TILE_LINK);
2413  case TLT_UNIT:
2414  return get_color(tileset, COLOR_MAPVIEW_UNIT_LINK);
2415  case TLT_INVALID:
2416  fc_assert_ret_val(pmark->type != TLT_INVALID, nullptr);
2417  }
2418  return QColor();
2419 }
2420 
2424 static void link_mark_draw(const struct link_mark *pmark)
2425 {
2426  int width = tileset_tile_width(tileset);
2427  int height = tileset_tile_height(tileset);
2428  int xd = width / 20, yd = height / 20;
2429  int xlen = width / 3, ylen = height / 3;
2430  float canvas_x, canvas_y;
2431  int x_left, x_right, y_top, y_bottom;
2432  struct tile *ptile = link_mark_tile(pmark);
2433  auto color = link_mark_color(pmark);
2434 
2435  if (!ptile || !tile_to_canvas_pos(&canvas_x, &canvas_y, ptile)) {
2436  return;
2437  }
2438 
2439  x_left = canvas_x + xd;
2440  x_right = canvas_x + width - xd;
2441  y_top = canvas_y + yd;
2442  y_bottom = canvas_y + height - yd;
2443 
2444  QPainter p(mapview.store);
2445  p.setPen(QPen(color, 2));
2446  p.drawLine(x_left, y_top, x_left + xlen, y_top);
2447  p.drawLine(x_left, y_top, x_left, y_top + ylen);
2448 
2449  p.drawLine(x_right, y_top, x_right - xlen, y_top);
2450  p.drawLine(x_right, y_top, x_right, y_top + ylen);
2451 
2452  p.drawLine(x_left, y_bottom, x_left + xlen, y_bottom);
2453  p.drawLine(x_left, y_bottom, x_left, y_bottom - ylen);
2454 
2455  p.drawLine(x_right, y_bottom, x_right - xlen, y_bottom);
2456  p.drawLine(x_right, y_bottom, x_right, y_bottom - ylen);
2457  p.end();
2458 }
2459 
2464 {
2465  if (link_marks) {
2466  link_marks_free();
2467  }
2468 
2469  link_marks = link_mark_list_new_full(link_mark_destroy);
2470 }
2471 
2476 {
2477  if (!link_marks) {
2478  return;
2479  }
2480 
2481  link_mark_list_destroy(link_marks);
2482  link_marks = nullptr;
2483 }
2484 
2489 {
2490  link_marks_iterate(pmark) { link_mark_draw(pmark); }
2492 }
2493 
2498 {
2499  link_mark_list_clear(link_marks);
2501 }
2502 
2507 {
2508  link_marks_iterate(pmark)
2509  {
2510  if (--pmark->turn_counter <= 0) {
2511  link_mark_list_remove(link_marks, pmark);
2512  }
2513  }
2515 
2516  // update_map_canvas_visible(); not needed here.
2517 }
2518 
2522 void link_mark_add_new(enum text_link_type type, int id)
2523 {
2524  struct link_mark *pmark = link_mark_find(type, id);
2525  struct tile *ptile;
2526 
2527  if (pmark) {
2528  // Already displayed, but maybe increase the turn counter.
2529  pmark->turn_counter = MAX(pmark->turn_counter, 2);
2530  return;
2531  }
2532 
2533  pmark = link_mark_new(type, id, 2);
2534  link_mark_list_append(link_marks, pmark);
2535  ptile = link_mark_tile(pmark);
2536  if (ptile && tile_visible_mapcanvas(ptile)) {
2537  refresh_tile_mapcanvas(ptile, false);
2538  }
2539 }
2540 
2544 void link_mark_restore(enum text_link_type type, int id)
2545 {
2546  struct link_mark *pmark;
2547  struct tile *ptile;
2548 
2549  if (link_mark_find(type, id)) {
2550  return;
2551  }
2552 
2553  pmark = link_mark_new(type, id, 1);
2554  link_mark_list_append(link_marks, pmark);
2555  ptile = link_mark_tile(pmark);
2556  if (ptile && tile_visible_mapcanvas(ptile)) {
2557  refresh_tile_mapcanvas(ptile, false);
2558  }
2559 }
2560 
2564 enum topo_comp_lvl tileset_map_topo_compatible(int topology_id,
2565  struct tileset *tset)
2566 {
2567  int tileset_topology;
2568 
2569  if (tileset_hex_width(tset) > 0) {
2571  tileset_topology = TF_HEX | TF_ISO;
2572  } else if (tileset_hex_height(tset) > 0) {
2574  tileset_topology = TF_HEX;
2575  } else if (tileset_is_isometric(tset)) {
2576  tileset_topology = TF_ISO;
2577  } else {
2578  tileset_topology = 0;
2579  }
2580 
2581  if (tileset_topology & TF_HEX) {
2582  if ((topology_id & (TF_HEX | TF_ISO)) == tileset_topology) {
2583  return TOPO_COMPATIBLE;
2584  }
2585 
2586  /* Hex topology must match for both hexness and iso/non-iso */
2587  return TOPO_INCOMP_HARD;
2588  }
2589 
2590  if (topology_id & TF_HEX) {
2591  return TOPO_INCOMP_HARD;
2592  }
2593 
2594  if ((topology_id & TF_ISO) != (tileset_topology & TF_ISO)) {
2595  /* Non-hex iso/non-iso incompatibility is a soft one */
2596  return TOPO_INCOMP_SOFT;
2597  }
2598 
2599  return TOPO_COMPATIBLE;
2600 }
QFont get_font(client_font font)
Returns given font.
Definition: canvas.cpp:57
client_font
Definition: canvas.h:19
@ FONT_CITY_NAME
Definition: canvas.h:20
int rs_max_city_radius_sq()
Maximum city radius in this ruleset.
Definition: city.cpp:152
struct player * city_owner(const struct city *pcity)
Return the owner of the city.
Definition: city.cpp:1083
struct tile * city_tile(const struct city *pcity)
Return the tile location of the city.
Definition: city.cpp:1095
bool city_production_has_flag(const struct city *pcity, enum impr_flag_id flag)
Return TRUE when the current production has this flag.
Definition: city.cpp:691
bool city_can_be_built_here(const struct tile *ptile, const struct unit *punit)
Returns TRUE if the given unit can build a city at the given map coordinates.
Definition: city.cpp:1423
const char * city_name_get(const struct city *pcity)
Return the name of the city.
Definition: city.cpp:1077
int city_production_turns_to_build(const struct city *pcity, bool include_shield_stock)
Calculates the turns which are needed to build the requested production in the city.
Definition: city.cpp:784
bool city_base_to_city_map(int *city_map_x, int *city_map_y, const struct city *const pcity, const struct tile *map_tile)
Finds the city map coordinate for a given map position and a city.
Definition: city.cpp:274
int city_turns_to_grow(const struct city *pcity)
Calculates the turns which are needed for the city to grow.
Definition: city.cpp:1897
#define cities_iterate_end
Definition: city.h:493
#define city_list_iterate(citylist, pcity)
Definition: city.h:482
#define cities_iterate(pcity)
Definition: city.h:486
#define city_tile_iterate(_radius_sq, _city_tile, _tile)
Definition: city.h:202
#define output_type_iterate(output)
Definition: city.h:764
#define city_list_iterate_end
Definition: city.h:484
#define city_tile_iterate_end
Definition: city.h:209
#define output_type_iterate_end
Definition: city.h:771
struct city * is_any_city_dialog_open()
Definition: citydlg.cpp:2315
int get_citydlg_canvas_height()
Return the height of the city dialog canvas.
int get_citydlg_canvas_width()
Return the width of the city dialog canvas.
bool city_can_buy(const struct city *pcity)
Return TRUE iff the city can buy.
Iterates over all map tiles that intersect with a rectangle in GUI coordinates.
bool next()
Iterates to the next item.
void update_all()
Requests an update of the whole (visible) map.
void update_tile_label(const tile *tile)
Registers a tile label for update.
void update_city_description(const city *city)
Registers a city label for update.
update_type
What kind of update should be performed.
void update(const city *city, bool full)
Registers a city for update.
static void invoke(_member_fct_ function, _args_ &&...args)
Definition: listener.h:145
bool client_is_global_observer()
Returns whether client is global observer.
struct player * client_player()
Either controlling or observing.
struct civclient client
bool can_client_change_view()
Return TRUE if the client can change the view; i.e.
enum known_type client_tile_get_known(const struct tile *ptile)
A tile's "known" field is used by the server to store whether each player knows the tile.
Definition: climap.cpp:29
bool client_city_can_work_tile(const struct city *pcity, const struct tile *ptile)
Returns TRUE when a tile is available to be worked, or the city itself is currently working the tile ...
Definition: climap.cpp:114
void flush_dirty_overview()
This module contains various general - mostly highlevel - functions used throughout the client.
QColor get_color(const struct tileset *t, enum color_std stdcolor)
Return a pointer to the given "standard" color.
void set_units_in_combat(struct unit *pattacker, struct unit *pdefender)
Adjusts way combatants are displayed suitable for combat.
Definition: control.cpp:905
enum cursor_hover_state hover_state
Definition: control.cpp:82
@ HOVER_GOTO
Definition: control.h:20
@ HOVER_PARADROP
Definition: control.h:21
@ HOVER_ACT_SEL_TGT
Definition: control.h:24
@ HOVER_NONE
Definition: control.h:19
@ HOVER_CONNECT
Definition: control.h:22
@ HOVER_DEBUG_TILE
Definition: control.h:26
@ HOVER_PATROL
Definition: control.h:23
@ HOVER_GOTO_SEL_TGT
Definition: control.h:25
bool editor_tile_is_selected(const struct tile *ptile)
Returns TRUE if the given tile is selected.
Definition: editor.cpp:1109
bool editor_is_active()
Returns TRUE if the client is in edit mode.
Definition: editor.cpp:335
#define DIR8_MAGIC_MAX
Definition: fc_types.h:386
@ O_SHIELD
Definition: fc_types.h:86
enum output_type_id Output_type_id
Definition: fc_types.h:295
text_link_type
@ TLT_INVALID
@ TLT_TILE
@ TLT_UNIT
@ TLT_CITY
struct unit * game_unit_by_number(int id)
Find unit out of all units in game: now uses fast idex method, instead of looking through all units o...
Definition: game.cpp:112
struct civ_game game
Definition: game.cpp:47
struct world wld
Definition: game.cpp:48
struct city * game_city_by_number(int id)
Often used function to get a city pointer from a city ID.
Definition: game.cpp:103
#define fc_assert(condition)
Definition: log.h:89
#define fc_assert_ret_val(condition, val)
Definition: log.h:114
#define log_debug(message,...)
Definition: log.h:65
const int DIR_DY[8]
Definition: map.cpp:58
#define nat_x
#define nat_y
struct tile * map_pos_to_tile(const struct civ_map *nmap, int map_x, int map_y)
Return the tile for the given cartesian (map) position.
Definition: map.cpp:391
bool map_is_empty()
Returns TRUE if we are at a stage of the game where the map has not yet been generated/loaded.
Definition: map.cpp:124
const int DIR_DX[8]
Definition: map.cpp:57
struct tile * nearest_real_tile(const struct civ_map *nmap, int x, int y)
Twiddle *x and *y to point to the nearest real tile, and ensure that the position is normalized.
Definition: map.cpp:935
void base_map_distance_vector(int *dx, int *dy, int x0dv, int y0dv, int x1dv, int y1dv)
Finds the difference between the two (unnormalized) positions, in cartesian (map) coordinates.
Definition: map.cpp:961
struct tile * index_to_tile(const struct civ_map *imap, int mindex)
Return the tile for the given index position.
Definition: map.cpp:429
bool base_get_direction_for_step(const struct civ_map *nmap, const struct tile *start_tile, const struct tile *end_tile, enum direction8 *dir)
Return TRUE and sets dir to the direction of the step if (end_x, end_y) can be reached from (start_x,...
Definition: map.cpp:1267
struct tile * mapstep(const struct civ_map *nmap, const struct tile *ptile, enum direction8 dir)
Step from the given tile in the given direction.
Definition: map.cpp:342
bool normalize_map_pos(const struct civ_map *nmap, int *x, int *y)
If the position is real, it will be normalized and TRUE will be returned.
Definition: map.cpp:919
#define current_topo_has_flag(flag)
Definition: map.h:37
#define adjc_dir_iterate(nmap, center_tile, itr_tile, dir_itr)
Definition: map.h:364
#define NATURAL_HEIGHT
Definition: map.h:158
#define MAP_IS_ISOMETRIC
Definition: map.h:32
#define MAP_TO_NATIVE_POS(pnat_x, pnat_y, map_x, map_y)
Definition: map.h:123
#define NATIVE_TO_MAP_POS(pmap_x, pmap_y, nat_x, nat_y)
Definition: map.h:118
#define adjc_dir_iterate_end
Definition: map.h:368
#define adjc_dir_base_iterate(nmap, center_tile, dir_itr)
Definition: map.h:371
#define NATURAL_WIDTH
Definition: map.h:157
#define adjc_dir_base_iterate_end
Definition: map.h:375
#define index_to_map_pos(pmap_x, pmap_y, mindex)
Definition: map.h:164
bool rectangle_active
void update_rect_at_mouse_pos()
The Area Selection rectangle.
Definition: mapctrl.cpp:88
void create_line_at_mouse_pos()
Draw a goto or patrol line at the current mouse position.
Definition: mapctrl.cpp:68
void flush_dirty(void)
Flush all regions that have been previously marked as dirty.
Definition: view_map.cpp:507
void dirty_all(void)
Mark the entire screen area as "dirty" so that we can flush it later.
Definition: view_map.cpp:494
void dirty_rect(int canvas_x, int canvas_y, int pixel_width, int pixel_height)
Mark the rectangular region as "dirty" so that we know to flush it later.
Definition: view_map.cpp:480
void update_minimap(void)
Return a canvas that is the overview window.
Definition: minimap.cpp:256
void show_city_desc(QPixmap *pcanvas, int canvas_x, int canvas_y, struct city *pcity, int *width, int *height)
Draw a description for the given city.
Definition: view_map.cpp:733
client_options * gui_options
Definition: options.cpp:74
void overview_init()
Allocates overview resources.
char * lines
Definition: packhand.cpp:131
#define fc_rand(_size)
Definition: rand.h:16
const char * universal_name_translation(const struct universal *psource, char *buf, size_t bufsz)
Make user-friendly text for the source.
#define CLIP(lower, current, upper)
Definition: shared.h:51
#define DIVIDE(n, d)
Definition: shared.h:71
#define FC_WRAP(value, range)
Definition: shared.h:59
#define MIN(x, y)
Definition: shared.h:49
#define FC_INFINITY
Definition: shared.h:32
#define ABS(x)
Definition: shared.h:55
#define MAX(x, y)
Definition: shared.h:48
#define XOR(p, q)
Definition: shared.h:64
size_t size
Definition: specvec.h:64
Definition: city.h:291
struct city::@15::@18 client
int surplus[O_LAST]
Definition: city.h:324
struct trade_route_list * routes
Definition: city.h:313
struct universal production
Definition: city.h:368
struct tile * tile
Definition: city.h:293
struct packet_scenario_info scenario
Definition: game.h:78
int xsize
Definition: map_types.h:73
int ysize
Definition: map_types.h:73
struct connection conn
Definition: client_main.h:89
int smooth_move_unit_msec
Definition: options.h:81
bool draw_city_buycost
Definition: options.h:126
bool draw_fog_of_war
Definition: options.h:139
bool draw_city_trade_routes
Definition: options.h:127
int smooth_combat_step_msec
Definition: options.h:83
struct player * playing
Definition: connection.h:142
int line_danger_count[DIR8_MAGIC_MAX]
int line_count[DIR8_MAGIC_MAX]
Definition: player.h:231
struct city_list * cities
Definition: player.h:263
Definition: tile.h:42
char * label
Definition: tile.h:57
enum unit_orders order
Definition: unit.h:80
enum direction8 dir
Definition: unit.h:89
int target
Definition: unit.h:84
Definition: unit.h:134
int length
Definition: unit.h:192
struct unit::@76::@78 client
int index
Definition: unit.h:192
int hp
Definition: unit.h:148
struct tile * tile
Definition: unit.h:136
struct unit_order * list
Definition: unit.h:195
bool repeat
Definition: unit.h:193
struct unit::@75 orders
int height
QPixmap * store
int store_width
QPixmap * tmp_store
float gui_x0
bool can_do_cached_drawing
int store_height
float gui_y0
int width
struct civ_map map
Definition: world_object.h:21
int fc_snprintf(char *str, size_t n, const char *format,...)
See also fc_utf8_snprintf_trunc(), fc_utf8_snprintf_rep().
Definition: support.cpp:537
size_t fc_strlcpy(char *dest, const char *src, size_t n)
fc_strlcpy() provides utf-8 version of (non-standard) function strlcpy() It is intended as more user-...
Definition: support.cpp:412
void fc_usleep(unsigned long usec)
Suspend execution for the specified number of microseconds.
Definition: support.cpp:349
int cat_snprintf(char *str, size_t n, const char *format,...)
cat_snprintf is like a combination of fc_snprintf and fc_strlcat; it does snprintf to the end of an e...
Definition: support.cpp:564
bool tile_virtual_check(const tile *vtile)
Check if the given tile is a virtual one or not.
Definition: tile.cpp:1085
struct city * tile_city(const struct tile *ptile)
Return the city on this tile (or nullptr), checking for city center.
Definition: tile.cpp:72
#define tile_index(_pt_)
Definition: tile.h:70
#define tile_worked(_tile)
Definition: tile.h:97
@ TILE_KNOWN_UNSEEN
Definition: tile.h:30
@ TILE_UNKNOWN
Definition: tile.h:29
#define TILE_XY(ptile)
Definition: tile.h:36
const QPixmap * get_nuke_explode_sprite(const struct tileset *t)
Return a sprite contining the single nuke graphic.
Definition: tilespec.cpp:3515
int tileset_hex_width(const struct tileset *t)
Return the hex_width of the current tileset.
Definition: tilespec.cpp:345
int tileset_unit_width(const struct tileset *t)
Return the unit tile width of the current tileset.
Definition: tilespec.cpp:426
int tileset_unit_height(const struct tileset *t)
Return the unit tile height of the current tileset.
Definition: tilespec.cpp:434
const std::vector< std::unique_ptr< freeciv::layer > > & tileset_get_layers(const struct tileset *t)
Definition: tilespec.cpp:3700
const QPixmap * get_unit_upkeep_sprite(const struct tileset *t, Output_type_id otype, const struct unit *punit, const int *upkeep_cost)
Return a sprite for the upkeep of the unit - to be shown as an overlay on the unit in the city suppor...
Definition: tilespec.cpp:3603
int tileset_full_tile_height(const struct tileset *t)
Return the full tile height of the current tileset.
Definition: tilespec.cpp:407
int tileset_citybar_offset_y(const struct tileset *t)
Return the offset from the origin of the city tile at which to place the city bar text.
Definition: tilespec.cpp:504
Q_LOGGING_CATEGORY(tileset_category, "freeciv.tileset")
Functions for handling the tilespec files which describe the files and contents of tilesets.
const std::vector< QPixmap * > & get_unit_explode_animation(const struct tileset *t)
Return a sprite_vector containing the animation sprites for a unit explosion.
Definition: tilespec.cpp:3505
bool tileset_is_isometric(const struct tileset *t)
Return whether the current tileset is isometric.
Definition: tilespec.cpp:336
int tileset_tile_height(const struct tileset *t)
Return the tile height of the current tileset.
Definition: tilespec.cpp:383
int tileset_hex_height(const struct tileset *t)
Return the hex_height of the current tileset.
Definition: tilespec.cpp:351
const QPixmap * get_unit_unhappy_sprite(const struct tileset *t, const struct unit *punit, int happy_cost)
Return a sprite for the unhappiness of the unit - to be shown as an overlay on the unit in the city s...
Definition: tilespec.cpp:3582
struct unit * get_drawable_unit(const struct tileset *t, const ::tile *ptile)
Find unit that we can display from given tile.
Definition: tilespec.cpp:3246
int tileset_tile_width(const struct tileset *t)
Return the tile width of the current tileset.
Definition: tilespec.cpp:371
int tileset_tilelabel_offset_y(const struct tileset *t)
Return the offset from the origin of the tile at which to place the label text.
Definition: tilespec.cpp:513
int max_trade_routes(const struct city *pcity)
Return current maximum number of trade routes city can have.
Definition: traderoutes.cpp:41
#define trade_partners_iterate_end
Definition: traderoutes.h:163
#define trade_partners_iterate(c, p)
Definition: traderoutes.h:153
bool unit_can_do_action(const struct unit *punit, const action_id act_id)
Return TRUE iff this unit can do the specified generalized (ruleset defined) action enabler controlle...
Definition: unit.cpp:309
bool unit_has_orders(const struct unit *punit)
Return TRUE iff the unit is following client-side orders.
Definition: unit.cpp:195
#define unit_tile(_pu)
Definition: unit.h:371
@ ORDER_ACTION_MOVE
Definition: unit.h:39
@ ORDER_MOVE
Definition: unit.h:33
@ ORDER_PERFORM_ACTION
Definition: unit.h:41
#define unit_owner(_pu)
Definition: unit.h:370
#define unit_list_iterate(unitlist, punit)
Definition: unitlist.h:25
#define unit_list_iterate_end
Definition: unitlist.h:27
static QColor link_mark_color(const struct link_mark *pmark)
Returns the color of the pointed mark.
void link_mark_restore(enum text_link_type type, int id)
Add a visible link for 1 turn.
static void base_canvas_to_map_pos(int *map_x, int *map_y, float canvas_x, float canvas_y)
Finds the map coordinates corresponding to pixel coordinates.
void put_nuke_mushroom_pixmaps(struct tile *ptile)
Animate the nuke explosion at map(x, y).
QHash< const struct tile *, struct gotoline_counter * > gotohash
static void normalize_gui_pos(const struct tileset *t, float *gui_x, float *gui_y)
Normalize (wrap) the GUI position.
bool tile_visible_mapcanvas(struct tile *ptile)
Return TRUE iff the given map position has a tile visible on the map canvas.
void link_marks_draw_all()
Draw all link marks.
static const int MAX_TRADE_ROUTE_DRAW_LINES
void mapdeco_init()
Called when we receive map dimensions.
void set_mapview_origin(float gui_x0, float gui_y0)
Change the mapview origin, clip it, and update everything.
void map_to_gui_vector(const struct tileset *t, float *gui_dx, float *gui_dy, int map_dx, int map_dy)
Translate from a cartesian system to the GUI system.
#define COLOR_MAPVIEW_TILELABEL
struct tile * canvas_pos_to_tile(float canvas_x, float canvas_y)
Finds the tile corresponding to pixel coordinates.
void mapdeco_set_crosshair(const struct tile *ptile, bool crosshair)
Marks the given tile as having a "crosshair" map decoration.
static int color_index
static int max_desc_width
static void link_mark_draw(const struct link_mark *pmark)
Print a link mark.
void init_mapcanvas_and_overview()
Sets up data for the mapview and overview.
struct view mapview
static void draw_trade_routes_for_city(const struct city *pcity_src)
Draw all trade routes for the given city.
static bool calc_mapview_origin(float *gui_x0, float *gui_y0)
Adjust mapview origin values.
void put_drawn_sprites(QPixmap *pcanvas, const QPoint &canvas_loc, const std::vector< drawn_sprite > &sprites, bool fog, bool city_unit)
Draw an array of drawn sprites onto the canvas.
void anim_delay(int milliseconds)
void get_city_mapview_production(const city *pcity, char *buffer, size_t buffer_len)
Find the mapview city production text for the given city, and place it into the buffer.
void get_mapview_scroll_pos(int *scroll_x, int *scroll_y)
Find the current scroll position (origin) of the mapview.
void mapdeco_clear_crosshairs()
Clears all previous set tile crosshair decorations.
bool mapdeco_is_highlight_set(const struct tile *ptile)
Return TRUE if the given tile is highlighted.
void get_mapview_scroll_window(float *xmin, float *ymin, float *xmax, float *ymax, int *xsize, int *ysize)
Return the scroll dimensions of the clipping window for the mapview window.
bool mapdeco_is_gotoline_set(const struct tile *ptile, enum direction8 dir, bool *safe)
Returns TRUE if a goto line should be drawn from the given tile in the given direction.
struct tile * get_center_tile_mapcanvas()
Finds the current center tile of the mapcanvas.
struct city * find_city_or_settler_near_tile(const struct tile *ptile, struct unit **punit)
Find the "best" city/settlers to associate with the selected tile.
void move_unit_map_canvas(struct unit *punit, struct tile *src_tile, int dx, int dy)
Animates punit's "smooth" move from (x0, y0) to (x0+dx, y0+dy).
void get_city_mapview_name_and_growth(const city *pcity, char *name_buffer, size_t name_buffer_len, char *growth_buffer, size_t growth_buffer_len, enum color_std *growth_color, enum color_std *production_color)
Fill the two buffers which information about the city which is shown below it.
static void link_mark_destroy(struct link_mark *pmark)
Remove a link mark.
void refresh_tile_mapcanvas(const tile *ptile, bool full_refresh)
Refreshes a single tile on the map canvas.
void mapdeco_set_gotoroute(const struct unit *punit)
Set the map decorations for the given unit's goto route.
static void gui_to_map_pos(const struct tileset *t, int *map_x, int *map_y, float gui_x, float gui_y)
Translate from gui to map coordinate systems.
static void draw_trade_routes()
Draw trade routes between cities as lines on the main map canvas.
void show_city_descriptions(int canvas_base_x, int canvas_base_y, int width_base, int height_base)
Show descriptions for all cities visible on the map canvas.
void mapdeco_add_gotoline(const struct tile *ptile, enum direction8 dir, bool safe)
Add a goto line from the given tile 'ptile' in the direction 'dir'.
void map_canvas_resized(int width, int height)
Called if the map in the GUI is resized.
void free_mapcanvas_and_overview()
Frees resources allocated for mapview and overview.
static bool can_do_cached_drawing()
Returns TRUE if cached drawing is possible.
void put_one_element(QPixmap *pcanvas, const std::unique_ptr< freeciv::layer > &layer, const struct tile *ptile, const struct tile_edge *pedge, const struct tile_corner *pcorner, const struct unit *punit, const QPoint &canvas_loc)
Draw one layer of a tile, edge, corner, unit, and/or city onto the canvas at the given position.
static QSize max_label_size
static QSize show_tile_label(QPixmap *pcanvas, int canvas_x, int canvas_y, const tile *ptile)
Draw a label for the given tile.
void mapdeco_free()
Free all memory used for map decorations.
#define link_marks_iterate(pmark)
void draw_segment(const tile *src_tile, enum direction8 dir, bool safe)
Draw a goto line at the given location and direction.
void put_terrain(struct tile *ptile, QPixmap *pcanvas, const QPoint &canvas_loc)
Draw the given tile terrain onto the canvas store at the given location.
void link_marks_free()
Free the link marks.
void toggle_city_color(struct city *pcity)
Toggle the city color.
void update_map_canvas(int canvas_x, int canvas_y, int width, int height)
Update (refresh) the map canvas starting at the given tile (in map coordinates) and with the given di...
Q_GLOBAL_STATIC(QElapsedTimer, anim_timer)
#define link_marks_iterate_end
void link_marks_clear_all()
Clear all visible links.
static void base_set_mapview_origin(float gui_x0, float gui_y0)
Move the GUI origin to the given normalized, clipped origin.
void animate_unit_explosion(const tile *location)
Draws an explosion animation on the given sprite.
void refresh_city_mapcanvas(struct city *pcity, struct tile *ptile, bool full_refresh)
Refreshes a single city on the map canvas.
void update_city_description(struct city *pcity)
Update the city description for the given city.
void put_unit_city_overlays(const unit *punit, QPixmap *pcanvas, int canvas_x, int canvas_y, const int *upkeep_cost, int happy_cost)
Draw food, gold, and shield upkeep values on the unit.
static struct link_mark * link_mark_new(enum text_link_type type, int id, int turns)
Create a new link mark.
static int trade_route_to_canvas_lines(const struct tile *ptile1, const struct tile *ptile2, struct trade_route_line *lines)
Depending on where ptile1 and ptile2 are on the map canvas, a trade route line may need to be drawn a...
void map_to_gui_pos(const struct tileset *t, float *gui_x, float *gui_y, int map_x, int map_y)
Translate from map to gui coordinate systems.
void gui_distance_vector(const struct tileset *t, float *gui_dx, float *gui_dy, float gui_x0, float gui_y0, float gui_x1, float gui_y1)
Find the vector with minimum "real" distance between two GUI positions.
static struct tile * link_mark_tile(const struct link_mark *pmark)
Returns the location of the pointed mark.
void toggle_unit_color(struct unit *punit)
Toggle the unit color.
void refresh_unit_mapcanvas(struct unit *punit, struct tile *ptile, bool full_refresh)
Refreshes a single unit on the map canvas.
static int max_desc_height
void link_mark_add_new(enum text_link_type type, int id)
Add a visible link for 2 turns.
static void draw_trade_route_line(const struct tile *ptile1, const struct tile *ptile2, enum color_std color)
Draw a colored trade route line from one tile to another.
void link_marks_decrease_turn_counters()
Clear all visible links.
bool tile_to_canvas_pos(float *canvas_x, float *canvas_y, const tile *ptile)
Finds the canvas coordinates for a map position.
void update_tile_label(const tile *ptile)
Update the label for the given tile.
void link_marks_init()
Initialize the link marks.
static struct link_mark_list * link_marks
void decrease_unit_hp_smooth(struct unit *punit0, int hp0, struct unit *punit1, int hp1)
This function is called to decrease a unit's HP smoothly in battle when combat_animation is turned on...
void mapdeco_clear_gotoroutes()
Clear all goto line map decorations and queues mapview updates for the affected tiles.
bool tile_visible_and_not_on_border_mapcanvas(struct tile *ptile)
Return TRUE iff the given map position has a tile visible within the interior of the map canvas.
void get_city_mapview_trade_routes(const city *pcity, char *trade_routes_buffer, size_t trade_routes_buffer_len, enum color_std *pcolor)
Find the mapview city trade routes text for the given city, and place it into the buffer.
static struct link_mark * link_mark_find(enum text_link_type type, int id)
Find a link mark in the list.
enum topo_comp_lvl tileset_map_topo_compatible(int topology_id, struct tileset *tset)
Are the topology and tileset compatible?
static void append_city_buycost_string(const struct city *pcity, char *buffer, int buffer_len)
Append the buy cost of the current production of the given city to the already nullptr-terminated buf...
bool mapdeco_is_crosshair_set(const struct tile *ptile)
Returns TRUE if there is a "crosshair" decoration set at the given tile.
static void put_one_tile(QPixmap *pcanvas, const std::unique_ptr< freeciv::layer > &layer, const tile *ptile, const QPoint &canvas_loc)
Draw some or all of a tile onto the canvas.
void show_tile_labels(int canvas_base_x, int canvas_base_y, int width_base, int height_base)
Show labels for all tiles visible on the map canvas.
void update_map_canvas_visible()
Schedules an update of (only) the visible part of the map at the next unqueue_mapview_update().
struct tile * canvas_pos_to_nearest_tile(float canvas_x, float canvas_y)
Finds the tile corresponding to pixel coordinates.
std::map< freeciv::map_updates_handler::update_type, QRectF > update_rects()
Calculates the area covered by each update type.
void put_unit(const struct unit *punit, QPixmap *pcanvas, const QPoint &canvas_loc)
Draw the given unit onto the canvas store at the given location.
void unqueue_mapview_updates()
See comment in update_map_canvas_visible().
#define GOTO_WIDTH
topo_comp_lvl
@ TOPO_COMPATIBLE
@ TOPO_INCOMP_HARD
@ TOPO_INCOMP_SOFT
static void swap(struct reqtree *tree, int layer, int order1, int order2)
Swap positions of two nodes on the same layer.