Freeciv21
Develop your civilization from humble roots to a global empire
overview_common.cpp
Go to the documentation of this file.
1 /*
2 Copyright (c) 1996-2021 Freeciv21 and Freeciv contributors. This file is
3  /\/\ part of Freeciv21. Freeciv21 is free software: you can
4  \_\ _..._ redistribute it and/or modify it under the terms of the
5  (" )(_..._) GNU General Public License as published by the Free
6  ^^ // \\ Software Foundation, either version 3 of the License,
7  or (at your option) any later version. You should have
8 received a copy of the GNU General Public License along with Freeciv21.
9  If not, see https://www.gnu.org/licenses/.
10  */
11 
12 #include <cmath> // floor
13 
14 // utility
15 #include "log.h"
16 
17 // client
18 #include "client_main.h" // can_client_change_view()
19 #include "climap.h"
20 #include "control.h"
21 #include "mapview_g.h"
22 #include "options.h"
23 
24 #include "overview_common.h"
25 
26 // Qt
27 #include <QPainter>
28 #include <QPixmap>
29 #include <qnamespace.h>
30 
32 
33 #if 0
34 struct overview overview = {
35  // These are the default values. All others are zeroed automatically.
36  .fog = TRUE,
37  .layers = {[OLAYER_BACKGROUND] = TRUE,
38  [OLAYER_UNITS] = TRUE,
39  [OLAYER_CITIES] = TRUE,
40  [OLAYER_BORDERS_ON_OCEAN] = TRUE}
41 };
42 #endif
43 
44 namespace /* anonymous */ {
45 
46 /*
47  * Set to TRUE if the backing store is more recent than the version
48  * drawn into overview.window.
49  */
50 static bool overview_dirty = false;
51 
52 // Tracks map updates for the overview
53 static std::unique_ptr<freeciv::map_updates_handler> updates = nullptr;
54 
55 } // anonymous namespace
56 
57 static void overview_update_tile(const tile *ptile);
58 
64 void gui_to_natural_pos(const struct tileset *t, double *ntl_x,
65  double *ntl_y, int gui_x, int gui_y)
66 {
67  const double gui_xd = gui_x, gui_yd = gui_y;
68  const double W = tileset_tile_width(t);
69  const double H = tileset_tile_height(t);
70  double map_x, map_y;
71 
72  /* First convert to map positions. This ignores hex conversions; we're
73  * not looking for an exact tile. */
74  if (tileset_is_isometric(t)) {
75  // Includes hex cases.
76  map_x = (gui_xd * H + gui_yd * W) / (W * H);
77  map_y = (gui_yd * W - gui_xd * H) / (W * H);
78  } else {
79  map_x = gui_xd / W;
80  map_y = gui_yd / H;
81  }
82 
83  /* Now convert to natural positions. Note this assumes the macro form
84  * of the conversion will work with floating-point values. */
85  MAP_TO_NATURAL_POS(ntl_x, ntl_y, map_x, map_y);
86 }
87 
91 void gui_to_overview_pos(const struct tileset *t, int *ovr_x, int *ovr_y,
92  int gui_x, int gui_y)
93 {
94  double ntl_x, ntl_y;
95 
96  gui_to_natural_pos(t, &ntl_x, &ntl_y, gui_x, gui_y);
97 
98  // Now convert straight to overview coordinates.
99  *ovr_x =
100  floor((ntl_x - gui_options->overview.map_x0) * OVERVIEW_TILE_SIZE);
101  *ovr_y =
102  floor((ntl_y - gui_options->overview.map_y0) * OVERVIEW_TILE_SIZE);
103 
104  // Now do additional adjustments.
105  if (current_topo_has_flag(TF_WRAPX)) {
106  *ovr_x = FC_WRAP(*ovr_x, NATURAL_WIDTH * OVERVIEW_TILE_SIZE);
107  } else {
108  if (MAP_IS_ISOMETRIC) {
109  /* HACK: For iso-maps that don't wrap in the X direction we clip
110  * a half-tile off of the left and right of the overview. This
111  * means some tiles only are halfway shown. However it means we
112  * don't show any unreal tiles, which we'd otherwise be doing. The
113  * rest of the code can't handle unreal tiles in the overview. */
114  *ovr_x -= OVERVIEW_TILE_SIZE;
115  }
116  }
117  if (current_topo_has_flag(TF_WRAPY)) {
118  *ovr_y = FC_WRAP(*ovr_y, NATURAL_HEIGHT * OVERVIEW_TILE_SIZE);
119  }
120 }
121 
125 static QColor overview_tile_color(const tile *ptile)
126 {
128  struct city *pcity = tile_city(ptile);
129 
130  if (pcity) {
131  if (nullptr == client.conn.playing
132  || city_owner(pcity) == client.conn.playing) {
133  return get_color(tileset, COLOR_OVERVIEW_MY_CITY);
134  } else if (pplayers_allied(city_owner(pcity), client.conn.playing)) {
135  // Includes teams.
136  return get_color(tileset, COLOR_OVERVIEW_ALLIED_CITY);
137  } else {
138  return get_color(tileset, COLOR_OVERVIEW_ENEMY_CITY);
139  }
140  }
141  }
143  struct unit *punit = find_visible_unit(ptile);
144 
145  if (punit) {
146  if (nullptr == client.conn.playing
147  || unit_owner(punit) == client.conn.playing) {
148  return get_color(tileset, COLOR_OVERVIEW_MY_UNIT);
149  } else if (pplayers_allied(unit_owner(punit), client.conn.playing)) {
150  // Includes teams.
151  return get_color(tileset, COLOR_OVERVIEW_ALLIED_UNIT);
152  } else {
153  return get_color(tileset, COLOR_OVERVIEW_ENEMY_UNIT);
154  }
155  }
156  }
158  struct player *owner = tile_owner(ptile);
159 
160  if (owner) {
162  return get_player_color(tileset, owner);
163  } else if (!is_ocean_tile(ptile)) {
164  return get_player_color(tileset, owner);
165  }
166  }
167  }
169  && tile_terrain(ptile) != T_UNKNOWN) {
170  return get_terrain_color(tileset, tile_terrain(ptile));
171  }
173  && tile_terrain(ptile) != T_UNKNOWN) {
174  if (terrain_has_flag(tile_terrain(ptile), TER_FROZEN)) {
175  return get_color(tileset, COLOR_OVERVIEW_FROZEN);
176  } else {
177  if (is_ocean_tile(ptile)) {
178  return get_color(tileset, COLOR_OVERVIEW_OCEAN);
179  } else {
180  return get_color(tileset, COLOR_OVERVIEW_LAND);
181  }
182  }
183  }
184 
185  return get_color(tileset, COLOR_OVERVIEW_UNKNOWN);
186 }
187 
192 static void redraw_overview()
193 {
194  int i, x[4], y[4];
195 
196  if (!gui_options->overview.map) {
197  return;
198  }
199 
200  {
201  QPixmap *src = gui_options->overview.map;
202  QPixmap *dst = gui_options->overview.window;
205  int ix = gui_options->overview.width - x_left;
206  int iy = gui_options->overview.height - y_top;
207 
208  QPainter p(dst);
209  p.drawPixmap(ix, iy, *src, 0, 0, x_left, y_top);
210  p.drawPixmap(ix, 0, *src, 0, y_top, x_left, iy);
211  p.drawPixmap(0, iy, *src, x_left, 0, ix, y_top);
212  p.drawPixmap(0, 0, *src, x_left, y_top, ix, iy);
213  p.end();
214  }
215 
218  mapview.gui_y0);
221  gui_to_overview_pos(tileset, &x[3], &y[3], mapview.gui_x0,
223 
224  QPainter p(gui_options->overview.window);
225  p.setPen(QPen(get_color(tileset, COLOR_OVERVIEW_VIEWRECT), 2));
226  for (i = 0; i < 4; i++) {
227  int src_x = x[i];
228  int src_y = y[i];
229  int dst_x = x[(i + 1) % 4];
230  int dst_y = y[(i + 1) % 4];
231 
232  p.drawLine(src_x, src_y, dst_x, dst_y);
233  }
234  p.end();
235 
236  update_minimap();
237 
238  overview_dirty = false;
239 }
240 
244 static void dirty_overview() { overview_dirty = true; }
245 
250 {
251  /* Currently this function is called from mapview_common. However it
252  * should be made static eventually. */
253  if (overview_dirty) {
254  redraw_overview();
255  }
256 }
257 
261 void overview_to_map_pos(int *map_x, int *map_y, int overview_x,
262  int overview_y)
263 {
264  int ntl_x = overview_x / OVERVIEW_TILE_SIZE + gui_options->overview.map_x0;
265  int ntl_y = overview_y / OVERVIEW_TILE_SIZE + gui_options->overview.map_y0;
266 
267  if (MAP_IS_ISOMETRIC && !current_topo_has_flag(TF_WRAPX)) {
268  // Clip half tile left and right.
269  // See comment in calculate_overview_dimensions().
270  ntl_x++;
271  }
272 
273  NATURAL_TO_MAP_POS(map_x, map_y, ntl_x, ntl_y);
274  // All positions on the overview should be valid.
275  fc_assert(normalize_map_pos(&(wld.map), map_x, map_y));
276 }
277 
282 {
283  if (!can_client_change_view()) {
284  return;
285  }
286  whole_map_iterate(&(wld.map), ptile) { overview_update_tile(ptile); }
288  redraw_overview();
289 }
290 
297 static void put_overview_tile_area(QPixmap *pcanvas, const tile *ptile,
298  int x, int y, int w, int h)
299 {
300  QPainter p(pcanvas);
301  p.fillRect(x, y, w, h, overview_tile_color(ptile));
304  p.fillRect(x, y, w, h, QColor(0, 0, 0, 128));
305  }
306  p.end();
307 }
308 
312 static void overview_update_tile(const tile *ptile)
313 {
314  if (!can_client_change_view()) {
315  return;
316  }
317 
318  int tile_x, tile_y;
319 
320  /* Base overview positions are just like natural positions, but scaled to
321  * the overview tile dimensions. */
322  index_to_map_pos(&tile_x, &tile_y, tile_index(ptile));
323  do_in_natural_pos(ntl_x, ntl_y, tile_x, tile_y)
324  {
325  int overview_y = ntl_y * OVERVIEW_TILE_SIZE;
326  int overview_x = ntl_x * OVERVIEW_TILE_SIZE;
327 
328  if (MAP_IS_ISOMETRIC) {
329  if (current_topo_has_flag(TF_WRAPX)) {
330  if (overview_x > gui_options->overview.width - OVERVIEW_TILE_WIDTH) {
331  /* This tile is shown half on the left and half on the right
332  * side of the overview. So we have to draw it in two parts. */
334  overview_x - gui_options->overview.width,
335  overview_y, OVERVIEW_TILE_WIDTH,
337  }
338  } else {
339  /* Clip half tile left and right.
340  * See comment in calculate_overview_dimensions(). */
341  overview_x -= OVERVIEW_TILE_SIZE;
342  }
343  }
344 
345  put_overview_tile_area(gui_options->overview.map, ptile, overview_x,
346  overview_y, OVERVIEW_TILE_WIDTH,
348 
349  dirty_overview();
350  }
352 }
353 
358 {
359  int w, h;
360  int xfact = MAP_IS_ISOMETRIC ? 2 : 1;
361 
362  static int recursion = 0; // Just to be safe.
363 
364  // Clip half tile left and right.
365  /* HACK: For iso-maps that don't wrap in the X direction we clip
366  * a half-tile off of the left and right of the overview. This
367  * means some tiles only are halfway shown. However it means we
368  * don't show any unreal tiles, which we'd otherwise be doing. The
369  * rest of the code can't handle unreal tiles in the overview. */
370  int shift =
371  (MAP_IS_ISOMETRIC && !current_topo_has_flag(TF_WRAPX)) ? -1 : 0;
372 
373  if (recursion > 0 || wld.map.xsize <= 0 || wld.map.ysize <= 0) {
374  return;
375  }
376  recursion++;
377  w = 0;
378  h = 0;
379  /* Set the scale of the overview map. This attempts to limit the
380  * overview to the size of the area available.
381  *
382  * It rounds up since this gives good results with the default settings.
383  * It may need tweaking if the panel resizes itself. */
384  OVERVIEW_TILE_SIZE = MIN((w - 1) / (wld.map.xsize * xfact) + 1,
385  (h - 1) / wld.map.ysize + 1);
387 
388  log_debug("Map size %d,%d - area size %d,%d - scale: %d", wld.map.xsize,
390 
394 
395  if (gui_options->overview.map) {
396  delete gui_options->overview.map;
397  delete gui_options->overview.window;
398  }
403  gui_options->overview.map->fill(
404  get_color(tileset, COLOR_OVERVIEW_UNKNOWN));
405 
406  update_minimap();
407  if (can_client_change_view()) {
409  }
410 
411  recursion--;
412 }
413 
414 namespace /* anonymous */ {
418 void unqueue_overview_updates()
419 {
420  if (updates->full()) {
422  } else {
423  for (const auto [tile, _] : updates->list()) {
425  }
426  }
427  updates->clear();
428 }
429 } // anonymous namespace
430 
435 {
436  updates = std::make_unique<freeciv::map_updates_handler>();
437  updates->connect(
439  updates.get(), unqueue_overview_updates, Qt::QueuedConnection);
440 }
441 
446 {
447  if (gui_options->overview.map) {
448  delete gui_options->overview.map;
449  delete gui_options->overview.window;
450  gui_options->overview.map = nullptr;
451  gui_options->overview.window = nullptr;
452  }
453  updates = nullptr;
454 }
455 
460 {
461  Q_UNUSED(option)
462  /* This is called once for each option changed so it is slower than
463  * necessary. If this becomes a problem it could be switched to use a
464  * queue system like the mapview drawing code does. */
466 }
struct player * city_owner(const struct city *pcity)
Return the owner of the city.
Definition: city.cpp:1083
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
QColor get_color(const struct tileset *t, enum color_std stdcolor)
Return a pointer to the given "standard" color.
QColor get_terrain_color(const struct tileset *t, const struct terrain *pterrain)
Return a pointer to the given "terrain" color.
QColor get_player_color(const struct tileset *t, const struct player *pplayer)
Return the color of the player.
unit * find_visible_unit(const ::tile *ptile)
Return a pointer to a visible unit, if there is one.
Definition: control.cpp:749
#define _(String)
Definition: fcintl.h:50
struct world wld
Definition: game.cpp:48
#define fc_assert(condition)
Definition: log.h:89
#define log_debug(message,...)
Definition: log.h:65
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 NATURAL_TO_MAP_POS(pmap_x, pmap_y, nat_x, nat_y)
Definition: map.h:129
#define do_in_natural_pos(ntl_x, ntl_y, map_x, map_y)
Definition: map.h:143
#define NATURAL_HEIGHT
Definition: map.h:158
#define MAP_IS_ISOMETRIC
Definition: map.h:32
#define MAP_TO_NATURAL_POS(pnat_x, pnat_y, map_x, map_y)
Definition: map.h:134
#define whole_map_iterate(_map, _tile)
Definition: map.h:473
#define NATURAL_WIDTH
Definition: map.h:157
#define do_in_natural_pos_end
Definition: map.h:150
#define whole_map_iterate_end
Definition: map.h:480
#define index_to_map_pos(pmap_x, pmap_y, mindex)
Definition: map.h:164
void update_minimap(void)
Return a canvas that is the overview window.
Definition: minimap.cpp:256
client_options * gui_options
Definition: options.cpp:74
@ OLAYER_BORDERS
Definition: options.h:25
@ OLAYER_BACKGROUND
Definition: options.h:23
@ OLAYER_CITIES
Definition: options.h:28
@ OLAYER_BORDERS_ON_OCEAN
Definition: options.h:26
@ OLAYER_UNITS
Definition: options.h:27
@ OLAYER_RELIEF
Definition: options.h:24
void overview_init()
Allocates overview resources.
void overview_to_map_pos(int *map_x, int *map_y, int overview_x, int overview_y)
Finds the map coordinates for a given overview (canvas) position.
static void redraw_overview()
Copies the overview image from the backing store to the window and draws the viewrect on top of it.
void gui_to_overview_pos(const struct tileset *t, int *ovr_x, int *ovr_y, int gui_x, int gui_y)
Translate from gui to overview coordinate systems.
void overview_redraw_callback(struct option *option)
Callback to be called when an overview option is changed.
void refresh_overview_canvas()
Redraw the entire backing store for the overview minimap.
void flush_dirty_overview()
Redraw the overview if it is "dirty".
static void overview_update_tile(const tile *ptile)
Redraw the given map position in the overview canvas.
void overview_free()
Free overview resources.
static void put_overview_tile_area(QPixmap *pcanvas, const tile *ptile, int x, int y, int w, int h)
Draws the color for this tile onto the given rectangle of the canvas.
int OVERVIEW_TILE_SIZE
void calculate_overview_dimensions()
Called if the map size is know or changes.
void gui_to_natural_pos(const struct tileset *t, double *ntl_x, double *ntl_y, int gui_x, int gui_y)
Translate from gui to natural coordinate systems.
static void dirty_overview()
Mark the overview as "dirty" so that it will be redrawn soon.
static QColor overview_tile_color(const tile *ptile)
Return color for overview map tile.
#define OVERVIEW_TILE_WIDTH
#define OVERVIEW_TILE_HEIGHT
bool pplayers_allied(const struct player *pplayer, const struct player *pplayer2)
Returns true iff players are allied.
Definition: player.cpp:1334
#define FC_WRAP(value, range)
Definition: shared.h:59
#define MIN(x, y)
Definition: shared.h:49
#define MAX(x, y)
Definition: shared.h:48
static int recursion[AIT_LAST]
Definition: srv_log.cpp:35
Definition: city.h:291
int xsize
Definition: map_types.h:73
int ysize
Definition: map_types.h:73
struct connection conn
Definition: client_main.h:89
struct overview overview
Definition: options.h:179
struct player * playing
Definition: connection.h:142
The base class for options.
Definition: options.cpp:209
double map_y0
Definition: options.h:35
QPixmap * window
Definition: options.h:42
double map_x0
Definition: options.h:35
QPixmap * map
Definition: options.h:39
bool fog
Definition: options.h:44
bool layers[OLAYER_COUNT]
Definition: options.h:45
int width
Definition: options.h:36
int height
Definition: options.h:36
Definition: player.h:231
Definition: tile.h:42
Definition: unit.h:134
int height
float gui_x0
float gui_y0
int width
struct civ_map map
Definition: world_object.h:21
#define T_UNKNOWN
Definition: terrain.h:51
#define is_ocean_tile(ptile)
Definition: terrain.h:279
#define terrain_has_flag(terr, flag)
Definition: terrain.h:260
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
@ TILE_KNOWN_UNSEEN
Definition: tile.h:30
#define tile_terrain(_tile)
Definition: tile.h:93
#define tile_owner(_tile)
Definition: tile.h:78
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_tile_width(const struct tileset *t)
Return the tile width of the current tileset.
Definition: tilespec.cpp:371
#define unit_owner(_pu)
Definition: unit.h:370
struct view mapview