Rulesave saves trade.type and trade.bonus correctly.
[freeciv.git] / client / mapview_common.c
blobc95165b54e16be35df64daf0c5be7c4e09c2f29f
1 /***********************************************************************
2 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
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 /* utility */
19 #include "fcintl.h"
20 #include "log.h"
21 #include "rand.h"
22 #include "support.h"
23 #include "timing.h"
25 /* common */
26 #include "featured_text.h"
27 #include "game.h"
28 #include "map.h"
29 #include "traderoutes.h"
30 #include "unitlist.h"
32 /* client/include */
33 #include "graphics_g.h"
34 #include "gui_main_g.h"
35 #include "mapctrl_g.h"
36 #include "mapview_g.h"
38 /* client */
39 #include "client_main.h"
40 #include "climap.h"
41 #include "control.h"
42 #include "editor.h"
43 #include "goto.h"
44 #include "citydlg_common.h"
45 #include "overview_common.h"
46 #include "tilespec.h"
47 #include "zoom.h"
49 #include "mapview_common.h"
52 struct tile_hash *mapdeco_highlight_table;
53 struct tile_hash *mapdeco_crosshair_table;
55 struct gotoline_counter {
56 int line_count[DIR8_MAGIC_MAX];
59 static inline struct gotoline_counter *gotoline_counter_new(void);
60 static void gotoline_counter_destroy(struct gotoline_counter *pglc);
62 #define SPECHASH_TAG gotoline
63 #define SPECHASH_IKEY_TYPE struct tile *
64 #define SPECHASH_IDATA_TYPE struct gotoline_counter *
65 #define SPECHASH_IDATA_FREE gotoline_counter_destroy
66 #include "spechash.h"
67 #define gotoline_hash_iterate(hash, ptile, pglc) \
68 TYPED_HASH_ITERATE(struct tile *, struct gotoline_counter *, \
69 hash, ptile, pglc)
70 #define gotoline_hash_iterate_end HASH_ITERATE_END
72 struct gotoline_hash *mapdeco_gotoline_table;
74 struct view mapview;
75 bool can_slide = TRUE;
77 struct tile *center_tile = NULL;
79 static void base_canvas_to_map_pos(int *map_x, int *map_y,
80 float canvas_x, float canvas_y);
82 enum update_type {
83 /* Masks */
84 UPDATE_NONE = 0,
85 UPDATE_CITY_DESCRIPTIONS = 1,
86 UPDATE_MAP_CANVAS_VISIBLE = 2,
87 UPDATE_TILE_LABELS = 4
90 /* A tile update has a tile associated with it as well as an area type.
91 * See unqueue_mapview_updates for a thorough explanation. */
92 enum tile_update_type {
93 TILE_UPDATE_TILE_SINGLE,
94 TILE_UPDATE_TILE_FULL,
95 TILE_UPDATE_UNIT,
96 TILE_UPDATE_CITY_DESC,
97 TILE_UPDATE_CITYMAP,
98 TILE_UPDATE_TILE_LABEL,
99 TILE_UPDATE_COUNT
101 static void queue_mapview_update(enum update_type update);
102 static void queue_mapview_tile_update(struct tile *ptile,
103 enum tile_update_type type);
105 /* Helper struct for drawing trade routes. */
106 struct trade_route_line {
107 float x, y, width, height;
110 /* A trade route line might need to be drawn in two parts. */
111 static const int MAX_TRADE_ROUTE_DRAW_LINES = 2;
114 /****************************************************************************
115 Create a new goto line counter.
116 ****************************************************************************/
117 static inline struct gotoline_counter *gotoline_counter_new(void)
119 struct gotoline_counter *pglc = fc_calloc(1, sizeof(*pglc));
120 return pglc;
123 /****************************************************************************
124 Create a new goto line counter.
125 ****************************************************************************/
126 static void gotoline_counter_destroy(struct gotoline_counter *pglc)
128 fc_assert_ret(NULL != pglc);
129 free(pglc);
132 /**************************************************************************
133 Refreshes a single tile on the map canvas.
134 **************************************************************************/
135 void refresh_tile_mapcanvas(struct tile *ptile,
136 bool full_refresh, bool write_to_screen)
138 if (full_refresh) {
139 queue_mapview_tile_update(ptile, TILE_UPDATE_TILE_FULL);
140 } else {
141 queue_mapview_tile_update(ptile, TILE_UPDATE_TILE_SINGLE);
143 if (write_to_screen) {
144 unqueue_mapview_updates(TRUE);
148 /**************************************************************************
149 Refreshes a single unit on the map canvas.
150 **************************************************************************/
151 void refresh_unit_mapcanvas(struct unit *punit, struct tile *ptile,
152 bool full_refresh, bool write_to_screen)
154 if (full_refresh && gui_options.draw_native) {
155 queue_mapview_update(UPDATE_MAP_CANVAS_VISIBLE);
156 } else if (full_refresh && unit_drawn_with_city_outline(punit, TRUE)) {
157 queue_mapview_tile_update(ptile, TILE_UPDATE_CITYMAP);
158 } else {
159 queue_mapview_tile_update(ptile, TILE_UPDATE_UNIT);
161 if (write_to_screen) {
162 unqueue_mapview_updates(TRUE);
166 /**************************************************************************
167 Refreshes a single city on the map canvas.
169 If full_refresh is given then the citymap area and the city text will
170 also be refreshed. Otherwise only the base city sprite is refreshed.
171 **************************************************************************/
172 void refresh_city_mapcanvas(struct city *pcity, struct tile *ptile,
173 bool full_refresh, bool write_to_screen)
175 if (full_refresh && (gui_options.draw_map_grid || gui_options.draw_borders)) {
176 queue_mapview_tile_update(ptile, TILE_UPDATE_CITYMAP);
177 } else {
178 queue_mapview_tile_update(ptile, TILE_UPDATE_UNIT);
180 if (write_to_screen) {
181 unqueue_mapview_updates(TRUE);
185 /****************************************************************************
186 Translate from a cartesian system to the GUI system. This function works
187 on vectors, meaning it can be passed a (dx,dy) pair and will return the
188 change in GUI coordinates corresponding to this vector. It is thus more
189 general than map_to_gui_pos.
191 Note that a gui_to_map_vector function is not possible, since the
192 resulting map vector may differ based on the origin of the gui vector.
193 ****************************************************************************/
194 void map_to_gui_vector(const struct tileset *t, float zoom,
195 float *gui_dx, float *gui_dy, int map_dx, int map_dy)
197 if (tileset_is_isometric(t)) {
199 * Convert the map coordinates to isometric GUI
200 * coordinates. We'll make tile map(0,0) be the origin, and
201 * transform like this:
204 * 123 2 6
205 * 456 -> becomes -> 1 5 9
206 * 789 4 8
209 *gui_dx = (map_dx - map_dy) * tileset_tile_width(t) / 2 * zoom;
210 *gui_dy = (map_dx + map_dy) * tileset_tile_height(t) / 2 * zoom;
211 } else {
212 *gui_dx = map_dx * tileset_tile_height(t) * zoom;
213 *gui_dy = map_dy * tileset_tile_width(t) * zoom;
217 /****************************************************************************
218 Translate from map to gui coordinate systems.
220 GUI coordinates are comparable to canvas coordinates but extend in all
221 directions. gui(0,0) == map(0,0).
222 ****************************************************************************/
223 static void map_to_gui_pos(const struct tileset *t,
224 float *gui_x, float *gui_y, int map_x, int map_y)
226 /* Since the GUI origin is the same as the map origin we can just do a
227 * vector conversion. */
228 map_to_gui_vector(t, map_zoom, gui_x, gui_y, map_x, map_y);
231 /****************************************************************************
232 Translate from gui to map coordinate systems. See map_to_gui_pos().
234 Note that you lose some information in this conversion. If you convert
235 from a gui position to a map position and back, you will probably not get
236 the same value you started with.
237 ****************************************************************************/
238 static void gui_to_map_pos(const struct tileset *t,
239 int *map_x, int *map_y, float gui_x, float gui_y)
241 const float W = tileset_tile_width(t) * map_zoom, H = tileset_tile_height(t) * map_zoom;
242 const float HH = tileset_hex_height(t) * map_zoom, HW = tileset_hex_width(t) * map_zoom;
244 if (HH > 0 || HW > 0) {
245 /* To handle hexagonal cases we have to revert to a less elegant method
246 * of calculation. */
247 float x, y;
248 int dx, dy;
249 int xmult, ymult, mod, compar;
251 fc_assert(tileset_is_isometric(t));
253 x = DIVIDE((int)gui_x, (int)W);
254 y = DIVIDE((int)gui_y, (int)H);
255 dx = gui_x - x * W;
256 dy = gui_y - y * H;
257 fc_assert(dx >= 0 && dx < W);
258 fc_assert(dy >= 0 && dy < H);
260 /* Now fold so we consider only one-quarter tile. */
261 xmult = (dx >= W / 2) ? -1 : 1;
262 ymult = (dy >= H / 2) ? -1 : 1;
263 dx = (dx >= W / 2) ? (W - 1 - dx) : dx;
264 dy = (dy >= H / 2) ? (H - 1 - dy) : dy;
266 /* Next compare to see if we're across onto the next tile. */
267 if (HW > 0) {
268 compar = (dx - HW / 2) * (H / 2) - (H / 2 - 1 - dy) * (W / 2 - HW);
269 } else {
270 compar = (dy - HH / 2) * (W / 2) - (W / 2 - 1 - dx) * (H / 2 - HH);
272 mod = (compar < 0) ? -1 : 0;
274 *map_x = (x + y) + mod * (xmult + ymult) / 2;
275 *map_y = (y - x) + mod * (ymult - xmult) / 2;
276 } else if (tileset_is_isometric(t)) {
277 /* The basic operation here is a simple pi/4 rotation; however, we
278 * have to first scale because the tiles have different width and
279 * height. Mathematically, this looks like
280 * | 1/W 1/H | |x| |x`|
281 * | | | | -> | |
282 * |-1/W 1/H | |y| |y`|
284 * Where W is the tile width and H the height.
286 * In simple terms, this is
287 * map_x = [ x / W + y / H ]
288 * map_y = [ - x / W + y / H ]
289 * where [q] stands for integer part of q.
291 * Here the division is proper mathematical floating point division.
293 * A picture demonstrating this can be seen at
294 * http://bugs.freeciv.org/Ticket/Attachment/16782/9982/grid1.png.
296 * We have to subtract off a half-tile in the X direction before doing
297 * the transformation. This is because, although the origin of the tile
298 * is the top-left corner of the bounding box, after the transformation
299 * the top corner of the diamond-shaped tile moves into this position.
301 * The calculation is complicated somewhat because of two things: we
302 * only use integer math, and C integer division rounds toward zero
303 * instead of rounding down.
305 * For another example of this math, see canvas_to_city_pos().
307 gui_x -= W / 2;
308 *map_x = DIVIDE((int)(gui_x * H + gui_y * W), (int)(W * H));
309 *map_y = DIVIDE((int)(gui_y * W - gui_x * H), (int)(W * H));
310 } else { /* tileset_is_isometric(t) */
311 /* We use DIVIDE so that we will get the correct result even
312 * for negative coordinates. */
313 *map_x = DIVIDE((int)gui_x, (int)W);
314 *map_y = DIVIDE((int)gui_y, (int)H);
318 /**************************************************************************
319 Finds the canvas coordinates for a map position. Beside setting the results
320 in canvas_x, canvas_y it returns whether the tile is inside the
321 visible mapview canvas.
323 The result represents the upper left pixel (origin) of the bounding box of
324 the tile. Note that in iso-view this origin is not a part of the tile
325 itself - so to make the operation reversible you would have to call
326 canvas_to_map_pos on the center of the tile, not the origin.
328 The center of a tile is defined as:
330 tile_to_canvas_pos(&canvas_x, &canvas_y, ptile);
331 canvas_x += tileset_tile_width(tileset) * map_zoom / 2;
332 canvas_y += tileset_tile_height(tileset) * map_zoom / 2;
335 This pixel is one position closer to the lower right, which may be
336 important to remember when doing some round-off operations. Other
337 parts of the code assume tileset_tile_width(tileset) and tileset_tile_height(tileset)
338 to be even numbers.
339 **************************************************************************/
340 bool tile_to_canvas_pos(float *canvas_x, float *canvas_y, struct tile *ptile)
342 int center_map_x, center_map_y, dx, dy, tile_x, tile_y;
345 * First we wrap the coordinates to hopefully be within the the mapview
346 * window. We do this by finding the position closest to the center
347 * of the window.
349 /* TODO: Cache the value of this position */
350 base_canvas_to_map_pos(&center_map_x, &center_map_y,
351 mapview.width / 2,
352 mapview.height / 2);
353 index_to_map_pos(&tile_x, &tile_y, tile_index(ptile));
354 base_map_distance_vector(&dx, &dy, center_map_x, center_map_y, tile_x,
355 tile_y);
357 map_to_gui_pos(tileset,
358 canvas_x, canvas_y, center_map_x + dx, center_map_y + dy);
359 *canvas_x -= mapview.gui_x0;
360 *canvas_y -= mapview.gui_y0;
363 * Finally we clip.
365 * This check is tailored to work for both iso-view and classic view. Note
366 * that (canvas_x, canvas_y) need not be aligned to a tile boundary, and
367 * that the position is at the top-left of the NORMAL (not UNIT) tile.
368 * This checks to see if _any part_ of the tile is present on the backing
369 * store. Even if it's not visible on the canvas, if it's present on the
370 * backing store we need to draw it in case the canvas is resized.
372 return (*canvas_x > -tileset_tile_width(tileset) * map_zoom
373 && *canvas_x < mapview.store_width
374 && *canvas_y > -tileset_tile_height(tileset) * map_zoom
375 && *canvas_y < (mapview.store_height
376 + (tileset_full_tile_height(tileset)
377 - tileset_tile_height(tileset)) * map_zoom));
380 /****************************************************************************
381 Finds the map coordinates corresponding to pixel coordinates. The
382 resulting position is unwrapped and may be unreal.
383 ****************************************************************************/
384 static void base_canvas_to_map_pos(int *map_x, int *map_y,
385 float canvas_x, float canvas_y)
387 gui_to_map_pos(tileset, map_x, map_y,
388 canvas_x + mapview.gui_x0,
389 canvas_y + mapview.gui_y0);
392 /**************************************************************************
393 Finds the tile corresponding to pixel coordinates. Returns that tile,
394 or NULL if the position is off the map.
395 **************************************************************************/
396 struct tile *canvas_pos_to_tile(float canvas_x, float canvas_y)
398 int map_x, map_y;
400 base_canvas_to_map_pos(&map_x, &map_y, canvas_x, canvas_y);
401 if (normalize_map_pos(&map_x, &map_y)) {
402 return map_pos_to_tile(map_x, map_y);
403 } else {
404 return NULL;
408 /**************************************************************************
409 Finds the tile corresponding to pixel coordinates. Returns that tile,
410 or the one nearest is the position is off the map. Will never return NULL.
411 **************************************************************************/
412 struct tile *canvas_pos_to_nearest_tile(float canvas_x, float canvas_y)
414 int map_x, map_y;
416 base_canvas_to_map_pos(&map_x, &map_y, canvas_x, canvas_y);
417 return nearest_real_tile(map_x, map_y);
420 /****************************************************************************
421 Normalize (wrap) the GUI position. This is equivalent to a map wrapping,
422 but in GUI coordinates so that pixel accuracy is preserved.
423 ****************************************************************************/
424 static void normalize_gui_pos(const struct tileset *t,
425 float *gui_x, float *gui_y)
427 int map_x, map_y, nat_x, nat_y, diff_x, diff_y;
428 float gui_x0, gui_y0;
430 /* Convert the (gui_x, gui_y) into a (map_x, map_y) plus a GUI offset
431 * from this tile. */
432 gui_to_map_pos(t, &map_x, &map_y, *gui_x, *gui_y);
433 map_to_gui_pos(t, &gui_x0, &gui_y0, map_x, map_y);
434 diff_x = *gui_x - gui_x0;
435 diff_y = *gui_y - gui_y0;
437 /* Perform wrapping without any realness check. It's important that
438 * we wrap even if the map position is unreal, which normalize_map_pos
439 * doesn't necessarily do. */
440 MAP_TO_NATIVE_POS(&nat_x, &nat_y, map_x, map_y);
441 if (current_topo_has_flag(TF_WRAPX)) {
442 nat_x = FC_WRAP(nat_x, game.map.xsize);
444 if (current_topo_has_flag(TF_WRAPY)) {
445 nat_y = FC_WRAP(nat_y, game.map.ysize);
447 NATIVE_TO_MAP_POS(&map_x, &map_y, nat_x, nat_y);
449 /* Now convert the wrapped map position back to a GUI position and add the
450 * offset back on. */
451 map_to_gui_pos(t, gui_x, gui_y, map_x, map_y);
452 *gui_x += diff_x;
453 *gui_y += diff_y;
456 /****************************************************************************
457 Find the vector with minimum "real" distance between two GUI positions.
458 This corresponds to map_to_distance_vector but works for GUI coordinates.
459 ****************************************************************************/
460 static void gui_distance_vector(const struct tileset *t,
461 float *gui_dx, float *gui_dy,
462 float gui_x0, float gui_y0,
463 float gui_x1, float gui_y1)
465 int map_x0, map_y0, map_x1, map_y1;
466 float gui_x0_base, gui_y0_base, gui_x1_base, gui_y1_base;
467 int gui_x0_diff, gui_y0_diff, gui_x1_diff, gui_y1_diff;
468 int map_dx, map_dy;
470 /* Make sure positions are canonical. Yes, this is the only way. */
471 normalize_gui_pos(t, &gui_x0, &gui_y0);
472 normalize_gui_pos(t, &gui_x1, &gui_y1);
474 /* Now we have to find the offset of each GUI position from its tile
475 * origin. This is complicated: it means converting to a map position and
476 * then back to the GUI position to find the tile origin, then subtracting
477 * to get the offset. */
478 gui_to_map_pos(t, &map_x0, &map_y0, gui_x0, gui_y0);
479 gui_to_map_pos(t, &map_x1, &map_y1, gui_x1, gui_y1);
481 map_to_gui_pos(t, &gui_x0_base, &gui_y0_base, map_x0, map_y0);
482 map_to_gui_pos(t, &gui_x1_base, &gui_y1_base, map_x1, map_y1);
484 gui_x0_diff = gui_x0 - gui_x0_base;
485 gui_y0_diff = gui_y0 - gui_y0_base;
486 gui_x1_diff = gui_x1 - gui_x1_base;
487 gui_y1_diff = gui_y1 - gui_y1_base;
489 /* Next we find the map distance vector and convert this into a GUI
490 * vector. */
491 base_map_distance_vector(&map_dx, &map_dy, map_x0, map_y0, map_x1, map_y1);
492 map_to_gui_pos(t, gui_dx, gui_dy, map_dx, map_dy);
494 /* Finally we add on the difference in offsets to retain pixel
495 * resolution. */
496 *gui_dx += gui_x1_diff - gui_x0_diff;
497 *gui_dy += gui_y1_diff - gui_y0_diff;
500 /****************************************************************************
501 Move the GUI origin to the given normalized, clipped origin. This may
502 be called many times when sliding the mapview.
503 ****************************************************************************/
504 static void base_set_mapview_origin(float gui_x0, float gui_y0)
506 float old_gui_x0, old_gui_y0;
507 float dx, dy;
508 const int width = mapview.width, height = mapview.height;
509 int common_x0, common_x1, common_y0, common_y1;
510 int update_x0, update_x1, update_y0, update_y1;
512 /* Then update everything. This does some tricky math to avoid having
513 * to do unnecessary redraws in update_map_canvas. This makes for ugly
514 * code but speeds up the mapview by a large factor. */
516 /* We need to calculate the vector of movement of the mapview. So
517 * we find the GUI distance vector and then use this to calculate
518 * the original mapview origin relative to the current position. Thus
519 * if we move one tile to the left, even if this causes GUI positions
520 * to wrap the distance vector is only one tile. */
521 normalize_gui_pos(tileset, &gui_x0, &gui_y0);
522 gui_distance_vector(tileset, &dx, &dy,
523 mapview.gui_x0, mapview.gui_y0,
524 gui_x0, gui_y0);
525 old_gui_x0 = gui_x0 - dx;
526 old_gui_y0 = gui_y0 - dy;
528 mapview.gui_x0 = gui_x0;
529 mapview.gui_y0 = gui_y0;
531 /* Find the overlapping area of the new and old mapview. This is
532 * done in GUI coordinates. Note that if the GUI coordinates wrap
533 * no overlap will be found. */
534 common_x0 = MAX(old_gui_x0, gui_x0);
535 common_x1 = MIN(old_gui_x0, gui_x0) + width;
536 common_y0 = MAX(old_gui_y0, gui_y0);
537 common_y1 = MIN(old_gui_y0, gui_y0) + height;
539 if (mapview.can_do_cached_drawing && !zoom_is_enabled()
540 && common_x1 > common_x0 && common_y1 > common_y0) {
541 /* Do a partial redraw only. This means the area of overlap (a
542 * rectangle) is copied. Then the remaining areas (two rectangles)
543 * are updated through update_map_canvas. */
544 struct canvas *target = mapview.tmp_store;
546 if (old_gui_x0 < gui_x0) {
547 update_x0 = MAX(old_gui_x0 + width, gui_x0);
548 update_x1 = gui_x0 + width;
549 } else {
550 update_x0 = gui_x0;
551 update_x1 = MIN(old_gui_x0, gui_x0 + width);
553 if (old_gui_y0 < gui_y0) {
554 update_y0 = MAX(old_gui_y0 + height, gui_y0);
555 update_y1 = gui_y0 + height;
556 } else {
557 update_y0 = gui_y0;
558 update_y1 = MIN(old_gui_y0, gui_y0 + height);
561 dirty_all();
562 canvas_copy(target, mapview.store,
563 common_x0 - old_gui_x0,
564 common_y0 - old_gui_y0,
565 common_x0 - gui_x0, common_y0 - gui_y0,
566 common_x1 - common_x0, common_y1 - common_y0);
567 mapview.tmp_store = mapview.store;
568 mapview.store = target;
570 if (update_y1 > update_y0) {
571 update_map_canvas(0, update_y0 - gui_y0,
572 width, update_y1 - update_y0);
574 if (update_x1 > update_x0) {
575 update_map_canvas(update_x0 - gui_x0, common_y0 - gui_y0,
576 update_x1 - update_x0, common_y1 - common_y0);
578 } else {
579 dirty_all();
580 update_map_canvas(0, 0, mapview.store_width, mapview.store_height);
583 center_tile_overviewcanvas();
584 switch (hover_state) {
585 case HOVER_GOTO:
586 case HOVER_PATROL:
587 case HOVER_CONNECT:
588 case HOVER_NUKE:
589 create_line_at_mouse_pos();
590 case HOVER_NONE:
591 case HOVER_PARADROP:
592 case HOVER_ACT_SEL_TGT:
593 break;
595 if (rectangle_active) {
596 update_rect_at_mouse_pos();
600 /****************************************************************************
601 Adjust mapview origin values. Returns TRUE iff values are different from
602 current mapview.
603 ****************************************************************************/
604 static bool calc_mapview_origin(float *gui_x0, float *gui_y0)
606 float xmin, ymin, xmax, ymax;
607 int xsize, ysize;
609 /* Normalize (wrap) the mapview origin. */
610 normalize_gui_pos(tileset, gui_x0, gui_y0);
612 /* First wrap/clip the position. Wrapping is done in native positions
613 * while clipping is done in scroll (native) positions. */
614 get_mapview_scroll_window(&xmin, &ymin, &xmax, &ymax, &xsize, &ysize);
616 if (!current_topo_has_flag(TF_WRAPX)) {
617 *gui_x0 = CLIP(xmin, *gui_x0, xmax - xsize);
620 if (!current_topo_has_flag(TF_WRAPY)) {
621 *gui_y0 = CLIP(ymin, *gui_y0, ymax - ysize);
624 if (mapview.gui_x0 == *gui_x0 && mapview.gui_y0 == *gui_y0) {
625 return FALSE;
628 return TRUE;
631 /****************************************************************************
632 Change the mapview origin, clip it, and update everything.
633 ****************************************************************************/
634 void set_mapview_origin(float gui_x0, float gui_y0)
636 if (!calc_mapview_origin(&gui_x0, &gui_y0)) {
637 return;
640 if (can_slide && gui_options.smooth_center_slide_msec > 0) {
641 int start_x = mapview.gui_x0, start_y = mapview.gui_y0;
642 float diff_x, diff_y;
643 double timing_sec = (double)gui_options.smooth_center_slide_msec / 1000.0;
644 double currtime;
645 static struct timer *anim_timer;
646 int frames = 0;
648 /* We track the average FPS, which is used to predict how long the
649 * next draw will take. We start with a 100 FPS estimate - this
650 * value will quickly become irrelevant as the correct value is
651 * calculated, but it's needed to give an estimate of the FPS for
652 * the first draw.
654 * Note that the initial value shouldn't be larger than the sliding
655 * time, or we'll jump straight to the last frame. The FPS should
656 * therefore be a "high" estimate. */
657 static double total_frames = 0.01;
658 static double total_time = 0.0001;
660 gui_distance_vector(tileset,
661 &diff_x, &diff_y, start_x, start_y, gui_x0, gui_y0);
662 anim_timer = timer_renew(anim_timer, TIMER_USER, TIMER_ACTIVE);
663 timer_start(anim_timer);
665 unqueue_mapview_updates(TRUE);
667 do {
668 double mytime;
670 /* Get the current time, and add on the average 1/FPS, which is the
671 * expected time this frame will take. This is done so that the
672 * frame's position is calculated from the expected time when the
673 * frame will complete, rather than the time when the frame drawing
674 * is started. */
675 currtime = timer_read_seconds(anim_timer);
676 currtime += total_time / total_frames;
678 mytime = MIN(currtime, timing_sec);
679 base_set_mapview_origin(start_x + diff_x * (mytime / timing_sec),
680 start_y + diff_y * (mytime / timing_sec));
681 flush_dirty();
682 gui_flush();
683 frames++;
684 } while (currtime < timing_sec);
686 currtime = timer_read_seconds(anim_timer);
687 total_frames += frames;
688 total_time += currtime;
689 log_debug("Got %d frames in %f seconds: %f FPS (avg %f).",
690 frames, currtime, (double)frames / currtime,
691 total_frames / total_time);
693 /* A very small decay factor to make things more accurate when something
694 * changes (mapview size, tileset change, etc.). This gives a
695 * half-life of 68 slides. */
696 total_frames *= 0.99;
697 total_time *= 0.99;
698 } else {
699 base_set_mapview_origin(gui_x0, gui_y0);
702 update_map_canvas_scrollbars();
705 /****************************************************************************
706 Return the scroll dimensions of the clipping window for the mapview window..
708 Imagine the entire map in scroll coordinates. It is a rectangle. Now
709 imagine the mapview "window" sliding around through this rectangle. How
710 far can it slide? In most cases it has to be able to slide past the
711 ends of the map rectangle so that it's capable of reaching the whole
712 area.
714 This function gives constraints on how far the window is allowed to
715 slide. xmin and ymin are the minimum values for the window origin.
716 xsize and ysize give the scroll dimensions of the mapview window.
717 xmax and ymax give the maximum values that the bottom/left ends of the
718 window may reach. The constraints, therefore, are that:
720 get_mapview_scroll_pos(&scroll_x, &scroll_y);
721 xmin <= scroll_x < xmax - xsize
722 ymin <= scroll_y < ymax - ysize
724 This function should be used anywhere and everywhere that scrolling is
725 constrained.
727 Note that scroll coordinates, not map coordinates, are used. Currently
728 these correspond to native coordinates.
729 ****************************************************************************/
730 void get_mapview_scroll_window(float *xmin, float *ymin,
731 float *xmax, float *ymax,
732 int *xsize, int *ysize)
734 int diff;
736 *xsize = mapview.width;
737 *ysize = mapview.height;
739 if (MAP_IS_ISOMETRIC == tileset_is_isometric(tileset)) {
740 /* If the map and view line up, it's easy. */
741 NATIVE_TO_MAP_POS(xmin, ymin, 0, 0);
742 map_to_gui_pos(tileset, xmin, ymin, *xmin, *ymin);
744 NATIVE_TO_MAP_POS(xmax, ymax, game.map.xsize - 1, game.map.ysize - 1);
745 map_to_gui_pos(tileset, xmax, ymax, *xmax, *ymax);
746 *xmax += tileset_tile_width(tileset) * map_zoom;
747 *ymax += tileset_tile_height(tileset) * map_zoom;
749 /* To be able to center on positions near the edges, we have to be
750 * allowed to scroll all the way to those edges. To allow wrapping the
751 * clipping boundary needs to extend past the edge - a half-tile in
752 * iso-view or a full tile in non-iso view. The above math already has
753 * taken care of some of this so all that's left is to fix the corner
754 * cases. */
755 if (current_topo_has_flag(TF_WRAPX)) {
756 *xmax += *xsize;
758 /* We need to be able to scroll a little further to the left. */
759 *xmin -= tileset_tile_width(tileset) * map_zoom;
761 if (current_topo_has_flag(TF_WRAPY)) {
762 *ymax += *ysize;
764 /* We need to be able to scroll a little further up. */
765 *ymin -= tileset_tile_height(tileset) * map_zoom;
767 } else {
768 /* Otherwise it's hard. Very hard. Impossible, in fact. This is just
769 * an approximation - a huge bounding box. */
770 float gui_x1, gui_y1, gui_x2, gui_y2, gui_x3, gui_y3, gui_x4, gui_y4;
771 int map_x, map_y;
773 NATIVE_TO_MAP_POS(&map_x, &map_y, 0, 0);
774 map_to_gui_pos(tileset, &gui_x1, &gui_y1, map_x, map_y);
776 NATIVE_TO_MAP_POS(&map_x, &map_y, game.map.xsize - 1, 0);
777 map_to_gui_pos(tileset, &gui_x2, &gui_y2, map_x, map_y);
779 NATIVE_TO_MAP_POS(&map_x, &map_y, 0, game.map.ysize - 1);
780 map_to_gui_pos(tileset, &gui_x3, &gui_y3, map_x, map_y);
782 NATIVE_TO_MAP_POS(&map_x, &map_y, game.map.xsize - 1, game.map.ysize - 1);
783 map_to_gui_pos(tileset, &gui_x4, &gui_y4, map_x, map_y);
785 *xmin = MIN(gui_x1, MIN(gui_x2, gui_x3)) - mapview.width / 2;
786 *ymin = MIN(gui_y1, MIN(gui_y2, gui_y3)) - mapview.height / 2;
788 *xmax = MAX(gui_x4, MAX(gui_x2, gui_x3)) + mapview.width / 2;
789 *ymax = MAX(gui_y4, MAX(gui_y2, gui_y3)) + mapview.height / 2;
792 /* Make sure the scroll window is big enough to hold the mapview. If
793 * not scrolling will be very ugly and the GUI may become confused. */
794 diff = *xsize - (*xmax - *xmin);
795 if (diff > 0) {
796 *xmin -= diff / 2;
797 *xmax += (diff + 1) / 2;
800 diff = *ysize - (*ymax - *ymin);
801 if (diff > 0) {
802 *ymin -= diff / 2;
803 *ymax += (diff + 1) / 2;
806 log_debug("x: %f<-%d->%f; y: %f<-%f->%d",
807 *xmin, *xsize, *xmax, *ymin, *ymax, *ysize);
810 /****************************************************************************
811 Find the scroll step for the mapview. This is the amount to scroll (in
812 scroll coordinates) on each "step". See also get_mapview_scroll_window.
813 ****************************************************************************/
814 void get_mapview_scroll_step(int *xstep, int *ystep)
816 *xstep = tileset_tile_width(tileset) * map_zoom;
817 *ystep = tileset_tile_height(tileset) * map_zoom;
819 if (tileset_is_isometric(tileset)) {
820 *xstep /= 2;
821 *ystep /= 2;
825 /****************************************************************************
826 Find the current scroll position (origin) of the mapview.
827 ****************************************************************************/
828 void get_mapview_scroll_pos(int *scroll_x, int *scroll_y)
830 *scroll_x = mapview.gui_x0;
831 *scroll_y = mapview.gui_y0;
834 /****************************************************************************
835 Set the scroll position (origin) of the mapview, and update the GUI.
836 ****************************************************************************/
837 void set_mapview_scroll_pos(int scroll_x, int scroll_y)
839 int gui_x0 = scroll_x, gui_y0 = scroll_y;
841 can_slide = FALSE;
842 set_mapview_origin(gui_x0, gui_y0);
843 can_slide = TRUE;
846 /**************************************************************************
847 Finds the current center tile of the mapcanvas.
848 **************************************************************************/
849 struct tile *get_center_tile_mapcanvas(void)
851 return canvas_pos_to_nearest_tile(mapview.width / 2,
852 mapview.height / 2);
855 /**************************************************************************
856 Centers the mapview around (map_x, map_y).
857 **************************************************************************/
858 void center_tile_mapcanvas(struct tile *ptile)
860 float gui_x, gui_y;
861 int tile_x, tile_y;
862 static bool first = TRUE;
864 if (first && can_slide) {
865 return;
867 first = FALSE;
869 index_to_map_pos(&tile_x, &tile_y, tile_index(ptile));
870 map_to_gui_pos(tileset, &gui_x, &gui_y, tile_x, tile_y);
872 /* Put the center pixel of the tile at the exact center of the mapview. */
873 gui_x -= (mapview.width - tileset_tile_width(tileset) * map_zoom) / 2;
874 gui_y -= (mapview.height - tileset_tile_height(tileset) * map_zoom) / 2;
876 set_mapview_origin(gui_x, gui_y);
878 center_tile = ptile;
881 /**************************************************************************
882 Return TRUE iff the given map position has a tile visible on the
883 map canvas.
884 **************************************************************************/
885 bool tile_visible_mapcanvas(struct tile *ptile)
887 float dummy_x, dummy_y; /* well, it needs two pointers... */
889 return tile_to_canvas_pos(&dummy_x, &dummy_y, ptile);
892 /**************************************************************************
893 Return TRUE iff the given map position has a tile visible within the
894 interior of the map canvas. This information is used to determine
895 when we need to recenter the map canvas.
897 The logic of this function is simple: if a tile is within 1.5 tiles
898 of a border of the canvas and that border is not aligned with the
899 edge of the map, then the tile is on the "border" of the map canvas.
901 This function is only correct for the current topology.
902 **************************************************************************/
903 bool tile_visible_and_not_on_border_mapcanvas(struct tile *ptile)
905 float canvas_x, canvas_y;
906 float xmin, ymin, xmax, ymax;
907 int xsize, ysize, scroll_x, scroll_y;
908 const int border_x = (tileset_is_isometric(tileset) ? tileset_tile_width(tileset) / 2 * map_zoom
909 : 2 * tileset_tile_width(tileset) * map_zoom);
910 const int border_y = (tileset_is_isometric(tileset) ? tileset_tile_height(tileset) / 2 * map_zoom
911 : 2 * tileset_tile_height(tileset) * map_zoom);
912 bool same = (tileset_is_isometric(tileset) == MAP_IS_ISOMETRIC);
914 get_mapview_scroll_window(&xmin, &ymin, &xmax, &ymax, &xsize, &ysize);
915 get_mapview_scroll_pos(&scroll_x, &scroll_y);
917 if (!tile_to_canvas_pos(&canvas_x, &canvas_y, ptile)) {
918 /* The tile isn't visible at all. */
919 return FALSE;
922 /* For each direction: if the tile is too close to the mapview border
923 * in that direction, and scrolling can get us any closer to the
924 * border, then it's a border tile. We can only really check the
925 * scrolling when the mapview window lines up with the map. */
926 if (canvas_x < border_x
927 && (!same || scroll_x > xmin || current_topo_has_flag(TF_WRAPX))) {
928 return FALSE;
930 if (canvas_y < border_y
931 && (!same || scroll_y > ymin || current_topo_has_flag(TF_WRAPY))) {
932 return FALSE;
934 if (canvas_x + tileset_tile_width(tileset) * map_zoom > mapview.width - border_x
935 && (!same || scroll_x + xsize < xmax || current_topo_has_flag(TF_WRAPX))) {
936 return FALSE;
938 if (canvas_y + tileset_tile_height(tileset) * map_zoom > mapview.height - border_y
939 && (!same || scroll_y + ysize < ymax || current_topo_has_flag(TF_WRAPY))) {
940 return FALSE;
943 return TRUE;
946 /**************************************************************************
947 Draw an array of drawn sprites onto the canvas.
948 **************************************************************************/
949 void put_drawn_sprites(struct canvas *pcanvas, float zoom,
950 int canvas_x, int canvas_y,
951 int count, struct drawn_sprite *pdrawn,
952 bool fog)
954 int i;
956 for (i = 0; i < count; i++) {
957 if (!pdrawn[i].sprite) {
958 /* This can happen, although it should probably be avoided. */
959 continue;
962 if (fog && pdrawn[i].foggable) {
963 canvas_put_sprite_fogged(pcanvas,
964 canvas_x / zoom + pdrawn[i].offset_x,
965 canvas_y / zoom + pdrawn[i].offset_y,
966 pdrawn[i].sprite,
967 TRUE,
968 canvas_x, canvas_y);
969 } else {
970 /* We avoid calling canvas_put_sprite_fogged, even though it
971 * should be a valid thing to do, because gui-gtk-2.0 doesn't have
972 * a full implementation. */
973 canvas_put_sprite_full(pcanvas,
974 canvas_x / zoom + pdrawn[i].offset_x,
975 canvas_y / zoom + pdrawn[i].offset_y,
976 pdrawn[i].sprite);
981 /**************************************************************************
982 Draw one layer of a tile, edge, corner, unit, and/or city onto the
983 canvas at the given position.
984 **************************************************************************/
985 void put_one_element(struct canvas *pcanvas, float zoom,
986 enum mapview_layer layer,
987 const struct tile *ptile,
988 const struct tile_edge *pedge,
989 const struct tile_corner *pcorner,
990 const struct unit *punit, const struct city *pcity,
991 int canvas_x, int canvas_y,
992 const struct city *citymode,
993 const struct unit_type *putype)
995 struct drawn_sprite tile_sprs[80];
996 int count = fill_sprite_array(tileset, tile_sprs, layer,
997 ptile, pedge, pcorner,
998 punit, pcity, citymode, putype);
999 bool fog = (ptile && gui_options.draw_fog_of_war
1000 && TILE_KNOWN_UNSEEN == client_tile_get_known(ptile));
1002 /*** Draw terrain and specials ***/
1003 put_drawn_sprites(pcanvas, zoom, canvas_x, canvas_y, count, tile_sprs, fog);
1006 /*****************************************************************************
1007 Draw the given unit onto the canvas store at the given location. The area
1008 of drawing is tileset_unit_height(tileset) x tileset_unit_width(tileset).
1009 *****************************************************************************/
1010 void put_unit(const struct unit *punit, struct canvas *pcanvas, float zoom,
1011 int canvas_x, int canvas_y)
1013 canvas_y += (tileset_unit_height(tileset) - tileset_tile_height(tileset)) * zoom;
1014 mapview_layer_iterate(layer) {
1015 put_one_element(pcanvas, zoom, layer, NULL, NULL, NULL,
1016 punit, NULL, canvas_x, canvas_y, NULL, NULL);
1017 } mapview_layer_iterate_end;
1020 /*****************************************************************************
1021 Draw the given unit onto the canvas store at the given location. The area
1022 of drawing is tileset_unit_height(tileset) x tileset_unit_width(tileset).
1023 *****************************************************************************/
1024 void put_unittype(const struct unit_type *putype, struct canvas *pcanvas, float zoom,
1025 int canvas_x, int canvas_y)
1027 canvas_y += (tileset_unit_height(tileset) - tileset_tile_height(tileset)) * zoom;
1028 mapview_layer_iterate(layer) {
1029 put_one_element(pcanvas, zoom, layer, NULL, NULL, NULL,
1030 NULL, NULL, canvas_x, canvas_y, NULL, putype);
1031 } mapview_layer_iterate_end;
1034 /**************************************************************************
1035 Draw the given city onto the canvas store at the given location. The
1036 area of drawing is
1037 tileset_full_tile_height(tileset) x tileset_full_tile_width(tileset).
1038 **************************************************************************/
1039 void put_city(struct city *pcity, struct canvas *pcanvas, float zoom,
1040 int canvas_x, int canvas_y)
1042 canvas_y += (tileset_full_tile_height(tileset) - tileset_tile_height(tileset)) * zoom;
1043 mapview_layer_iterate(layer) {
1044 put_one_element(pcanvas, zoom, layer,
1045 NULL, NULL, NULL, NULL, pcity,
1046 canvas_x, canvas_y, NULL, NULL);
1047 } mapview_layer_iterate_end;
1050 /**************************************************************************
1051 Draw the given tile terrain onto the canvas store at the given location.
1052 The area of drawing is
1053 tileset_full_tile_height(tileset) x tileset_full_tile_width(tileset)
1054 (even though most tiles are not this tall).
1055 **************************************************************************/
1056 void put_terrain(struct tile *ptile, struct canvas *pcanvas, float zoom,
1057 int canvas_x, int canvas_y)
1059 /* Use full tile height, even for terrains. */
1060 canvas_y += (tileset_full_tile_height(tileset) - tileset_tile_height(tileset)) * zoom;
1061 mapview_layer_iterate(layer) {
1062 put_one_element(pcanvas, zoom, layer, ptile, NULL, NULL, NULL, NULL,
1063 canvas_x, canvas_y, NULL, NULL);
1064 } mapview_layer_iterate_end;
1067 /****************************************************************************
1068 Draw food, gold, and shield upkeep values on the unit.
1070 The proper way to do this is probably something like what Civ II does
1071 (one sprite drawn N times on top of itself), but we just use separate
1072 sprites (limiting the number of combinations).
1073 ****************************************************************************/
1074 void put_unit_city_overlays(struct unit *punit,
1075 struct canvas *pcanvas,
1076 int canvas_x, int canvas_y, int *upkeep_cost,
1077 int happy_cost)
1079 struct sprite *sprite;
1081 sprite = get_unit_unhappy_sprite(tileset, punit, happy_cost);
1082 if (sprite) {
1083 canvas_put_sprite_full(pcanvas, canvas_x, canvas_y, sprite);
1086 output_type_iterate(o) {
1087 sprite = get_unit_upkeep_sprite(tileset, o, punit, upkeep_cost);
1088 if (sprite) {
1089 canvas_put_sprite_full(pcanvas, canvas_x, canvas_y, sprite);
1091 } output_type_iterate_end;
1095 * pcity->client.color_index is an index into the city_colors array.
1096 * When toggle_city_color is called the city's coloration is toggled. When
1097 * a city is newly colored its color is taken from color_index and
1098 * color_index is moved forward one position. Each color in the array
1099 * tells what color the citymap will be drawn on the mapview.
1101 * This array can be added to without breaking anything elsewhere.
1103 static int color_index = 0;
1104 #define NUM_CITY_COLORS tileset_num_city_colors(tileset)
1107 /****************************************************************************
1108 Toggle the city color. This cycles through the possible colors for the
1109 citymap as shown on the mapview. These colors are listed in the
1110 city_colors array; above.
1111 ****************************************************************************/
1112 void toggle_city_color(struct city *pcity)
1114 if (pcity->client.colored) {
1115 pcity->client.colored = FALSE;
1116 } else {
1117 pcity->client.colored = TRUE;
1118 pcity->client.color_index = color_index;
1119 color_index = (color_index + 1) % NUM_CITY_COLORS;
1122 refresh_city_mapcanvas(pcity, pcity->tile, TRUE, FALSE);
1125 /****************************************************************************
1126 Toggle the unit color. This cycles through the possible colors for the
1127 citymap as shown on the mapview. These colors are listed in the
1128 city_colors array; above.
1129 ****************************************************************************/
1130 void toggle_unit_color(struct unit *punit)
1132 if (punit->client.colored) {
1133 punit->client.colored = FALSE;
1134 } else {
1135 punit->client.colored = TRUE;
1136 punit->client.color_index = color_index;
1137 color_index = (color_index + 1) % NUM_CITY_COLORS;
1140 refresh_unit_mapcanvas(punit, unit_tile(punit), TRUE, FALSE);
1143 /****************************************************************************
1144 Animate the nuke explosion at map(x, y).
1145 ****************************************************************************/
1146 void put_nuke_mushroom_pixmaps(struct tile *ptile)
1148 float canvas_x, canvas_y;
1149 struct sprite *mysprite = get_nuke_explode_sprite(tileset);
1150 int width, height;
1152 /* We can't count on the return value of tile_to_canvas_pos since the
1153 * sprite may span multiple tiles. */
1154 (void) tile_to_canvas_pos(&canvas_x, &canvas_y, ptile);
1155 get_sprite_dimensions(mysprite, &width, &height);
1157 canvas_x += (tileset_tile_width(tileset) - width) / 2 * map_zoom;
1158 canvas_y += (tileset_tile_height(tileset) - height) / 2 * map_zoom;
1160 /* Make sure everything is flushed and synced before proceeding. First
1161 * we update everything to the store, but don't write this to screen.
1162 * Then add the nuke graphic to the store. Finally flush everything to
1163 * the screen and wait 1 second. */
1164 unqueue_mapview_updates(FALSE);
1166 canvas_put_sprite_full(mapview.store, canvas_x, canvas_y, mysprite);
1167 dirty_rect(canvas_x, canvas_y, width, height);
1169 flush_dirty();
1170 gui_flush();
1172 fc_usleep(1000000);
1174 update_map_canvas_visible();
1177 /**************************************************************************
1178 Draw some or all of a tile onto the canvas.
1179 **************************************************************************/
1180 static void put_one_tile(struct canvas *pcanvas, enum mapview_layer layer,
1181 struct tile *ptile, int canvas_x, int canvas_y,
1182 const struct city *citymode)
1184 if (client_tile_get_known(ptile) != TILE_UNKNOWN
1185 || (editor_is_active() && editor_tile_is_selected(ptile))) {
1186 put_one_element(pcanvas, map_zoom, layer, ptile, NULL, NULL,
1187 get_drawable_unit(tileset, ptile, citymode),
1188 tile_city(ptile), canvas_x, canvas_y, citymode, NULL);
1192 /**************************************************************************
1193 Depending on where ptile1 and ptile2 are on the map canvas, a trade route
1194 line may need to be drawn as two disjointed line segments. This function
1195 fills the given line array 'lines' with the necessary line segments.
1197 The return value is the number of line segments that need to be drawn.
1199 NB: It is assumed ptile1 and ptile2 are already consistently ordered.
1200 NB: 'lines' must be able to hold least MAX_TRADE_ROUTE_DRAW_LINES
1201 elements.
1202 **************************************************************************/
1203 static int trade_route_to_canvas_lines(const struct tile *ptile1,
1204 const struct tile *ptile2,
1205 struct trade_route_line *lines)
1207 int dx, dy;
1209 if (!ptile1 || !ptile2 || !lines) {
1210 return 0;
1213 base_map_distance_vector(&dx, &dy, TILE_XY(ptile1), TILE_XY(ptile2));
1214 map_to_gui_pos(tileset, &lines[0].width, &lines[0].height, dx, dy);
1216 /* FIXME: Remove these casts. */
1217 tile_to_canvas_pos(&lines[0].x, &lines[0].y, (struct tile *)ptile1);
1218 tile_to_canvas_pos(&lines[1].x, &lines[1].y, (struct tile *)ptile2);
1220 if (lines[1].x - lines[0].x == lines[0].width
1221 && lines[1].y - lines[0].y == lines[0].height) {
1222 return 1;
1225 lines[1].width = -lines[0].width;
1226 lines[1].height = -lines[0].height;
1227 return 2;
1230 /**************************************************************************
1231 Draw a colored trade route line from one tile to another.
1232 **************************************************************************/
1233 static void draw_trade_route_line(const struct tile *ptile1,
1234 const struct tile *ptile2,
1235 enum color_std color)
1237 struct trade_route_line lines[MAX_TRADE_ROUTE_DRAW_LINES];
1238 int line_count, i;
1239 struct color *pcolor;
1241 if (!ptile1 || !ptile2) {
1242 return;
1245 pcolor = get_color(tileset, color);
1246 if (!pcolor) {
1247 return;
1250 /* Order the source and destination tiles consistently
1251 * so that if a line is drawn twice it does not produce
1252 * ugly effects due to dashes not lining up. */
1253 if (tile_index(ptile2) > tile_index(ptile1)) {
1254 const struct tile *tmp;
1255 tmp = ptile1;
1256 ptile1 = ptile2;
1257 ptile2 = tmp;
1260 line_count = trade_route_to_canvas_lines(ptile1, ptile2, lines);
1261 for (i = 0; i < line_count; i++) {
1262 canvas_put_line(mapview.store, pcolor, LINE_BORDER,
1263 lines[i].x + tileset_tile_width(tileset) / 2 * map_zoom,
1264 lines[i].y + tileset_tile_height(tileset) / 2 * map_zoom,
1265 lines[i].width, lines[i].height);
1269 /**************************************************************************
1270 Draw all trade routes for the given city.
1271 **************************************************************************/
1272 static void draw_trade_routes_for_city(const struct city *pcity_src)
1274 if (!pcity_src) {
1275 return;
1278 trade_routes_iterate(pcity_src, pcity_dest) {
1279 draw_trade_route_line(city_tile(pcity_src), city_tile(pcity_dest),
1280 COLOR_MAPVIEW_TRADE_ROUTE_LINE);
1281 } trade_routes_iterate_end;
1284 /**************************************************************************
1285 Draw trade routes between cities as lines on the main map canvas.
1286 **************************************************************************/
1287 static void draw_trade_routes(void)
1289 if (!gui_options.draw_city_trade_routes) {
1290 return;
1293 if (client_is_global_observer()) {
1294 cities_iterate(pcity) {
1295 draw_trade_routes_for_city(pcity);
1296 } cities_iterate_end;
1297 } else {
1298 struct player *pplayer = client_player();
1299 if (!pplayer) {
1300 return;
1302 city_list_iterate(pplayer->cities, pcity) {
1303 draw_trade_routes_for_city(pcity);
1304 } city_list_iterate_end;
1308 /**************************************************************************
1309 Update (refresh) the map canvas starting at the given tile (in map
1310 coordinates) and with the given dimensions (also in map coordinates).
1312 In non-iso view, this is easy. In iso view, we have to use the
1313 Painter's Algorithm to draw the tiles in back first. When we draw
1314 a tile, we tell the GUI which part of the tile to draw - which is
1315 necessary unless we have an extra buffering step.
1317 After refreshing the backing store tile-by-tile, we write the store
1318 out to the display if write_to_screen is specified.
1320 x, y, width, and height are in map coordinates; they need not be
1321 normalized or even real.
1322 **************************************************************************/
1323 void update_map_canvas(int canvas_x, int canvas_y, int width, int height)
1325 int gui_x0, gui_y0;
1326 bool full;
1327 struct canvas *tmp;
1329 canvas_x = MAX(canvas_x, 0);
1330 canvas_y = MAX(canvas_y, 0);
1331 width = MIN(mapview.store_width - canvas_x, width);
1332 height = MIN(mapview.store_height - canvas_y, height);
1334 gui_x0 = mapview.gui_x0 + canvas_x;
1335 gui_y0 = mapview.gui_y0 + canvas_y;
1336 full = (canvas_x == 0 && canvas_y == 0
1337 && width == mapview.store_width
1338 && height == mapview.store_height);
1340 log_debug("update_map_canvas(pos=(%d,%d), size=(%d,%d))",
1341 canvas_x, canvas_y, width, height);
1343 /* If a full redraw is done, we just draw everything onto the canvas.
1344 * However if a partial redraw is done we draw everything onto the
1345 * tmp_canvas then copy *just* the area of update onto the canvas. */
1346 if (!full) {
1347 /* Swap store and tmp_store. */
1348 tmp = mapview.store;
1349 mapview.store = mapview.tmp_store;
1350 mapview.tmp_store = tmp;
1353 /* Clear the area. This is necessary since some parts of the rectangle
1354 * may not actually have any tiles drawn on them. This will happen when
1355 * the mapview is large enough so that the tile is visible in multiple
1356 * locations. In this case it will only be drawn in one place.
1358 * Of course it's necessary to draw to the whole area to cover up any old
1359 * drawing that was done there. */
1360 canvas_put_rectangle(mapview.store,
1361 get_color(tileset, COLOR_MAPVIEW_UNKNOWN),
1362 canvas_x, canvas_y, width, height);
1364 mapview_layer_iterate(layer) {
1365 if (layer == LAYER_TILELABEL) {
1366 show_tile_labels(canvas_x, canvas_y, width, height);
1368 if (layer == LAYER_CITYBAR) {
1369 show_city_descriptions(canvas_x, canvas_y, width, height);
1370 continue;
1372 gui_rect_iterate_coord(gui_x0, gui_y0, width,
1373 height + (tileset_is_isometric(tileset)
1374 ? (tileset_tile_height(tileset) / 2 * map_zoom) : 0),
1375 ptile, pedge, pcorner, gui_x, gui_y, map_zoom) {
1376 const int cx = gui_x - mapview.gui_x0, cy = gui_y - mapview.gui_y0;
1378 if (ptile) {
1379 put_one_tile(mapview.store, layer, ptile, cx, cy, NULL);
1380 } else if (pedge) {
1381 put_one_element(mapview.store, map_zoom, layer, NULL, pedge, NULL,
1382 NULL, NULL, cx, cy, NULL, NULL);
1383 } else if (pcorner) {
1384 put_one_element(mapview.store, map_zoom, layer, NULL, NULL, pcorner,
1385 NULL, NULL, cx, cy, NULL, NULL);
1386 } else {
1387 /* This can happen, for instance for unreal tiles. */
1389 } gui_rect_iterate_coord_end;
1390 } mapview_layer_iterate_end;
1392 draw_trade_routes();
1393 link_marks_draw_all();
1395 /* Draw the goto lines on top of the whole thing. This is done last as
1396 * we want it completely on top.
1398 * Note that a pixel right on the border of a tile may actually contain a
1399 * goto line from an adjacent tile. Thus we draw any extra goto lines
1400 * from adjacent tiles (if they're close enough). */
1401 gui_rect_iterate(gui_x0 - GOTO_WIDTH, gui_y0 - GOTO_WIDTH,
1402 width + 2 * GOTO_WIDTH, height + 2 * GOTO_WIDTH,
1403 ptile, pedge, pcorner, map_zoom) {
1404 if (!ptile) {
1405 continue;
1407 adjc_dir_base_iterate(ptile, dir) {
1408 if (mapdeco_is_gotoline_set(ptile, dir)) {
1409 draw_segment(ptile, dir);
1411 } adjc_dir_base_iterate_end;
1412 } gui_rect_iterate_end;
1414 if (!full) {
1415 /* Swap store and tmp_store back. */
1416 tmp = mapview.store;
1417 mapview.store = mapview.tmp_store;
1418 mapview.tmp_store = tmp;
1420 /* And copy store to tmp_store. */
1421 canvas_copy(mapview.store, mapview.tmp_store,
1422 canvas_x, canvas_y, canvas_x, canvas_y, width, height);
1425 dirty_rect(canvas_x, canvas_y, width, height);
1428 /**************************************************************************
1429 Update (only) the visible part of the map
1430 **************************************************************************/
1431 void update_map_canvas_visible(void)
1433 queue_mapview_update(UPDATE_MAP_CANVAS_VISIBLE);
1436 /* The maximum city description width and height. This gives the dimensions
1437 * of a rectangle centered directly beneath the tile a city is on, that
1438 * contains the city description.
1440 * These values are increased when drawing is done. This may mean that
1441 * the change (from increasing the value) won't take place until the
1442 * next redraw. */
1443 static int max_desc_width = 0, max_desc_height = 0;
1445 /* Same for tile labels */
1446 static int max_label_width = 0, max_label_height = 0 ;
1448 /**************************************************************************
1449 Update the city description for the given city.
1450 **************************************************************************/
1451 void update_city_description(struct city *pcity)
1453 queue_mapview_tile_update(pcity->tile, TILE_UPDATE_CITY_DESC);
1456 /**************************************************************************
1457 Update the label for the given tile
1458 **************************************************************************/
1459 void update_tile_label(struct tile *ptile)
1461 queue_mapview_tile_update(ptile, TILE_UPDATE_TILE_LABEL);
1464 /****************************************************************************
1465 Draw a "full" city bar for the city. This is a subcase of show_city_desc
1466 (see that function for more info) for tilesets that have a full city bar.
1467 ****************************************************************************/
1468 static void show_full_citybar(struct canvas *pcanvas,
1469 const int canvas_x0, const int canvas_y0,
1470 struct city *pcity, int *width, int *height)
1472 const struct citybar_sprites *citybar = get_citybar_sprites(tileset);
1473 static char name[512], growth[32], prod[512], size[32], trade_routes[32];
1474 enum color_std growth_color;
1475 /* trade_routes_color initialized just to get rid off gcc warning
1476 * on optimization level 3 when it misdiagnoses that it would be used
1477 * uninitialized otherwise. Funny thing here is that warning would
1478 * go away also by *not* setting it to values other than
1479 * COLOR_MAPVIEW_CITYTEXT in get_city_mapview_trade_routes() */
1480 enum color_std trade_routes_color = COLOR_MAPVIEW_CITYTEXT;
1481 struct color *owner_color;
1482 struct {
1483 int x, y, w, h;
1484 } name_rect = {0, 0, 0, 0}, growth_rect = {0, 0, 0, 0},
1485 prod_rect = {0, 0, 0, 0}, size_rect = {0, 0, 0, 0},
1486 flag_rect = {0, 0, 0, 0}, occupy_rect = {0, 0, 0, 0},
1487 food_rect = {0, 0, 0, 0}, shield_rect = {0, 0, 0, 0},
1488 trade_routes_rect = {0,}, trade_rect = {0,};
1489 int width1 = 0, width2 = 0, height1 = 0, height2 = 0;
1490 struct sprite *bg = citybar->background;
1491 struct sprite *flag = get_city_flag_sprite(tileset, pcity);
1492 struct sprite *occupy = NULL;
1493 int bg_w, bg_h, x, y;
1494 const int canvas_x = canvas_x0 + tileset_tile_width(tileset) / 2 * map_zoom;
1495 const int canvas_y = canvas_y0 + tileset_citybar_offset_y(tileset) * map_zoom;
1496 const int border = 6;
1497 const enum client_font FONT_CITY_SIZE = FONT_CITY_NAME; /* TODO: new font */
1499 /* We can see the city's production or growth values if
1500 * we are observing or playing as the owner of the city. */
1501 const bool can_see_inside
1502 = (client_is_global_observer() || city_owner(pcity) == client_player());
1503 const bool should_draw_productions
1504 = can_see_inside && gui_options.draw_city_productions;
1505 const bool should_draw_growth = can_see_inside && gui_options.draw_city_growth;
1506 const bool should_draw_trade_routes = can_see_inside
1507 && gui_options.draw_city_trade_routes;
1508 const bool should_draw_lower_bar
1509 = should_draw_productions || should_draw_growth
1510 || should_draw_trade_routes;
1513 if (width != NULL) {
1514 *width = 0;
1516 if (height != NULL) {
1517 *height = 0;
1520 if (!gui_options.draw_city_names && !should_draw_lower_bar) {
1521 return;
1525 /* First: calculate rect dimensions (but not positioning). */
1527 get_sprite_dimensions(bg, &bg_w, &bg_h);
1528 get_city_mapview_name_and_growth(pcity, name, sizeof(name),
1529 growth, sizeof(growth), &growth_color);
1531 if (gui_options.draw_city_names) {
1532 fc_snprintf(size, sizeof(size), "%d", city_size_get(pcity));
1534 get_text_size(&size_rect.w, &size_rect.h, FONT_CITY_SIZE, size);
1535 get_text_size(&name_rect.w, &name_rect.h, FONT_CITY_NAME, name);
1537 if (can_player_see_units_in_city(client.conn.playing, pcity)) {
1538 int count = unit_list_size(pcity->tile->units);
1540 count = CLIP(0, count, citybar->occupancy.size - 1);
1541 occupy = citybar->occupancy.p[count];
1542 } else {
1543 if (pcity->client.occupied) {
1544 occupy = citybar->occupied;
1545 } else {
1546 occupy = citybar->occupancy.p[0];
1550 get_sprite_dimensions(flag, &flag_rect.w, &flag_rect.h);
1551 get_sprite_dimensions(occupy, &occupy_rect.w, &occupy_rect.h);
1553 width1 = (flag_rect.w + occupy_rect.w + name_rect.w
1554 + 2 * border + size_rect.w);
1555 height1 = MAX(flag_rect.h,
1556 MAX(occupy_rect.h,
1557 MAX(name_rect.h + border,
1558 size_rect.h + border)));
1561 if (should_draw_lower_bar) {
1562 width2 = 0;
1563 height2 = 0;
1565 if (should_draw_productions) {
1566 get_city_mapview_production(pcity, prod, sizeof(prod));
1567 get_text_size(&prod_rect.w, &prod_rect.h, FONT_CITY_PROD, prod);
1569 get_sprite_dimensions(citybar->shields, &shield_rect.w, &shield_rect.h);
1570 width2 += shield_rect.w + prod_rect.w + border;
1571 height2 = MAX(height2, shield_rect.h);
1572 height2 = MAX(height2, prod_rect.h + border);
1575 if (should_draw_growth) {
1576 get_text_size(&growth_rect.w, &growth_rect.h, FONT_CITY_PROD, growth);
1577 get_sprite_dimensions(citybar->food, &food_rect.w, &food_rect.h);
1578 width2 += food_rect.w + growth_rect.w + border;
1579 height2 = MAX(height2, food_rect.h);
1580 height2 = MAX(height2, growth_rect.h + border);
1583 if (should_draw_trade_routes) {
1584 get_city_mapview_trade_routes(pcity, trade_routes,
1585 sizeof(trade_routes),
1586 &trade_routes_color);
1587 get_text_size(&trade_routes_rect.w, &trade_routes_rect.h,
1588 FONT_CITY_PROD, trade_routes);
1589 get_sprite_dimensions(citybar->trade, &trade_rect.w, &trade_rect.h);
1590 width2 += trade_rect.w + trade_routes_rect.w + border;
1591 height2 = MAX(height2, trade_rect.h);
1592 height2 = MAX(height2, trade_routes_rect.h + border);
1596 *width = MAX(width1, width2);
1597 *height = height1 + height2;
1600 /* Next fill in X and Y locations. */
1602 if (gui_options.draw_city_names) {
1603 flag_rect.x = canvas_x - *width / 2;
1604 flag_rect.y = canvas_y + (height1 - flag_rect.h) / 2;
1606 occupy_rect.x = flag_rect.x + flag_rect.w;
1607 occupy_rect.y = canvas_y + (height1 - occupy_rect.h) / 2;
1609 name_rect.x = canvas_x + (flag_rect.w + occupy_rect.w
1610 - name_rect.w - size_rect.w - border) / 2;
1611 name_rect.y = canvas_y + (height1 - name_rect.h) / 2;
1613 size_rect.x = canvas_x + (*width + 1) / 2 - size_rect.w - border / 2;
1614 size_rect.y = canvas_y + (height1 - size_rect.h) / 2;
1617 if (should_draw_lower_bar) {
1618 if (should_draw_productions) {
1619 shield_rect.x = canvas_x - *width / 2;
1620 shield_rect.y = canvas_y + height1 + (height2 - shield_rect.h) / 2;
1622 prod_rect.x = shield_rect.x + shield_rect.w + border / 2;
1623 prod_rect.y = canvas_y + height1 + (height2 - prod_rect.h) / 2;
1626 if (should_draw_trade_routes) {
1627 trade_routes_rect.x = canvas_x + (*width + 1) / 2
1628 - trade_routes_rect.w - border / 2;
1629 trade_routes_rect.y = canvas_y + height1
1630 + (height2 - trade_routes_rect.h) / 2;
1632 trade_rect.x = trade_routes_rect.x - border / 2 - trade_rect.w;
1633 trade_rect.y = canvas_y + height1 + (height2 - trade_rect.h) / 2;
1636 if (should_draw_growth) {
1637 growth_rect.x = canvas_x + (*width + 1) / 2
1638 - growth_rect.w - border / 2;
1639 if (trade_routes_rect.w > 0) {
1640 growth_rect.x = growth_rect.x
1641 - trade_routes_rect.w - border / 2 - trade_rect.w - border / 2;
1643 growth_rect.y = canvas_y + height1 + (height2 - growth_rect.h) / 2;
1645 food_rect.x = growth_rect.x - border / 2 - food_rect.w;
1646 food_rect.y = canvas_y + height1 + (height2 - food_rect.h) / 2;
1651 /* Now draw. */
1653 /* Draw the city bar's background. */
1654 for (x = 0; x < *width; x += bg_w) {
1655 for (y = 0; y < *height; y += bg_h) {
1656 canvas_put_sprite(pcanvas, (canvas_x - *width / 2 + x) / map_zoom,
1657 (canvas_y + y) / map_zoom,
1658 bg, 0, 0, *width - x, *height - y);
1662 owner_color = get_player_color(tileset, city_owner(pcity));
1664 if (gui_options.draw_city_names) {
1665 canvas_put_sprite_full(pcanvas, flag_rect.x / map_zoom, flag_rect.y / map_zoom,
1666 flag);
1667 canvas_put_line(pcanvas, owner_color, LINE_NORMAL,
1668 (flag_rect.x + flag_rect.w) / map_zoom - 1, canvas_y / map_zoom,
1669 0, height1);
1670 canvas_put_sprite_full(pcanvas,
1671 occupy_rect.x / map_zoom, occupy_rect.y / map_zoom,
1672 occupy);
1673 canvas_put_text(pcanvas, name_rect.x / map_zoom, name_rect.y / map_zoom,
1674 FONT_CITY_NAME,
1675 get_color(tileset, COLOR_MAPVIEW_CITYTEXT),
1676 name);
1678 canvas_put_rectangle(pcanvas, owner_color,
1679 (size_rect.x - border / 2) / map_zoom,
1680 canvas_y / map_zoom,
1681 size_rect.w + border, height1);
1683 /* Try to pick a color for city size text that contrasts with
1684 * player color */
1685 struct color *textcolors[2] = {
1686 get_color(tileset, COLOR_MAPVIEW_CITYTEXT),
1687 get_color(tileset, COLOR_MAPVIEW_CITYTEXT_DARK)
1690 canvas_put_text(pcanvas, size_rect.x / map_zoom, size_rect.y / map_zoom,
1691 FONT_CITY_NAME,
1692 color_best_contrast(owner_color, textcolors,
1693 ARRAY_SIZE(textcolors)), size);
1697 if (should_draw_lower_bar) {
1699 if (should_draw_productions) {
1700 canvas_put_sprite_full(pcanvas,
1701 shield_rect.x / map_zoom, shield_rect.y / map_zoom,
1702 citybar->shields);
1703 canvas_put_text(pcanvas, prod_rect.x / map_zoom, prod_rect.y / map_zoom,
1704 FONT_CITY_PROD,
1705 get_color(tileset, COLOR_MAPVIEW_CITYTEXT), prod);
1708 if (should_draw_trade_routes) {
1709 canvas_put_sprite_full(pcanvas,
1710 trade_rect.x / map_zoom, trade_rect.y / map_zoom,
1711 citybar->trade);
1712 canvas_put_text(pcanvas,
1713 trade_routes_rect.x / map_zoom, trade_routes_rect.y / map_zoom,
1714 FONT_CITY_PROD,
1715 get_color(tileset, trade_routes_color), trade_routes);
1718 if (should_draw_growth) {
1719 canvas_put_sprite_full(pcanvas,
1720 food_rect.x / map_zoom, food_rect.y / map_zoom,
1721 citybar->food);
1722 canvas_put_text(pcanvas, growth_rect.x / map_zoom, growth_rect.y / map_zoom,
1723 FONT_CITY_PROD,
1724 get_color(tileset, growth_color), growth);
1728 /* Draw the city bar's outline. */
1729 canvas_put_line(pcanvas, owner_color, LINE_NORMAL,
1730 (canvas_x - *width / 2) / map_zoom, canvas_y / map_zoom,
1731 *width, 0);
1732 canvas_put_line(pcanvas, owner_color, LINE_NORMAL,
1733 (canvas_x - *width / 2) / map_zoom, canvas_y / map_zoom,
1734 0, *height);
1735 canvas_put_line(pcanvas, owner_color, LINE_NORMAL,
1736 (canvas_x - *width / 2) / map_zoom,
1737 (canvas_y + *height) / map_zoom - 1,
1738 *width, 0);
1739 canvas_put_line(pcanvas, owner_color, LINE_NORMAL,
1740 (canvas_x - *width / 2 + *width) / map_zoom,
1741 canvas_y / map_zoom,
1742 0, *height);
1744 /* Draw the dividing line if we drew both the
1745 * upper and lower parts. */
1746 if (gui_options.draw_city_names && should_draw_lower_bar) {
1747 canvas_put_line(pcanvas, owner_color, LINE_NORMAL,
1748 (canvas_x - *width / 2) / map_zoom,
1749 (canvas_y + height1) / map_zoom - 1,
1750 *width, 0);
1754 /****************************************************************************
1755 Draw a "small" city bar for the city. This is a subcase of show_city_desc
1756 (see that function for more info) for tilesets that do not have a full
1757 city bar.
1758 ****************************************************************************/
1759 static void show_small_citybar(struct canvas *pcanvas,
1760 int canvas_x, int canvas_y,
1761 struct city *pcity, int *width, int *height)
1763 static char name[512], growth[32], prod[512], trade_routes[32];
1764 enum color_std growth_color;
1765 /* trade_routes_color initialized just to get rid off gcc warning
1766 * on optimization level 3 when it misdiagnoses that it would be used
1767 * uninitialized otherwise. Funny thing here is that warning would
1768 * go away also by *not* setting it to values other than
1769 * COLOR_MAPVIEW_CITYTEXT in get_city_mapview_trade_routes() */
1770 enum color_std trade_routes_color = COLOR_MAPVIEW_CITYTEXT;
1771 struct {
1772 int x, y, w, h;
1773 } name_rect = {0, 0, 0, 0}, growth_rect = {0, 0, 0, 0},
1774 prod_rect = {0, 0, 0, 0}, trade_routes_rect = {0,};
1775 int total_width, total_height;
1776 int spacer_width = 0;
1777 const bool can_see_inside = (client_is_global_observer()
1778 || city_owner(pcity) == client_player());
1780 *width = *height = 0;
1782 canvas_x += tileset_tile_width(tileset) / 2 * map_zoom;
1783 canvas_y += tileset_citybar_offset_y(tileset) * map_zoom;
1785 if (gui_options.draw_city_names) {
1786 int drawposx;
1788 /* HACK: put a character's worth of space between the two
1789 * strings if needed. */
1790 get_text_size(&spacer_width, NULL, FONT_CITY_NAME, "M");
1792 total_width = 0;
1793 total_height = 0;
1795 get_city_mapview_name_and_growth(pcity, name, sizeof(name),
1796 growth, sizeof(growth), &growth_color);
1798 get_text_size(&name_rect.w, &name_rect.h, FONT_CITY_NAME, name);
1799 total_width += name_rect.w;
1800 total_height = MAX(total_height, name_rect.h);
1802 if (gui_options.draw_city_growth && can_see_inside) {
1803 get_text_size(&growth_rect.w, &growth_rect.h, FONT_CITY_PROD, growth);
1804 total_width += spacer_width + growth_rect.w;
1805 total_height = MAX(total_height, growth_rect.h);
1808 if (gui_options.draw_city_trade_routes && can_see_inside) {
1809 get_city_mapview_trade_routes(pcity, trade_routes,
1810 sizeof(trade_routes),
1811 &trade_routes_color);
1812 get_text_size(&trade_routes_rect.w, &trade_routes_rect.h,
1813 FONT_CITY_PROD, trade_routes);
1814 total_width += spacer_width + trade_routes_rect.w;
1815 total_height = MAX(total_height, trade_routes_rect.h);
1818 drawposx = canvas_x;
1819 drawposx -= total_width / 2;
1820 canvas_put_text(pcanvas, drawposx / map_zoom, canvas_y / map_zoom,
1821 FONT_CITY_NAME,
1822 get_color(tileset, COLOR_MAPVIEW_CITYTEXT), name);
1823 drawposx += name_rect.w;
1825 if (gui_options.draw_city_growth && can_see_inside) {
1826 drawposx += spacer_width;
1827 canvas_put_text(pcanvas, drawposx / map_zoom,
1828 (canvas_y + total_height - growth_rect.h) / map_zoom,
1829 FONT_CITY_PROD,
1830 get_color(tileset, growth_color), growth);
1831 drawposx += growth_rect.w;
1834 if (gui_options.draw_city_trade_routes && can_see_inside) {
1835 drawposx += spacer_width;
1836 canvas_put_text(pcanvas, drawposx / map_zoom,
1837 (canvas_y + total_height - trade_routes_rect.h) / map_zoom,
1838 FONT_CITY_PROD,
1839 get_color(tileset, trade_routes_color), trade_routes);
1840 drawposx += trade_routes_rect.w;
1843 canvas_y += total_height + 3;
1845 *width = MAX(*width, total_width);
1846 *height += total_height + 3;
1848 if (gui_options.draw_city_productions && can_see_inside) {
1849 get_city_mapview_production(pcity, prod, sizeof(prod));
1850 get_text_size(&prod_rect.w, &prod_rect.h, FONT_CITY_PROD, prod);
1852 total_width = prod_rect.w;
1853 total_height = prod_rect.h;
1855 canvas_put_text(pcanvas, (canvas_x - total_width / 2) / map_zoom,
1856 canvas_y / map_zoom,
1857 FONT_CITY_PROD,
1858 get_color(tileset, COLOR_MAPVIEW_CITYTEXT), prod);
1860 canvas_y += total_height;
1861 *width = MAX(*width, total_width);
1862 *height += total_height;
1866 /****************************************************************************
1867 Draw a description for the given city. This description may include the
1868 name, turns-to-grow, production, and city turns-to-build (depending on
1869 client options).
1871 (canvas_x, canvas_y) gives the location on the given canvas at which to
1872 draw the description. This is the location of the city itself so the
1873 text must be drawn underneath it. pcity gives the city to be drawn,
1874 while (*width, *height) should be set by show_city_desc to contain the
1875 width and height of the text block (centered directly underneath the
1876 city's tile).
1877 ****************************************************************************/
1878 static void show_city_desc(struct canvas *pcanvas,
1879 int canvas_x, int canvas_y,
1880 struct city *pcity, int *width, int *height)
1882 if (gui_options.draw_full_citybar) {
1883 show_full_citybar(pcanvas, canvas_x, canvas_y, pcity, width, height);
1884 } else {
1885 show_small_citybar(pcanvas, canvas_x, canvas_y, pcity, width, height);
1889 /****************************************************************************
1890 Draw a label for the given tile.
1892 (canvas_x, canvas_y) gives the location on the given canvas at which to
1893 draw the label. This is the location of the tile itself so the
1894 text must be drawn underneath it. pcity gives the city to be drawn,
1895 while (*width, *height) should be set by show_tile_label to contain the
1896 width and height of the text block (centered directly underneath the
1897 city's tile).
1898 ****************************************************************************/
1899 static void show_tile_label(struct canvas *pcanvas,
1900 int canvas_x, int canvas_y,
1901 struct tile *ptile, int *width, int *height)
1903 const enum client_font FONT_TILE_LABEL = FONT_CITY_NAME; /* TODO: new font */
1904 #define COLOR_MAPVIEW_TILELABEL COLOR_MAPVIEW_CITYTEXT
1906 canvas_x += tileset_tile_width(tileset) / 2 * map_zoom;
1907 canvas_y += tileset_tilelabel_offset_y(tileset) * map_zoom;
1909 get_text_size(width, height, FONT_TILE_LABEL, ptile->label);
1911 canvas_put_text(pcanvas, (canvas_x - * width / 2) / map_zoom, canvas_y / map_zoom,
1912 FONT_TILE_LABEL,
1913 get_color(tileset, COLOR_MAPVIEW_TILELABEL), ptile->label);
1914 #undef COLOR_MAPVIEW_TILELABEL
1917 /**************************************************************************
1918 Show descriptions for all cities visible on the map canvas.
1919 **************************************************************************/
1920 void show_city_descriptions(int canvas_base_x, int canvas_base_y,
1921 int width_base, int height_base)
1923 const int dx = max_desc_width - tileset_tile_width(tileset) * map_zoom;
1924 const int dy = max_desc_height;
1925 const int offset_y = tileset_citybar_offset_y(tileset) * map_zoom;
1926 int new_max_width = max_desc_width, new_max_height = max_desc_height;
1928 if (gui_options.draw_full_citybar && !(gui_options.draw_city_names
1929 || gui_options.draw_city_productions
1930 || gui_options.draw_city_growth)) {
1931 return;
1934 if (!gui_options.draw_full_citybar && !(gui_options.draw_city_names
1935 || gui_options.draw_city_productions)) {
1936 return;
1939 /* A city description is shown below the city. It has a specified
1940 * maximum width and height (although these are only estimates). Thus
1941 * we need to update some tiles above the mapview and some to the left
1942 * and right.
1944 * /--W1--\ (W1 = tileset_tile_width(tileset))
1945 * -------- \
1946 * | CITY | H1 (H1 = tileset_tile_height(tileset))
1947 * | | /
1948 * ------------------ \
1949 * | DESCRIPTION | H2 (H2 = MAX_CITY_DESC_HEIGHT)
1950 * | | /
1951 * ------------------
1952 * \-------W2-------/ (W2 = MAX_CITY_DESC_WIDTH)
1954 * We must draw H2 extra pixels above and (W2 - W1) / 2 extra pixels
1955 * to each side of the mapview.
1957 gui_rect_iterate_coord(mapview.gui_x0 + canvas_base_x - dx / 2,
1958 mapview.gui_y0 + canvas_base_y - dy,
1959 width_base + dx, height_base + dy - offset_y,
1960 ptile, pedge, pcorner, gui_x, gui_y, map_zoom) {
1961 const int canvas_x = gui_x - mapview.gui_x0;
1962 const int canvas_y = gui_y - mapview.gui_y0;
1964 if (ptile && tile_city(ptile)) {
1965 int width = 0, height = 0;
1966 struct city *pcity = tile_city(ptile);
1968 show_city_desc(mapview.store, canvas_x, canvas_y,
1969 pcity, &width, &height);
1970 log_debug("Drawing %s.", city_name_get(pcity));
1972 if (width > max_desc_width || height > max_desc_height) {
1973 /* The update was incomplete! We queue a new update. Note that
1974 * this is recursively queueing an update within a dequeuing of an
1975 * update. This is allowed specifically because of the code in
1976 * unqueue_mapview_updates. See that function for more. */
1977 log_debug("Re-queuing %s.", city_name_get(pcity));
1978 update_city_description(pcity);
1980 new_max_width = MAX(width, new_max_width);
1981 new_max_height = MAX(height, new_max_height);
1983 } gui_rect_iterate_coord_end;
1985 /* We don't update the new max values until the end, so that the
1986 * check above to see what cities need redrawing will be complete. */
1987 max_desc_width = MAX(max_desc_width, new_max_width);
1988 max_desc_height = MAX(max_desc_height, new_max_height);
1991 /**************************************************************************
1992 Show labels for all tiles visible on the map canvas.
1993 **************************************************************************/
1994 void show_tile_labels(int canvas_base_x, int canvas_base_y,
1995 int width_base, int height_base)
1997 const int dx = max_label_width - tileset_tile_width(tileset) * map_zoom;
1998 const int dy = max_label_height;
1999 int new_max_width = max_label_width, new_max_height = max_label_height;
2001 gui_rect_iterate_coord(mapview.gui_x0 + canvas_base_x - dx / 2,
2002 mapview.gui_y0 + canvas_base_y - dy,
2003 width_base + dx, height_base + dy,
2004 ptile, pedge, pcorner, gui_x, gui_y, map_zoom) {
2005 const int canvas_x = gui_x - mapview.gui_x0;
2006 const int canvas_y = gui_y - mapview.gui_y0;
2008 if (ptile && ptile->label != NULL) {
2009 int width = 0, height = 0;
2011 show_tile_label(mapview.store, canvas_x, canvas_y,
2012 ptile, &width, &height);
2013 log_debug("Drawing label %s.", ptile->label);
2015 if (width > max_label_width || height > max_label_height) {
2016 /* The update was incomplete! We queue a new update. Note that
2017 * this is recursively queueing an update within a dequeuing of an
2018 * update. This is allowed specifically because of the code in
2019 * unqueue_mapview_updates. See that function for more. */
2020 log_debug("Re-queuing tile label %s drawing.", ptile->label);
2021 update_tile_label(ptile);
2023 new_max_width = MAX(width, new_max_width);
2024 new_max_height = MAX(height, new_max_height);
2026 } gui_rect_iterate_coord_end;
2028 /* We don't update the new max values until the end, so that the
2029 * check above to see what cities need redrawing will be complete. */
2030 max_label_width = MAX(max_label_width, new_max_width);
2031 max_label_height = MAX(max_label_height, new_max_height);
2034 /****************************************************************************
2035 Draw the goto route for the unit. Return TRUE if anything is drawn.
2037 This duplicates drawing code that is run during the hover state.
2038 ****************************************************************************/
2039 bool show_unit_orders(struct unit *punit)
2041 if (punit && unit_has_orders(punit)) {
2042 struct tile *ptile = unit_tile(punit);
2043 int i;
2045 for (i = 0; i < punit->orders.length; i++) {
2046 int idx = (punit->orders.index + i) % punit->orders.length;
2047 struct unit_order *order;
2049 if (punit->orders.index + i >= punit->orders.length
2050 && !punit->orders.repeat) {
2051 break;
2054 order = &punit->orders.list[idx];
2056 switch (order->order) {
2057 case ORDER_MOVE:
2058 draw_segment(ptile, order->dir);
2059 ptile = mapstep(ptile, order->dir);
2060 if (!ptile) {
2061 /* This shouldn't happen unless the server gives us invalid
2062 * data. To avoid disaster we need to break out of the
2063 * switch and the enclosing for loop. */
2064 fc_assert(NULL != ptile);
2065 i = punit->orders.length;
2067 break;
2068 default:
2069 /* TODO: graphics for other orders. */
2070 break;
2073 return TRUE;
2074 } else {
2075 return FALSE;
2079 /****************************************************************************
2080 Draw a goto line at the given location and direction. The line goes from
2081 the source tile to the adjacent tile in the given direction.
2082 ****************************************************************************/
2083 void draw_segment(struct tile *src_tile, enum direction8 dir)
2085 float canvas_x, canvas_y, canvas_dx, canvas_dy;
2087 /* Determine the source position of the segment. */
2088 (void) tile_to_canvas_pos(&canvas_x, &canvas_y, src_tile);
2089 canvas_x += tileset_tile_width(tileset) / 2 * map_zoom;
2090 canvas_y += tileset_tile_height(tileset) / 2 * map_zoom;
2092 /* Determine the vector of the segment. */
2093 map_to_gui_vector(tileset, map_zoom, &canvas_dx, &canvas_dy,
2094 DIR_DX[dir], DIR_DY[dir]);
2096 /* Draw the segment. */
2097 canvas_put_line(mapview.store,
2098 get_color(tileset, COLOR_MAPVIEW_GOTO), LINE_GOTO,
2099 canvas_x, canvas_y, canvas_dx, canvas_dy);
2101 /* The actual area drawn will extend beyond the base rectangle, since
2102 * the goto lines have width. */
2103 dirty_rect(MIN(canvas_x, canvas_x + canvas_dx) - GOTO_WIDTH,
2104 MIN(canvas_y, canvas_y + canvas_dy) - GOTO_WIDTH,
2105 ABS(canvas_dx) + 2 * GOTO_WIDTH,
2106 ABS(canvas_dy) + 2 * GOTO_WIDTH);
2108 /* It is possible that the mapview wraps between the source and dest
2109 * tiles. In this case they will not be next to each other; they'll be
2110 * on the opposite sides of the screen. If this happens then the dest
2111 * tile will not be updated. This is consistent with the mapview design
2112 * which fails when the size of the mapview approaches that of the map. */
2115 /****************************************************************************
2116 This function is called to decrease a unit's HP smoothly in battle
2117 when combat_animation is turned on.
2118 ****************************************************************************/
2119 void decrease_unit_hp_smooth(struct unit *punit0, int hp0,
2120 struct unit *punit1, int hp1)
2122 static struct timer *anim_timer = NULL;
2123 const struct sprite_vector *anim = get_unit_explode_animation(tileset);
2124 const int num_tiles_explode_unit = sprite_vector_size(anim);
2125 struct unit *losing_unit = (hp0 == 0 ? punit0 : punit1);
2126 float canvas_x, canvas_y;
2127 int i;
2129 set_units_in_combat(punit0, punit1);
2131 /* Make sure we don't start out with fewer HP than we're supposed to
2132 * end up with (which would cause the following loop to break). */
2133 punit0->hp = MAX(punit0->hp, hp0);
2134 punit1->hp = MAX(punit1->hp, hp1);
2136 unqueue_mapview_updates(TRUE);
2137 while (punit0->hp > hp0 || punit1->hp > hp1) {
2138 const int diff0 = punit0->hp - hp0, diff1 = punit1->hp - hp1;
2140 anim_timer = timer_renew(anim_timer, TIMER_USER, TIMER_ACTIVE);
2141 timer_start(anim_timer);
2143 if (fc_rand(diff0 + diff1) < diff0) {
2144 punit0->hp--;
2145 refresh_unit_mapcanvas(punit0, unit_tile(punit0), FALSE, FALSE);
2146 } else {
2147 punit1->hp--;
2148 refresh_unit_mapcanvas(punit1, unit_tile(punit1), FALSE, FALSE);
2151 unqueue_mapview_updates(TRUE);
2152 gui_flush();
2154 timer_usleep_since_start(anim_timer,
2155 gui_options.smooth_combat_step_msec * 1000ul);
2158 if (num_tiles_explode_unit > 0
2159 && tile_to_canvas_pos(&canvas_x, &canvas_y,
2160 unit_tile(losing_unit))) {
2161 refresh_unit_mapcanvas(losing_unit, unit_tile(losing_unit), FALSE, FALSE);
2162 unqueue_mapview_updates(FALSE);
2163 canvas_copy(mapview.tmp_store, mapview.store,
2164 canvas_x, canvas_y, canvas_x, canvas_y,
2165 tileset_tile_width(tileset) * map_zoom,
2166 tileset_tile_height(tileset) * map_zoom);
2168 for (i = 0; i < num_tiles_explode_unit; i++) {
2169 int w, h;
2170 struct sprite *sprite = *sprite_vector_get(anim, i);
2172 get_sprite_dimensions(sprite, &w, &h);
2173 anim_timer = timer_renew(anim_timer, TIMER_USER, TIMER_ACTIVE);
2174 timer_start(anim_timer);
2176 /* We first draw the explosion onto the unit and draw draw the
2177 * complete thing onto the map canvas window. This avoids
2178 * flickering. */
2179 canvas_copy(mapview.store, mapview.tmp_store,
2180 canvas_x, canvas_y, canvas_x, canvas_y,
2181 tileset_tile_width(tileset) * map_zoom,
2182 tileset_tile_height(tileset) * map_zoom);
2183 canvas_put_sprite_full(mapview.store,
2184 canvas_x + tileset_tile_width(tileset) / 2 * map_zoom
2185 - w / 2,
2186 canvas_y + tileset_tile_height(tileset) / 2 * map_zoom
2187 - h / 2,
2188 sprite);
2189 dirty_rect(canvas_x, canvas_y, tileset_tile_width(tileset) * map_zoom,
2190 tileset_tile_height(tileset) * map_zoom);
2192 flush_dirty();
2193 gui_flush();
2195 timer_usleep_since_start(anim_timer,
2196 gui_options.smooth_combat_step_msec * 2 * 1000ul);
2200 set_units_in_combat(NULL, NULL);
2201 refresh_unit_mapcanvas(punit0, unit_tile(punit0), TRUE, FALSE);
2202 refresh_unit_mapcanvas(punit1, unit_tile(punit1), TRUE, FALSE);
2205 /**************************************************************************
2206 Animates punit's "smooth" move from (x0, y0) to (x0+dx, y0+dy).
2207 Note: Works only for adjacent-tile moves.
2208 **************************************************************************/
2209 void move_unit_map_canvas(struct unit *punit,
2210 struct tile *src_tile, int dx, int dy)
2212 static struct timer *anim_timer = NULL;
2213 struct tile *dest_tile;
2214 int dest_x, dest_y, src_x, src_y;
2215 int prev_x = -1;
2216 int prev_y = -1;
2217 int tuw;
2218 int tuh;
2220 /* only works for adjacent-square moves */
2221 if (dx < -1 || dx > 1 || dy < -1 || dy > 1 || (dx == 0 && dy == 0)) {
2222 return;
2225 index_to_map_pos(&src_x, &src_y, tile_index(src_tile));
2226 dest_x = src_x + dx;
2227 dest_y = src_y + dy;
2228 dest_tile = map_pos_to_tile(dest_x, dest_y);
2229 if (!dest_tile) {
2230 return;
2233 if (tile_visible_mapcanvas(src_tile)
2234 || tile_visible_mapcanvas(dest_tile)) {
2235 float start_x, start_y;
2236 float canvas_dx, canvas_dy;
2237 double timing_sec = (double)gui_options.smooth_move_unit_msec / 1000.0;
2238 double mytime;
2240 fc_assert(gui_options.smooth_move_unit_msec > 0);
2242 map_to_gui_vector(tileset, map_zoom, &canvas_dx, &canvas_dy, dx, dy);
2244 tile_to_canvas_pos(&start_x, &start_y, src_tile);
2245 if (tileset_is_isometric(tileset) && tileset_hex_height(tileset) == 0) {
2246 start_y -= tileset_tile_height(tileset) / 2 * map_zoom;
2247 start_y -= (tileset_unit_height(tileset) - tileset_full_tile_height(tileset)) * map_zoom;
2250 /* Bring the backing store up to date, but don't flush. */
2251 unqueue_mapview_updates(FALSE);
2253 /* Start the timer (AFTER the unqueue above). */
2254 anim_timer = timer_renew(anim_timer, TIMER_USER, TIMER_ACTIVE);
2255 timer_start(anim_timer);
2257 tuw = tileset_unit_width(tileset) * map_zoom;
2258 tuh = tileset_unit_height(tileset) * map_zoom;
2260 do {
2261 int new_x, new_y;
2263 mytime = MIN(timer_read_seconds(anim_timer), timing_sec);
2265 new_x = start_x + canvas_dx * (mytime / timing_sec);
2266 new_y = start_y + canvas_dy * (mytime / timing_sec);
2268 if (new_x != prev_x || new_y != prev_y) {
2269 /* Backup the canvas store to the temp store. */
2270 canvas_copy(mapview.tmp_store, mapview.store,
2271 new_x, new_y, new_x, new_y,
2272 tuw, tuh);
2274 /* Draw */
2275 put_unit(punit, mapview.store, map_zoom, new_x, new_y);
2276 dirty_rect(new_x, new_y, tuw, tuh);
2278 /* Flush. */
2279 flush_dirty();
2280 gui_flush();
2282 /* Restore the backup. It won't take effect until the next flush. */
2283 canvas_copy(mapview.store, mapview.tmp_store,
2284 new_x, new_y, new_x, new_y,
2285 tuw, tuh);
2286 dirty_rect(new_x, new_y, tuw, tuh);
2288 prev_x = new_x;
2289 prev_y = new_y;
2290 } else {
2291 fc_usleep(500);
2293 } while (mytime < timing_sec);
2297 /**************************************************************************
2298 Find the "best" city/settlers to associate with the selected tile.
2299 a. If a visible city is working the tile, return that city.
2300 b. If another player's city is working the tile, return NULL.
2301 c. If any selected cities are within range, return the closest one.
2302 d. If any cities are within range, return the closest one.
2303 e. If any active (with color) settler could work it if they founded a
2304 city, choose the closest one (only if punit != NULL).
2305 f. If any settler could work it if they founded a city, choose the
2306 closest one (only if punit != NULL).
2307 g. If nobody can work it, return NULL.
2308 **************************************************************************/
2309 struct city *find_city_or_settler_near_tile(const struct tile *ptile,
2310 struct unit **punit)
2312 struct city *closest_city;
2313 struct city *pcity;
2314 struct unit *closest_settler = NULL, *best_settler = NULL;
2315 int max_rad = rs_max_city_radius_sq();
2317 if (punit) {
2318 *punit = NULL;
2321 /* Check if there is visible city working that tile */
2322 pcity = tile_worked(ptile);
2323 if (pcity && pcity->tile) {
2324 if (NULL == client.conn.playing
2325 || city_owner(pcity) == client.conn.playing) {
2326 /* rule a */
2327 return pcity;
2328 } else {
2329 /* rule b */
2330 return NULL;
2334 /* rule e */
2335 closest_city = NULL;
2337 /* check within maximum (squared) city radius */
2338 city_tile_iterate(max_rad, ptile, tile1) {
2339 pcity = tile_city(tile1);
2340 if (pcity
2341 && (NULL == client.conn.playing
2342 || city_owner(pcity) == client.conn.playing)
2343 && client_city_can_work_tile(pcity, tile1)) {
2345 * Note, we must explicitly check if the tile is workable (with
2346 * city_can_work_tile() above), since it is possible that another
2347 * city (perhaps an UNSEEN city) may be working it!
2350 if (mapdeco_is_highlight_set(city_tile(pcity))) {
2351 /* rule c */
2352 return pcity;
2354 if (!closest_city) {
2355 closest_city = pcity;
2358 } city_tile_iterate_end;
2360 /* rule d */
2361 if (closest_city || !punit) {
2362 return closest_city;
2365 if (!game.scenario.prevent_new_cities) {
2366 /* check within maximum (squared) city radius */
2367 city_tile_iterate(max_rad, ptile, tile1) {
2368 unit_list_iterate(tile1->units, psettler) {
2369 if ((NULL == client.conn.playing
2370 || unit_owner(psettler) == client.conn.playing)
2371 && unit_has_type_flag(psettler, UTYF_CITIES)
2372 && city_can_be_built_here(unit_tile(psettler), psettler)) {
2373 if (!closest_settler) {
2374 closest_settler = psettler;
2376 if (!best_settler && psettler->client.colored) {
2377 best_settler = psettler;
2380 } unit_list_iterate_end;
2381 } city_tile_iterate_end;
2383 if (best_settler) {
2384 /* Rule e */
2385 *punit = best_settler;
2386 } else if (closest_settler) {
2387 /* Rule f */
2388 *punit = closest_settler;
2392 /* rule g */
2393 return NULL;
2396 /**************************************************************************
2397 Find the nearest/best city that owns the tile.
2398 **************************************************************************/
2399 struct city *find_city_near_tile(const struct tile *ptile)
2401 return find_city_or_settler_near_tile(ptile, NULL);
2404 /**************************************************************************
2405 Append the buy cost of the current production of the given city to the
2406 already NULL-terminated buffer. Does nothing if draw_city_buycost is
2407 set to FALSE, or if it does not make sense to buy the current production
2408 (e.g. coinage).
2409 **************************************************************************/
2410 static void append_city_buycost_string(const struct city *pcity,
2411 char *buffer, int buffer_len)
2413 if (!pcity || !buffer || buffer_len < 1) {
2414 return;
2417 if (!gui_options.draw_city_buycost || !city_can_buy(pcity)) {
2418 return;
2421 cat_snprintf(buffer, buffer_len, "/%d",
2422 city_production_buy_gold_cost(pcity));
2425 /**************************************************************************
2426 Find the mapview city production text for the given city, and place it
2427 into the buffer.
2428 **************************************************************************/
2429 void get_city_mapview_production(struct city *pcity,
2430 char *buffer, size_t buffer_len)
2432 int turns;
2434 universal_name_translation(&pcity->production, buffer, buffer_len);
2436 if (city_production_has_flag(pcity, IF_GOLD)) {
2437 return;
2439 turns = city_production_turns_to_build(pcity, TRUE);
2441 if (999 < turns) {
2442 cat_snprintf(buffer, buffer_len, " -");
2443 } else {
2444 cat_snprintf(buffer, buffer_len, " %d", turns);
2447 append_city_buycost_string(pcity, buffer, buffer_len);
2450 /**************************************************************************
2451 Find the mapview city trade routes text for the given city, and place it
2452 into the buffer. Sets 'pcolor' to the preferred color the text should
2453 be drawn in if it is non-NULL.
2454 **************************************************************************/
2455 void get_city_mapview_trade_routes(struct city *pcity,
2456 char *trade_routes_buffer,
2457 size_t trade_routes_buffer_len,
2458 enum color_std *pcolor)
2460 int num_trade_routes = 0, i;
2461 int max_routes;
2463 if (!trade_routes_buffer || trade_routes_buffer_len <= 0) {
2464 return;
2467 if (!pcity) {
2468 trade_routes_buffer[0] = '\0';
2469 if (pcolor) {
2470 *pcolor = COLOR_MAPVIEW_CITYTEXT;
2472 return;
2475 for (i = 0; i < MAX_TRADE_ROUTES; i++) {
2476 if (pcity->trade[i] <= 0) {
2477 /* NB: pcity->trade_value[i] == 0 is a valid case. */
2478 continue;
2480 num_trade_routes++;
2483 max_routes = max_trade_routes(pcity);
2485 fc_snprintf(trade_routes_buffer, trade_routes_buffer_len,
2486 "%d/%d", num_trade_routes, max_routes);
2488 if (pcolor) {
2489 if (num_trade_routes == max_routes) {
2490 *pcolor = COLOR_MAPVIEW_TRADE_ROUTES_ALL_BUILT;
2491 } else if (num_trade_routes == 0) {
2492 *pcolor = COLOR_MAPVIEW_TRADE_ROUTES_NO_BUILT;
2493 } else {
2494 *pcolor = COLOR_MAPVIEW_TRADE_ROUTES_SOME_BUILT;
2499 /***************************************************************************/
2500 static enum update_type needed_updates = UPDATE_NONE;
2501 static bool callback_queued = FALSE;
2503 /* These values hold the tiles that need city, unit, or tile updates.
2504 * These different types of updates just tell what area need to be updated,
2505 * not necessarily what's sitting on the tile. A city update covers the
2506 * whole citymap area. A unit update covers just the "full" unit tile
2507 * area. A tile update covers the base tile plus half a tile in each
2508 * direction. */
2509 struct tile_list *tile_updates[TILE_UPDATE_COUNT];
2511 /****************************************************************************
2512 This callback is called during an idle moment to unqueue any pending
2513 mapview updates.
2514 ****************************************************************************/
2515 static void queue_callback(void *data)
2517 callback_queued = FALSE;
2518 unqueue_mapview_updates(TRUE);
2521 /****************************************************************************
2522 When a mapview update is queued this function should be called to prepare
2523 an idle-time callback to unqueue the updates.
2524 ****************************************************************************/
2525 static void queue_add_callback(void)
2527 if (!callback_queued) {
2528 callback_queued = TRUE;
2529 add_idle_callback(queue_callback, NULL);
2533 /**************************************************************************
2534 This function, along with unqueue_mapview_update(), helps in updating
2535 the mapview when a packet is received. Previously, we just called
2536 update_map_canvas when (for instance) a city update was received.
2537 Not only would this often end up with a lot of duplicated work, but it
2538 would also draw over the city descriptions, which would then just
2539 "disappear" from the mapview. The hack is to instead call
2540 queue_mapview_update in place of this update, and later (after all
2541 packets have been read) call unqueue_mapview_update. The functions
2542 don't track which areas of the screen need updating, rather when the
2543 unqueue is done we just update the whole visible mapqueue, and redraw
2544 the city descriptions.
2546 Using these functions, updates are done correctly, and are probably
2547 faster too. But it's a bit of a hack to insert this code into the
2548 packet-handling code.
2549 **************************************************************************/
2550 void queue_mapview_update(enum update_type update)
2552 if (can_client_change_view()) {
2553 needed_updates |= update;
2554 queue_add_callback();
2558 /**************************************************************************
2559 Queue this tile to be refreshed. The refresh will be done some time
2560 soon thereafter, and grouped with other needed refreshes.
2562 Note this should only be called for tiles. For cities or units use
2563 queue_mapview_xxx_update instead.
2564 **************************************************************************/
2565 void queue_mapview_tile_update(struct tile *ptile,
2566 enum tile_update_type type)
2568 if (can_client_change_view()) {
2569 if (!tile_updates[type]) {
2570 tile_updates[type] = tile_list_new();
2572 tile_list_append(tile_updates[type], ptile);
2573 queue_add_callback();
2577 /**************************************************************************
2578 See comment for queue_mapview_update().
2579 **************************************************************************/
2580 void unqueue_mapview_updates(bool write_to_screen)
2582 /* Calculate the area covered by each update type. The area array gives
2583 * the offset from the tile origin as well as the width and height of the
2584 * area to be updated. This is initialized each time when entering the
2585 * function from the existing tileset variables.
2587 * A TILE update covers the base tile (W x H) plus a half-tile in each
2588 * direction (for edge/corner graphics), making its area 2W x 2H.
2590 * A UNIT update covers a UW x UH area. This is centered horizontally
2591 * over the tile but extends up above the tile (e.g., units in iso-view).
2593 * A CITYMAP update covers the whole citymap of a tile. This includes
2594 * the citymap area itself plus an extra half-tile in each direction (for
2595 * edge/corner graphics).
2597 const float W = tileset_tile_width(tileset) * map_zoom;
2598 const float H = tileset_tile_height(tileset) * map_zoom;
2599 const float UW = tileset_unit_width(tileset) * map_zoom;
2600 const float UH = tileset_unit_height(tileset) * map_zoom;
2601 const float city_width = get_citydlg_canvas_width() * map_zoom + W;
2602 const float city_height = get_citydlg_canvas_height() * map_zoom + H;
2603 const struct {
2604 float dx, dy, w, h;
2605 } area[TILE_UPDATE_COUNT] = {
2606 {0, 0, W, H},
2607 {-W / 2, -H / 2, 2 * W, 2 * H},
2608 {(W - UW) / 2, H - UH, UW, UH},
2609 {-(max_desc_width - W) / 2, H, max_desc_width, max_desc_height},
2610 {-(city_width - W) / 2, -(city_height - H) / 2, city_width, city_height},
2611 {-(max_label_width - W) / 2, H, max_label_width, max_label_height}
2613 struct tile_list *my_tile_updates[TILE_UPDATE_COUNT];
2615 int i;
2617 if (!can_client_change_view()) {
2618 /* Double sanity check: make sure we don't unqueue an invalid update
2619 * after we've already detached. */
2620 return;
2623 log_debug("unqueue_mapview_update: needed_updates=%d",
2624 needed_updates);
2626 /* This code "pops" the lists of tile updates off of the static array and
2627 * stores them locally. This allows further updates to be queued within
2628 * the function itself (namely, within update_map_canvas). */
2629 for (i = 0; i < TILE_UPDATE_COUNT; i++) {
2630 my_tile_updates[i] = tile_updates[i];
2631 tile_updates[i] = NULL;
2634 if (map_exists()) {
2635 if ((needed_updates & UPDATE_MAP_CANVAS_VISIBLE)
2636 || (needed_updates & UPDATE_CITY_DESCRIPTIONS)
2637 || (needed_updates & UPDATE_TILE_LABELS)) {
2638 dirty_all();
2639 update_map_canvas(0, 0, mapview.store_width,
2640 mapview.store_height);
2641 /* Have to update the overview too, since some tiles may have changed. */
2642 refresh_overview_canvas();
2643 } else {
2644 int min_x = mapview.width, min_y = mapview.height;
2645 int max_x = 0, max_y = 0;
2647 for (i = 0; i < TILE_UPDATE_COUNT; i++) {
2648 if (my_tile_updates[i]) {
2649 tile_list_iterate(my_tile_updates[i], ptile) {
2650 float xl, yt;
2651 int xr, yb;
2653 (void) tile_to_canvas_pos(&xl, &yt, ptile);
2655 xl += area[i].dx;
2656 yt += area[i].dy;
2657 xr = xl + area[i].w;
2658 yb = yt + area[i].h;
2660 if (xr > 0 && xl < mapview.width
2661 && yb > 0 && yt < mapview.height) {
2662 min_x = MIN(min_x, xl);
2663 min_y = MIN(min_y, yt);
2664 max_x = MAX(max_x, xr);
2665 max_y = MAX(max_y, yb);
2668 /* FIXME: These overview updates should be batched as well.
2669 * Right now they account for as much as 90% of the runtime of
2670 * the unqueue. */
2671 overview_update_tile(ptile);
2672 } tile_list_iterate_end;
2676 if (min_x < max_x && min_y < max_y) {
2677 update_map_canvas(min_x, min_y, max_x - min_x, max_y - min_y);
2682 for (i = 0; i < TILE_UPDATE_COUNT; i++) {
2683 if (my_tile_updates[i]) {
2684 tile_list_destroy(my_tile_updates[i]);
2687 needed_updates = UPDATE_NONE;
2689 if (write_to_screen) {
2690 flush_dirty();
2691 flush_dirty_overview();
2695 /**************************************************************************
2696 Fill the two buffers which information about the city which is shown
2697 below it. It does not take draw_city_names/draw_city_growth into account.
2698 **************************************************************************/
2699 void get_city_mapview_name_and_growth(struct city *pcity,
2700 char *name_buffer,
2701 size_t name_buffer_len,
2702 char *growth_buffer,
2703 size_t growth_buffer_len,
2704 enum color_std *growth_color)
2706 fc_strlcpy(name_buffer, city_name_get(pcity), name_buffer_len);
2708 if (NULL == client.conn.playing
2709 || city_owner(pcity) == client.conn.playing) {
2710 int turns = city_turns_to_grow(pcity);
2712 if (turns == 0) {
2713 fc_snprintf(growth_buffer, growth_buffer_len, "X");
2714 } else if (turns == FC_INFINITY) {
2715 fc_snprintf(growth_buffer, growth_buffer_len, "-");
2716 } else {
2717 /* Negative turns means we're shrinking, but that's handled
2718 down below. */
2719 fc_snprintf(growth_buffer, growth_buffer_len, "%d", abs(turns));
2722 if (turns <= 0) {
2723 /* A blocked or shrinking city has its growth status shown in red. */
2724 *growth_color = COLOR_MAPVIEW_CITYGROWTH_BLOCKED;
2725 } else {
2726 *growth_color = COLOR_MAPVIEW_CITYTEXT;
2728 } else {
2729 growth_buffer[0] = '\0';
2730 *growth_color = COLOR_MAPVIEW_CITYTEXT;
2734 /**************************************************************************
2735 Returns TRUE if cached drawing is possible. If the mapview is too large
2736 we have to turn it off.
2737 **************************************************************************/
2738 static bool can_do_cached_drawing(void)
2740 const int W = tileset_tile_width(tileset) * map_zoom;
2741 const int H = tileset_tile_height(tileset) * map_zoom;
2742 int w = mapview.store_width, h = mapview.store_height;
2744 /* If the mapview window is too large, cached drawing is not possible.
2746 * BACKGROUND: cached drawing occurrs when the mapview is scrolled just
2747 * a short distance. The majority of the mapview window can simply be
2748 * copied while the newly visible areas must be drawn from scratch. This
2749 * speeds up drawing significantly, especially when using the scrollbars
2750 * or mapview sliding.
2752 * When the mapview is larger than the map, however, some tiles may become
2753 * visible twice. In this case one instance of the tile will be drawn
2754 * while all others are drawn black. When this happens the cached drawing
2755 * system breaks since it assumes the mapview canvas is an "ideal" window
2756 * over the map. So black tiles may be scrolled from the edges of the
2757 * mapview into the center, while drawn tiles may be scrolled from the
2758 * center of the mapview out to the edges. The result is very bad.
2760 * There are a few different ways this could be solved. One way is simply
2761 * to turn off cached drawing, which is what we do now. If the mapview
2762 * window gets to be too large, the caching is disabled. Another would
2763 * be to prevent the window from getting too large in the first place -
2764 * but because the window boundaries aren't at an even tile this would
2765 * mean the entire map could never be shown. Yet another way would be
2766 * to draw tiles more than once if they are visible in multiple locations
2767 * on the mapview.
2769 * The logic below is complicated and determined in part by
2770 * trial-and-error. */
2771 if (!current_topo_has_flag(TF_WRAPX) && !current_topo_has_flag(TF_WRAPY)) {
2772 /* An unwrapping map: no limitation. On an unwrapping map no tile can
2773 * be visible twice so there's no problem. */
2774 return TRUE;
2776 if (XOR(current_topo_has_flag(TF_ISO) || current_topo_has_flag(TF_HEX),
2777 tileset_is_isometric(tileset))) {
2778 /* Non-matching. In this case the mapview does not line up with the
2779 * map's axis of wrapping. This will give very bad results for the
2780 * player!
2781 * We can never show more than half of the map.
2783 * We divide by 4 below because we have to divide by 2 twice. The
2784 * first division by 2 is because the square must be half the size
2785 * of the (width+height). The second division by two is because for
2786 * an iso-map, NATURAL_XXX has a scale of 2, whereas for iso-view
2787 * NORMAL_TILE_XXX has a scale of 2. */
2788 return (w <= (NATURAL_WIDTH + NATURAL_HEIGHT) * W / 4
2789 && h <= (NATURAL_WIDTH + NATURAL_HEIGHT) * H / 4);
2790 } else {
2791 /* Matching. */
2792 const int isofactor = (tileset_is_isometric(tileset) ? 2 : 1);
2793 const int isodiff = (tileset_is_isometric(tileset) ? 6 : 2);
2795 /* Now we can use the full width and height, with the exception of a small
2796 * area on each side. */
2797 if (current_topo_has_flag(TF_WRAPX)
2798 && w > (NATURAL_WIDTH - isodiff) * W / isofactor) {
2799 return FALSE;
2801 if (current_topo_has_flag(TF_WRAPY)
2802 && h > (NATURAL_HEIGHT - isodiff) * H / isofactor) {
2803 return FALSE;
2805 return TRUE;
2809 /**************************************************************************
2810 Called when we receive map dimensions. It initialized the mapview
2811 decorations.
2812 **************************************************************************/
2813 void mapdeco_init(void)
2815 /* HACK: this must be called on a map_info packet. */
2816 mapview.can_do_cached_drawing = can_do_cached_drawing();
2818 mapdeco_free();
2819 mapdeco_highlight_table = tile_hash_new();
2820 mapdeco_crosshair_table = tile_hash_new();
2821 mapdeco_gotoline_table = gotoline_hash_new();
2824 /**************************************************************************
2825 Free all memory used for map decorations.
2826 **************************************************************************/
2827 void mapdeco_free(void)
2829 if (mapdeco_highlight_table) {
2830 tile_hash_destroy(mapdeco_highlight_table);
2831 mapdeco_highlight_table = NULL;
2833 if (mapdeco_crosshair_table) {
2834 tile_hash_destroy(mapdeco_crosshair_table);
2835 mapdeco_crosshair_table = NULL;
2837 if (mapdeco_gotoline_table) {
2838 gotoline_hash_destroy(mapdeco_gotoline_table);
2839 mapdeco_gotoline_table = NULL;
2843 /**************************************************************************
2844 Set the given tile's map decoration as either highlighted or not,
2845 depending on the value of 'highlight'.
2846 **************************************************************************/
2847 void mapdeco_set_highlight(const struct tile *ptile, bool highlight)
2849 bool changed = FALSE;
2850 if (!ptile || !mapdeco_highlight_table) {
2851 return;
2854 if (highlight) {
2855 changed = tile_hash_insert(mapdeco_highlight_table, ptile, NULL);
2856 } else {
2857 changed = tile_hash_remove(mapdeco_highlight_table, ptile);
2860 if (changed) {
2861 /* FIXME: Remove the cast. */
2862 refresh_tile_mapcanvas((struct tile *) ptile, TRUE, FALSE);
2866 /**************************************************************************
2867 Return TRUE if the given tile is highlighted.
2868 **************************************************************************/
2869 bool mapdeco_is_highlight_set(const struct tile *ptile)
2871 if (!ptile || !mapdeco_highlight_table) {
2872 return FALSE;
2874 return tile_hash_lookup(mapdeco_highlight_table, ptile, NULL);
2877 /**************************************************************************
2878 Clears all highlighting. Marks the previously highlighted tiles as
2879 needing a mapview update.
2880 **************************************************************************/
2881 void mapdeco_clear_highlights(void)
2883 if (!mapdeco_highlight_table) {
2884 return;
2887 tile_hash_iterate(mapdeco_highlight_table, ptile) {
2888 refresh_tile_mapcanvas(ptile, TRUE, FALSE);
2889 } tile_hash_iterate_end;
2891 tile_hash_clear(mapdeco_highlight_table);
2894 /**************************************************************************
2895 Marks the given tile as having a "crosshair" map decoration.
2896 **************************************************************************/
2897 void mapdeco_set_crosshair(const struct tile *ptile, bool crosshair)
2899 bool changed;
2901 if (!mapdeco_crosshair_table || !ptile) {
2902 return;
2905 if (crosshair) {
2906 changed = tile_hash_insert(mapdeco_crosshair_table, ptile, NULL);
2907 } else {
2908 changed = tile_hash_remove(mapdeco_crosshair_table, ptile);
2911 if (changed) {
2912 /* FIXME: Remove the cast. */
2913 refresh_tile_mapcanvas((struct tile *) ptile, FALSE, FALSE);
2917 /**************************************************************************
2918 Returns TRUE if there is a "crosshair" decoration set at the given tile.
2919 **************************************************************************/
2920 bool mapdeco_is_crosshair_set(const struct tile *ptile)
2922 if (!mapdeco_crosshair_table || !ptile) {
2923 return FALSE;
2925 return tile_hash_lookup(mapdeco_crosshair_table, ptile, NULL);
2928 /**************************************************************************
2929 Clears all previous set tile crosshair decorations. Marks the affected
2930 tiles as needing a mapview update.
2931 **************************************************************************/
2932 void mapdeco_clear_crosshairs(void)
2934 if (!mapdeco_crosshair_table) {
2935 return;
2938 tile_hash_iterate(mapdeco_crosshair_table, ptile) {
2939 refresh_tile_mapcanvas(ptile, FALSE, FALSE);
2940 } tile_hash_iterate_end;
2942 tile_hash_clear(mapdeco_crosshair_table);
2945 /**************************************************************************
2946 Add a goto line from the given tile 'ptile' in the direction 'dir'. If
2947 there was no previously drawn line there, a mapview update is queued
2948 for the source and destination tiles.
2949 **************************************************************************/
2950 void mapdeco_add_gotoline(const struct tile *ptile, enum direction8 dir)
2952 struct gotoline_counter *pglc;
2953 const struct tile *ptile_dest;
2954 bool changed;
2956 if (!mapdeco_gotoline_table || !ptile
2957 || !(0 <= dir && dir <= direction8_max())) {
2958 return;
2960 ptile_dest = mapstep(ptile, dir);
2961 if (!ptile_dest) {
2962 return;
2965 if (!gotoline_hash_lookup(mapdeco_gotoline_table, ptile, &pglc)) {
2966 pglc = gotoline_counter_new();
2967 gotoline_hash_insert(mapdeco_gotoline_table, ptile, pglc);
2969 changed = (pglc->line_count[dir] < 1);
2970 pglc->line_count[dir]++;
2972 if (changed) {
2973 /* FIXME: Remove cast. */
2974 refresh_tile_mapcanvas((struct tile *) ptile, FALSE, FALSE);
2975 refresh_tile_mapcanvas((struct tile *) ptile_dest, FALSE, FALSE);
2979 /**************************************************************************
2980 Removes a goto line from the given tile 'ptile' going in the direction
2981 'dir'. If this was the last line there, a mapview update is queued to
2982 erase the drawn line.
2983 **************************************************************************/
2984 void mapdeco_remove_gotoline(const struct tile *ptile,
2985 enum direction8 dir)
2987 struct gotoline_counter *pglc;
2988 bool changed = FALSE;
2990 if (!mapdeco_gotoline_table || !ptile
2991 || !(0 <= dir && dir <= direction8_max())) {
2992 return;
2995 if (!gotoline_hash_lookup(mapdeco_gotoline_table, ptile, &pglc)) {
2996 return;
2999 pglc->line_count[dir]--;
3000 if (pglc->line_count[dir] <= 0) {
3001 pglc->line_count[dir] = 0;
3002 changed = TRUE;
3005 if (changed) {
3006 /* FIXME: Remove the casts. */
3007 refresh_tile_mapcanvas((struct tile *) ptile, FALSE, FALSE);
3008 ptile = mapstep(ptile, dir);
3009 if (ptile != NULL) {
3010 refresh_tile_mapcanvas((struct tile *) ptile, FALSE, FALSE);
3015 /**************************************************************************
3016 Set the map decorations for the given unit's goto route. A goto route
3017 consists of one or more goto lines, with each line being from the center
3018 of one tile to the center of another tile.
3019 **************************************************************************/
3020 void mapdeco_set_gotoroute(const struct unit *punit)
3022 const struct unit_order *porder;
3023 const struct tile *ptile;
3024 int i, ind;
3026 if (!punit || !unit_tile(punit) || !unit_has_orders(punit)
3027 || punit->orders.length < 1) {
3028 return;
3031 ptile = unit_tile(punit);
3033 for (i = 0; ptile != NULL && i < punit->orders.length; i++) {
3034 if (punit->orders.index + i >= punit->orders.length
3035 && !punit->orders.repeat) {
3036 break;
3039 ind = (punit->orders.index + i) % punit->orders.length;
3040 porder = &punit->orders.list[ind];
3041 if (porder->order != ORDER_MOVE) {
3042 /* FIXME: should display some indication of non-move orders here. */
3043 continue;
3046 mapdeco_add_gotoline(ptile, porder->dir);
3047 ptile = mapstep(ptile, porder->dir);
3051 /**************************************************************************
3052 Returns TRUE if a goto line should be drawn from the given tile in the
3053 given direction.
3054 **************************************************************************/
3055 bool mapdeco_is_gotoline_set(const struct tile *ptile,
3056 enum direction8 dir)
3058 struct gotoline_counter *pglc;
3060 if (!ptile || !(0 <= dir && dir <= direction8_max())
3061 || !mapdeco_gotoline_table) {
3062 return FALSE;
3065 if (!gotoline_hash_lookup(mapdeco_gotoline_table, ptile, &pglc)) {
3066 return FALSE;
3069 return pglc->line_count[dir] > 0;
3072 /**************************************************************************
3073 Clear all goto line map decorations and queues mapview updates for the
3074 affected tiles.
3075 **************************************************************************/
3076 void mapdeco_clear_gotoroutes(void)
3078 if (!mapdeco_gotoline_table) {
3079 return;
3082 gotoline_hash_iterate(mapdeco_gotoline_table, ptile, pglc) {
3083 refresh_tile_mapcanvas(ptile, FALSE, FALSE);
3084 adjc_dir_iterate(ptile, ptile_dest, dir) {
3085 if (pglc->line_count[dir] > 0) {
3086 refresh_tile_mapcanvas(ptile_dest, FALSE, FALSE);
3088 } adjc_dir_iterate_end;
3089 } gotoline_hash_iterate_end;
3090 gotoline_hash_clear(mapdeco_gotoline_table);
3093 /**************************************************************************
3094 Called if the map in the GUI is resized.
3096 Returns TRUE iff the canvas was redrawn.
3097 **************************************************************************/
3098 bool map_canvas_resized(int width, int height)
3100 int old_tile_width = mapview.tile_width;
3101 int old_tile_height = mapview.tile_height;
3102 int old_width = mapview.width, old_height = mapview.height;
3103 int tile_width = (width + tileset_tile_width(tileset) * map_zoom - 1) /
3104 (tileset_tile_width(tileset) * map_zoom);
3105 int tile_height = (height + tileset_tile_height(tileset) * map_zoom - 1) /
3106 (tileset_tile_height(tileset) * map_zoom);
3107 int full_width = tile_width * tileset_tile_width(tileset) * map_zoom;
3108 int full_height = tile_height * tileset_tile_height(tileset) * map_zoom;
3109 bool tile_size_changed, size_changed, redrawn = FALSE;
3111 /* Resized */
3113 /* Since a resize is only triggered when the tile_*** changes, the canvas
3114 * width and height must include the entire backing store - otherwise
3115 * small resizings may lead to undrawn tiles. */
3116 mapview.tile_width = tile_width;
3117 mapview.tile_height = tile_height;
3118 mapview.width = width;
3119 mapview.height = height;
3120 mapview.store_width = full_width;
3121 mapview.store_height = full_height;
3123 /* Check for what's changed. */
3124 tile_size_changed = (tile_width != old_tile_width
3125 || tile_height != old_tile_height);
3126 size_changed = (width != old_width || height != old_height);
3128 /* If the tile size has changed, resize the canvas. */
3129 if (tile_size_changed) {
3130 if (mapview.store) {
3131 canvas_free(mapview.store);
3132 canvas_free(mapview.tmp_store);
3134 mapview.store = canvas_create(full_width, full_height);
3135 canvas_set_zoom(mapview.store, map_zoom);
3136 canvas_put_rectangle(mapview.store,
3137 get_color(tileset, COLOR_MAPVIEW_UNKNOWN),
3138 0, 0, full_width, full_height);
3140 mapview.tmp_store = canvas_create(full_width, full_height);
3141 canvas_set_zoom(mapview.tmp_store, map_zoom);
3144 if (map_exists() && can_client_change_view()) {
3145 if (tile_size_changed) {
3146 if (center_tile != NULL) {
3147 int x_left, y_top;
3148 float gui_x, gui_y;
3150 index_to_map_pos(&x_left, &y_top, tile_index(center_tile));
3151 map_to_gui_pos(tileset, &gui_x, &gui_y, x_left, y_top);
3153 /* Put the center pixel of the tile at the exact center of the mapview. */
3154 gui_x -= (mapview.width - tileset_tile_width(tileset) * map_zoom) / 2;
3155 gui_y -= (mapview.height - tileset_tile_height(tileset) * map_zoom) / 2;
3157 calc_mapview_origin(&gui_x, &gui_y);
3158 mapview.gui_x0 = gui_x;
3159 mapview.gui_y0 = gui_y;
3161 update_map_canvas_visible();
3162 center_tile_overviewcanvas();
3163 unqueue_mapview_updates(TRUE);
3164 redrawn = TRUE;
3167 /* If the width/height has changed, update the scrollbars even if
3168 * the backing store is not resized. */
3169 if (size_changed) {
3170 update_map_canvas_scrollbars_size();
3171 update_map_canvas_scrollbars();
3175 mapview.can_do_cached_drawing = can_do_cached_drawing();
3177 return redrawn;
3180 /**************************************************************************
3181 Sets up data for the mapview and overview.
3182 **************************************************************************/
3183 void init_mapcanvas_and_overview(void)
3185 /* Create a dummy map to make sure mapview.store is never NULL. */
3186 map_canvas_resized(1, 1);
3189 /**************************************************************************
3190 Frees resources allocated for mapview and overview
3191 **************************************************************************/
3192 void free_mapcanvas_and_overview(void)
3194 canvas_free(mapview.store);
3195 canvas_free(mapview.tmp_store);
3198 /****************************************************************************
3199 Return the desired width of the spaceship canvas.
3200 ****************************************************************************/
3201 void get_spaceship_dimensions(int *width, int *height)
3203 struct sprite *sprite
3204 = get_spaceship_sprite(tileset, SPACESHIP_HABITATION);
3206 get_sprite_dimensions(sprite, width, height);
3207 *width *= 7;
3208 *height *= 7;
3211 /****************************************************************************
3212 Draw the spaceship onto the canvas.
3213 ****************************************************************************/
3214 void put_spaceship(struct canvas *pcanvas, int canvas_x, int canvas_y,
3215 const struct player *pplayer)
3217 int i, x, y;
3218 const struct player_spaceship *ship = &pplayer->spaceship;
3219 int w, h;
3220 struct sprite *spr;
3221 struct tileset *t = tileset;
3223 spr = get_spaceship_sprite(t, SPACESHIP_HABITATION);
3224 get_sprite_dimensions(spr, &w, &h);
3226 canvas_put_rectangle(pcanvas,
3227 get_color(tileset, COLOR_SPACESHIP_BACKGROUND),
3228 0, 0, w * 7, h * 7);
3230 for (i = 0; i < NUM_SS_MODULES; i++) {
3231 const int j = i / 3;
3232 const int k = i % 3;
3234 if ((k == 0 && j >= ship->habitation)
3235 || (k == 1 && j >= ship->life_support)
3236 || (k == 2 && j >= ship->solar_panels)) {
3237 continue;
3239 x = modules_info[i].x * w / 4 - w / 2;
3240 y = modules_info[i].y * h / 4 - h / 2;
3242 spr = (k == 0 ? get_spaceship_sprite(t, SPACESHIP_HABITATION)
3243 : k == 1 ? get_spaceship_sprite(t, SPACESHIP_LIFE_SUPPORT)
3244 : get_spaceship_sprite(t, SPACESHIP_SOLAR_PANEL));
3245 canvas_put_sprite_full(pcanvas, x, y, spr);
3248 for (i = 0; i < NUM_SS_COMPONENTS; i++) {
3249 const int j = i / 2;
3250 const int k = i % 2;
3252 if ((k == 0 && j >= ship->fuel)
3253 || (k == 1 && j >= ship->propulsion)) {
3254 continue;
3256 x = components_info[i].x * w / 4 - w / 2;
3257 y = components_info[i].y * h / 4 - h / 2;
3259 spr = ((k == 0) ? get_spaceship_sprite(t, SPACESHIP_FUEL)
3260 : get_spaceship_sprite(t, SPACESHIP_PROPULSION));
3262 canvas_put_sprite_full(pcanvas, x, y, spr);
3264 if (k && ship->state == SSHIP_LAUNCHED) {
3265 spr = get_spaceship_sprite(t, SPACESHIP_EXHAUST);
3266 canvas_put_sprite_full(pcanvas, x + w, y, spr);
3270 for (i = 0; i < NUM_SS_STRUCTURALS; i++) {
3271 if (!BV_ISSET(ship->structure, i)) {
3272 continue;
3274 x = structurals_info[i].x * w / 4 - w / 2;
3275 y = structurals_info[i].y * h / 4 - h / 2;
3277 spr = get_spaceship_sprite(t, SPACESHIP_STRUCTURAL);
3278 canvas_put_sprite_full(pcanvas, x, y, spr);
3282 /****************************************************************************
3283 Map link mark module: it makes link marks when a link is sent by chating,
3284 or restore a mark with clicking a link on the chatline.
3285 ****************************************************************************/
3286 struct link_mark {
3287 enum text_link_type type; /* The target type. */
3288 int id; /* The city or unit id, or tile index. */
3289 int turn_counter; /* The turn counter before it disappears. */
3292 #define SPECLIST_TAG link_mark
3293 #define SPECLIST_TYPE struct link_mark
3294 #include "speclist.h"
3295 #define link_marks_iterate(pmark) \
3296 TYPED_LIST_ITERATE(struct link_mark, link_marks, pmark)
3297 #define link_marks_iterate_end LIST_ITERATE_END
3299 static struct link_mark_list *link_marks = NULL;
3301 /**********************************************************************
3302 Find a link mark in the list.
3303 ***********************************************************************/
3304 static struct link_mark *link_mark_find(enum text_link_type type, int id)
3306 link_marks_iterate(pmark) {
3307 if (pmark->type == type && pmark->id == id) {
3308 return pmark;
3310 } link_marks_iterate_end;
3312 return NULL;
3315 /**********************************************************************
3316 Create a new link mark.
3317 ***********************************************************************/
3318 static struct link_mark *link_mark_new(enum text_link_type type,
3319 int id, int turns)
3321 struct link_mark *pmark = fc_malloc(sizeof(struct link_mark));
3323 pmark->type = type;
3324 pmark->id = id;
3325 pmark->turn_counter = turns;
3327 return pmark;
3330 /**********************************************************************
3331 Remove a link mark.
3332 ***********************************************************************/
3333 static void link_mark_destroy(struct link_mark *pmark)
3335 free(pmark);
3338 /**********************************************************************
3339 Returns the location of the pointed mark.
3340 ***********************************************************************/
3341 static struct tile *link_mark_tile(const struct link_mark *pmark)
3343 switch (pmark->type) {
3344 case TLT_CITY:
3346 struct city *pcity = game_city_by_number(pmark->id);
3347 return pcity ? pcity->tile : NULL;
3349 case TLT_TILE:
3350 return index_to_tile(pmark->id);
3351 case TLT_UNIT:
3353 struct unit *punit = game_unit_by_number(pmark->id);
3354 return punit ? unit_tile(punit) : NULL;
3357 return NULL;
3360 /**********************************************************************
3361 Returns the color of the pointed mark.
3362 ***********************************************************************/
3363 static struct color *link_mark_color(const struct link_mark *pmark)
3365 switch (pmark->type) {
3366 case TLT_CITY:
3367 return get_color(tileset, COLOR_MAPVIEW_CITY_LINK);
3368 case TLT_TILE:
3369 return get_color(tileset, COLOR_MAPVIEW_TILE_LINK);
3370 case TLT_UNIT:
3371 return get_color(tileset, COLOR_MAPVIEW_UNIT_LINK);
3373 return NULL;
3376 /**********************************************************************
3377 Print a link mark.
3378 ***********************************************************************/
3379 static void link_mark_draw(const struct link_mark *pmark)
3381 int width = tileset_tile_width(tileset) * map_zoom;
3382 int height = tileset_tile_height(tileset) * map_zoom;
3383 int xd = width / 20, yd = height / 20;
3384 int xlen = width / 3, ylen = height / 3;
3385 float canvas_x, canvas_y;
3386 int x_left, x_right, y_top, y_bottom;
3387 struct tile *ptile = link_mark_tile(pmark);
3388 struct color *pcolor = link_mark_color(pmark);
3390 if (!ptile || !tile_to_canvas_pos(&canvas_x, &canvas_y, ptile)) {
3391 return;
3394 x_left = canvas_x + xd;
3395 x_right = canvas_x + width - xd;
3396 y_top = canvas_y + yd;
3397 y_bottom = canvas_y + height - yd;
3399 canvas_put_line(mapview.store, pcolor, LINE_TILE_FRAME, x_left, y_top, xlen, 0);
3400 canvas_put_line(mapview.store, pcolor, LINE_TILE_FRAME, x_left, y_top, 0, ylen);
3402 canvas_put_line(mapview.store, pcolor, LINE_TILE_FRAME, x_right, y_top, -xlen, 0);
3403 canvas_put_line(mapview.store, pcolor, LINE_TILE_FRAME, x_right, y_top, 0, ylen);
3405 canvas_put_line(mapview.store, pcolor, LINE_TILE_FRAME, x_left, y_bottom, xlen, 0);
3406 canvas_put_line(mapview.store, pcolor, LINE_TILE_FRAME, x_left, y_bottom, 0, -ylen);
3408 canvas_put_line(mapview.store, pcolor, LINE_TILE_FRAME, x_right, y_bottom, -xlen, 0);
3409 canvas_put_line(mapview.store, pcolor, LINE_TILE_FRAME, x_right, y_bottom, 0, -ylen);
3412 /**********************************************************************
3413 Initialize the link marks.
3414 ***********************************************************************/
3415 void link_marks_init(void)
3417 if (link_marks) {
3418 link_marks_free();
3421 link_marks = link_mark_list_new_full(link_mark_destroy);
3424 /**********************************************************************
3425 Free the link marks.
3426 ***********************************************************************/
3427 void link_marks_free(void)
3429 if (!link_marks) {
3430 return;
3433 link_mark_list_destroy(link_marks);
3434 link_marks = NULL;
3437 /**********************************************************************
3438 Draw all link marks.
3439 ***********************************************************************/
3440 void link_marks_draw_all(void)
3442 link_marks_iterate(pmark) {
3443 link_mark_draw(pmark);
3444 } link_marks_iterate_end;
3447 /**********************************************************************
3448 Clear all visible links.
3449 ***********************************************************************/
3450 void link_marks_clear_all(void)
3452 link_mark_list_clear(link_marks);
3453 update_map_canvas_visible();
3456 /**********************************************************************
3457 Clear all visible links.
3458 ***********************************************************************/
3459 void link_marks_decrease_turn_counters(void)
3461 link_marks_iterate(pmark) {
3462 if (--pmark->turn_counter <= 0) {
3463 link_mark_list_remove(link_marks, pmark);
3465 } link_marks_iterate_end;
3467 /* update_map_canvas_visible(); not needed here. */
3470 /**********************************************************************
3471 Add a visible link for 2 turns.
3472 ***********************************************************************/
3473 void link_mark_add_new(enum text_link_type type, int id)
3475 struct link_mark *pmark = link_mark_find(type, id);
3476 struct tile *ptile;
3478 if (pmark) {
3479 /* Already displayed, but maybe increase the turn counter. */
3480 pmark->turn_counter = MAX(pmark->turn_counter, 2);
3481 return;
3484 pmark = link_mark_new(type, id, 2);
3485 link_mark_list_append(link_marks, pmark);
3486 ptile = link_mark_tile(pmark);
3487 if (ptile && tile_visible_mapcanvas(ptile)) {
3488 refresh_tile_mapcanvas(ptile, FALSE, FALSE);
3492 /**********************************************************************
3493 Add a visible link for 1 turn.
3494 ***********************************************************************/
3495 void link_mark_restore(enum text_link_type type, int id)
3497 struct link_mark *pmark;
3498 struct tile *ptile;
3500 if (link_mark_find(type, id)) {
3501 return;
3504 pmark = link_mark_new(type, id, 1);
3505 link_mark_list_append(link_marks, pmark);
3506 ptile = link_mark_tile(pmark);
3507 if (ptile && tile_visible_mapcanvas(ptile)) {
3508 refresh_tile_mapcanvas(ptile, FALSE, FALSE);
3512 /**********************************************************************
3513 Are the topology and tileset compatible?
3514 ***********************************************************************/
3515 bool tileset_map_topo_compatible(int topology_id, struct tileset *tset)
3517 int tileset_topology;
3519 if (tileset_hex_width(tset) > 0) {
3520 fc_assert(tileset_is_isometric(tset));
3521 tileset_topology = TF_HEX | TF_ISO;
3522 } else if (tileset_hex_height(tset) > 0) {
3523 fc_assert(tileset_is_isometric(tset));
3524 tileset_topology = TF_HEX;
3525 } else if (tileset_is_isometric(tset)) {
3526 tileset_topology = TF_ISO;
3527 } else {
3528 tileset_topology = 0;
3531 return ((topology_id & (TF_HEX | TF_ISO)) == tileset_topology);