Make multiplayer ruleset default to iso topology, to match client default.
[freeciv.git] / client / overview_common.c
blob9b673c21f2781f51ed5e71abbeabd3058c64ef0a
1 /**********************************************************************
2 Freeciv - Copyright (C) 1996-2005 - Freeciv Development Team
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12 ***********************************************************************/
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
18 #include <math.h> /* floor */
20 /* utility */
21 #include "log.h"
23 /* client */
24 #include "client_main.h" /* can_client_change_view() */
25 #include "climap.h"
26 #include "control.h"
27 #include "options.h"
28 #include "overview_common.h"
30 #include "mapview_g.h"
32 int OVERVIEW_TILE_SIZE = 2;
33 struct overview overview = {
34 /* These are the default values. All others are zeroed automatically. */
35 .fog = TRUE,
36 .layers = {[OLAYER_BACKGROUND] = TRUE,
37 [OLAYER_UNITS] = TRUE,
38 [OLAYER_CITIES] = TRUE,
39 [OLAYER_BORDERS_ON_OCEAN] = TRUE}
43 * Set to TRUE if the backing store is more recent than the version
44 * drawn into overview.window.
46 static bool overview_dirty = FALSE;
48 /****************************************************************************
49 Translate from gui to natural coordinate systems. This provides natural
50 coordinates as a floating-point value so there is no loss of information
51 in the resulting values.
52 ****************************************************************************/
53 static void gui_to_natural_pos(const struct tileset *t,
54 double *ntl_x, double *ntl_y,
55 int gui_x, int gui_y)
57 const double gui_xd = gui_x, gui_yd = gui_y;
58 const double W = tileset_tile_width(t), H = tileset_tile_height(t);
59 double map_x, map_y;
61 /* First convert to map positions. This ignores hex conversions; we're
62 * not looking for an exact tile. */
63 if (tileset_is_isometric(t)) {
64 /* Includes hex cases. */
65 map_x = (gui_xd * H + gui_yd * W) / (W * H);
66 map_y = (gui_yd * W - gui_xd * H) / (W * H);
67 } else {
68 map_x = gui_xd / W;
69 map_y = gui_yd / H;
72 /* Now convert to natural positions. Note this assumes the macro form
73 * of the conversion will work with floating-point values. */
74 MAP_TO_NATURAL_POS(ntl_x, ntl_y, map_x, map_y);
78 /****************************************************************************
79 Translate from gui to overview coordinate systems.
80 ****************************************************************************/
81 static void gui_to_overview_pos(const struct tileset *t,
82 int *ovr_x, int *ovr_y,
83 int gui_x, int gui_y)
85 double ntl_x, ntl_y;
87 gui_to_natural_pos(t, &ntl_x, &ntl_y, gui_x, gui_y);
89 /* Now convert straight to overview coordinates. */
90 *ovr_x = floor((ntl_x - (double)overview.map_x0) * OVERVIEW_TILE_SIZE);
91 *ovr_y = floor((ntl_y - (double)overview.map_y0) * OVERVIEW_TILE_SIZE);
93 /* Now do additional adjustments. See map_to_overview_pos(). */
94 if (current_topo_has_flag(TF_WRAPX)) {
95 *ovr_x = FC_WRAP(*ovr_x, NATURAL_WIDTH * OVERVIEW_TILE_SIZE);
96 } else {
97 if (MAP_IS_ISOMETRIC) {
98 /* HACK: For iso-maps that don't wrap in the X direction we clip
99 * a half-tile off of the left and right of the overview. This
100 * means some tiles only are halfway shown. However it means we
101 * don't show any unreal tiles, which we'd otherwise be doing. The
102 * rest of the code can't handle unreal tiles in the overview. */
103 *ovr_x -= OVERVIEW_TILE_SIZE;
106 if (current_topo_has_flag(TF_WRAPY)) {
107 *ovr_y = FC_WRAP(*ovr_y, NATURAL_HEIGHT * OVERVIEW_TILE_SIZE);
111 /****************************************************************************
112 Return color for overview map tile.
113 ****************************************************************************/
114 static struct color *overview_tile_color(struct tile *ptile)
116 if (overview.layers[OLAYER_CITIES]) {
117 struct city *pcity = tile_city(ptile);
119 if (pcity) {
120 if (NULL == client.conn.playing
121 || city_owner(pcity) == client.conn.playing) {
122 return get_color(tileset, COLOR_OVERVIEW_MY_CITY);
123 } else if (pplayers_allied(city_owner(pcity), client.conn.playing)) {
124 /* Includes teams. */
125 return get_color(tileset, COLOR_OVERVIEW_ALLIED_CITY);
126 } else {
127 return get_color(tileset, COLOR_OVERVIEW_ENEMY_CITY);
131 if (overview.layers[OLAYER_UNITS]) {
132 struct unit *punit = find_visible_unit(ptile);
134 if (punit) {
135 if (NULL == client.conn.playing
136 || unit_owner(punit) == client.conn.playing) {
137 return get_color(tileset, COLOR_OVERVIEW_MY_UNIT);
138 } else if (pplayers_allied(unit_owner(punit), client.conn.playing)) {
139 /* Includes teams. */
140 return get_color(tileset, COLOR_OVERVIEW_ALLIED_UNIT);
141 } else {
142 return get_color(tileset, COLOR_OVERVIEW_ENEMY_UNIT);
146 if (overview.layers[OLAYER_BORDERS]) {
147 struct player *owner = tile_owner(ptile);
149 if (owner) {
150 if (overview.layers[OLAYER_BORDERS_ON_OCEAN]) {
151 return get_player_color(tileset, owner);
152 } else if (!is_ocean_tile(ptile)) {
153 return get_player_color(tileset, owner);
157 if (overview.layers[OLAYER_RELIEF] && tile_terrain(ptile) != T_UNKNOWN) {
158 return get_terrain_color(tileset, tile_terrain(ptile));
160 if (overview.layers[OLAYER_BACKGROUND] && tile_terrain(ptile) != T_UNKNOWN) {
161 if (is_ocean_tile(ptile)) {
162 return get_color(tileset, COLOR_OVERVIEW_OCEAN);
163 } else {
164 return get_color(tileset, COLOR_OVERVIEW_LAND);
168 return get_color(tileset, COLOR_OVERVIEW_UNKNOWN);
171 /**************************************************************************
172 Copies the current centred image + viewrect unchanged to the client's
173 overview window (for expose events etc).
174 **************************************************************************/
175 void refresh_overview_from_canvas(void)
177 struct canvas *dest = get_overview_window();
178 if (!dest) {
179 return;
181 canvas_copy(dest, overview.window,
182 0, 0, 0, 0, overview.width, overview.height);
185 /**************************************************************************
186 Copies the overview image from the backing store to the window and
187 draws the viewrect on top of it.
188 **************************************************************************/
189 static void redraw_overview(void)
191 int i, x[4], y[4];
193 if (!overview.map) {
194 return;
198 struct canvas *src = overview.map, *dst = overview.window;
199 int x = overview.map_x0 * OVERVIEW_TILE_SIZE;
200 int y = overview.map_y0 * OVERVIEW_TILE_SIZE;
201 int ix = overview.width - x;
202 int iy = overview.height - y;
204 canvas_copy(dst, src, 0, 0, ix, iy, x, y);
205 canvas_copy(dst, src, 0, y, ix, 0, x, iy);
206 canvas_copy(dst, src, x, 0, 0, iy, ix, y);
207 canvas_copy(dst, src, x, y, 0, 0, ix, iy);
210 gui_to_overview_pos(tileset, &x[0], &y[0],
211 mapview.gui_x0, mapview.gui_y0);
212 gui_to_overview_pos(tileset, &x[1], &y[1],
213 mapview.gui_x0 + mapview.width, mapview.gui_y0);
214 gui_to_overview_pos(tileset, &x[2], &y[2],
215 mapview.gui_x0 + mapview.width,
216 mapview.gui_y0 + mapview.height);
217 gui_to_overview_pos(tileset, &x[3], &y[3],
218 mapview.gui_x0, mapview.gui_y0 + mapview.height);
220 for (i = 0; i < 4; i++) {
221 int src_x = x[i];
222 int src_y = y[i];
223 int dst_x = x[(i + 1) % 4];
224 int dst_y = y[(i + 1) % 4];
226 canvas_put_line(overview.window,
227 get_color(tileset, COLOR_OVERVIEW_VIEWRECT),
228 LINE_NORMAL,
229 src_x, src_y, dst_x - src_x, dst_y - src_y);
232 refresh_overview_from_canvas();
234 overview_dirty = FALSE;
237 /****************************************************************************
238 Mark the overview as "dirty" so that it will be redrawn soon.
239 ****************************************************************************/
240 static void dirty_overview(void)
242 overview_dirty = TRUE;
245 /****************************************************************************
246 Redraw the overview if it is "dirty".
247 ****************************************************************************/
248 void flush_dirty_overview(void)
250 /* Currently this function is called from mapview_common. However it
251 * should be made static eventually. */
252 if (overview_dirty) {
253 redraw_overview();
257 /****************************************************************************
258 Equivalent to FC_WRAP, but it works for doubles.
259 ****************************************************************************/
260 static double wrap_double(double value, double wrap)
262 while (value < 0) {
263 value += wrap;
265 while (value >= wrap) {
266 value -= wrap;
268 return value;
271 /**************************************************************************
272 Center the overview around the mapview.
273 **************************************************************************/
274 void center_tile_overviewcanvas(void)
276 double ntl_x, ntl_y;
277 int ox, oy;
279 gui_to_natural_pos(tileset, &ntl_x, &ntl_y,
280 mapview.gui_x0 + mapview.width / 2,
281 mapview.gui_y0 + mapview.height / 2);
283 /* NOTE: this embeds the map wrapping in the overview code. This is
284 * basically necessary for the overview to be efficiently
285 * updated. */
286 if (current_topo_has_flag(TF_WRAPX)) {
287 overview.map_x0 = wrap_double(ntl_x - (double)NATURAL_WIDTH / 2.0,
288 NATURAL_WIDTH);
289 } else {
290 overview.map_x0 = 0;
292 if (current_topo_has_flag(TF_WRAPY)) {
293 overview.map_y0 = wrap_double(ntl_y - (double)NATURAL_HEIGHT / 2.0,
294 NATURAL_HEIGHT);
295 } else {
296 overview.map_y0 = 0;
299 redraw_overview();
301 gui_to_overview_pos(tileset, &ox, &oy,
302 mapview.gui_x0, mapview.gui_y0);
303 update_overview_scroll_window_pos(ox, oy);
306 /**************************************************************************
307 Finds the overview (canvas) coordinates for a given map position.
308 **************************************************************************/
309 void map_to_overview_pos(int *overview_x, int *overview_y,
310 int map_x, int map_y)
312 /* The map position may not be normal, for instance when the mapview
313 * origin is not a normal position.
315 * NOTE: this embeds the map wrapping in the overview code. */
316 do_in_natural_pos(ntl_x, ntl_y, map_x, map_y) {
317 int ovr_x = ntl_x - overview.map_x0, ovr_y = ntl_y - overview.map_y0;
319 if (current_topo_has_flag(TF_WRAPX)) {
320 ovr_x = FC_WRAP(ovr_x, NATURAL_WIDTH);
321 } else {
322 if (MAP_IS_ISOMETRIC) {
323 /* HACK: For iso-maps that don't wrap in the X direction we clip
324 * a half-tile off of the left and right of the overview. This
325 * means some tiles only are halfway shown. However it means we
326 * don't show any unreal tiles, which we'd otherwise be doing. The
327 * rest of the code can't handle unreal tiles in the overview. */
328 ovr_x--;
331 if (current_topo_has_flag(TF_WRAPY)) {
332 ovr_y = FC_WRAP(ovr_y, NATURAL_HEIGHT);
334 *overview_x = OVERVIEW_TILE_SIZE * ovr_x;
335 *overview_y = OVERVIEW_TILE_SIZE * ovr_y;
336 } do_in_natural_pos_end;
339 /**************************************************************************
340 Finds the map coordinates for a given overview (canvas) position.
341 **************************************************************************/
342 void overview_to_map_pos(int *map_x, int *map_y,
343 int overview_x, int overview_y)
345 int ntl_x = overview_x / OVERVIEW_TILE_SIZE + overview.map_x0;
346 int ntl_y = overview_y / OVERVIEW_TILE_SIZE + overview.map_y0;
348 if (MAP_IS_ISOMETRIC && !current_topo_has_flag(TF_WRAPX)) {
349 /* Clip half tile left and right. See comment in map_to_overview_pos. */
350 ntl_x++;
353 NATURAL_TO_MAP_POS(map_x, map_y, ntl_x, ntl_y);
354 /* All positions on the overview should be valid. */
355 fc_assert(normalize_map_pos(map_x, map_y));
358 /**************************************************************************
359 Redraw the entire backing store for the overview minimap.
360 **************************************************************************/
361 void refresh_overview_canvas(void)
363 if (!can_client_change_view()) {
364 return;
366 whole_map_iterate(ptile) {
367 overview_update_tile(ptile);
368 } whole_map_iterate_end;
369 redraw_overview();
372 /****************************************************************************
373 Draws the color for this tile onto the given rectangle of the canvas.
375 This is just a simple helper function for overview_update_tile, since
376 sometimes a tile may cover more than one rectangle.
377 ****************************************************************************/
378 static void put_overview_tile_area(struct canvas *pcanvas,
379 struct tile *ptile,
380 int x, int y, int w, int h)
382 canvas_put_rectangle(pcanvas,
383 overview_tile_color(ptile),
384 x, y, w, h);
385 if (overview.fog
386 && TILE_KNOWN_UNSEEN == client_tile_get_known(ptile)) {
387 canvas_put_sprite(pcanvas, x, y, get_basic_fog_sprite(tileset),
388 0, 0, w, h);
392 /**************************************************************************
393 Redraw the given map position in the overview canvas.
394 **************************************************************************/
395 void overview_update_tile(struct tile *ptile)
397 int tile_x, tile_y;
399 /* Base overview positions are just like natural positions, but scaled to
400 * the overview tile dimensions. */
401 index_to_map_pos(&tile_x, &tile_y, tile_index(ptile));
402 do_in_natural_pos(ntl_x, ntl_y, tile_x, tile_y) {
403 int overview_y = ntl_y * OVERVIEW_TILE_SIZE;
404 int overview_x = ntl_x * OVERVIEW_TILE_SIZE;
406 if (MAP_IS_ISOMETRIC) {
407 if (current_topo_has_flag(TF_WRAPX)) {
408 if (overview_x > overview.width - OVERVIEW_TILE_WIDTH) {
409 /* This tile is shown half on the left and half on the right
410 * side of the overview. So we have to draw it in two parts. */
411 put_overview_tile_area(overview.map, ptile,
412 overview_x - overview.width, overview_y,
413 OVERVIEW_TILE_WIDTH, OVERVIEW_TILE_HEIGHT);
415 } else {
416 /* Clip half tile left and right.
417 * See comment in map_to_overview_pos. */
418 overview_x -= OVERVIEW_TILE_SIZE;
422 put_overview_tile_area(overview.map, ptile,
423 overview_x, overview_y,
424 OVERVIEW_TILE_WIDTH, OVERVIEW_TILE_HEIGHT);
426 dirty_overview();
427 } do_in_natural_pos_end;
430 /**************************************************************************
431 Called if the map size is know or changes.
432 **************************************************************************/
433 void calculate_overview_dimensions(void)
435 int w, h;
436 int xfact = MAP_IS_ISOMETRIC ? 2 : 1;
438 static int recursion = 0; /* Just to be safe. */
440 /* Clip half tile left and right. See comment in map_to_overview_pos. */
441 int shift = (MAP_IS_ISOMETRIC && !current_topo_has_flag(TF_WRAPX)) ? -1 : 0;
443 if (recursion > 0 || map.xsize <= 0 || map.ysize <= 0) {
444 return;
446 recursion++;
448 get_overview_area_dimensions(&w, &h);
449 get_overview_area_dimensions(&w, &h);
451 /* Set the scale of the overview map. This attempts to limit the
452 * overview to the size of the area available.
454 * It rounds up since this gives good results with the default settings.
455 * It may need tweaking if the panel resizes itself. */
456 OVERVIEW_TILE_SIZE = MIN((w - 1) / (map.xsize * xfact) + 1,
457 (h - 1) / map.ysize + 1);
458 OVERVIEW_TILE_SIZE = MAX(OVERVIEW_TILE_SIZE, 1);
460 log_debug("Map size %d,%d - area size %d,%d - scale: %d", map.xsize,
461 map.ysize, w, h, OVERVIEW_TILE_SIZE);
463 overview.width
464 = OVERVIEW_TILE_WIDTH * map.xsize + shift * OVERVIEW_TILE_SIZE;
465 overview.height = OVERVIEW_TILE_HEIGHT * map.ysize;
467 if (overview.map) {
468 canvas_free(overview.map);
469 canvas_free(overview.window);
471 overview.map = canvas_create(overview.width, overview.height);
472 overview.window = canvas_create(overview.width, overview.height);
473 canvas_put_rectangle(overview.map,
474 get_color(tileset, COLOR_OVERVIEW_UNKNOWN),
475 0, 0, overview.width, overview.height);
476 update_map_canvas_scrollbars_size();
478 /* Call gui specific function. */
479 overview_size_changed();
481 if (can_client_change_view()) {
482 refresh_overview_canvas();
485 recursion--;
488 /****************************************************************************
489 Callback to be called when an overview option is changed.
490 ****************************************************************************/
491 void overview_redraw_callback(struct option *option)
493 /* This is called once for each option changed so it is slower than
494 * necessary. If this becomes a problem it could be switched to use a
495 * queue system like the mapview drawing code does. */
496 refresh_overview_canvas();