Set production text color correctly when city production display is
[freeciv.git] / client / mapview_common.c
blob5b6b68596f9f1019356baac85eb65fbfae9d79ab
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 enum color_std production_color;
1476 /* trade_routes_color initialized just to get rid off gcc warning
1477 * on optimization level 3 when it misdiagnoses that it would be used
1478 * uninitialized otherwise. Funny thing here is that warning would
1479 * go away also by *not* setting it to values other than
1480 * COLOR_MAPVIEW_CITYTEXT in get_city_mapview_trade_routes() */
1481 enum color_std trade_routes_color = COLOR_MAPVIEW_CITYTEXT;
1482 struct color *owner_color;
1483 struct {
1484 int x, y, w, h;
1485 } name_rect = {0, 0, 0, 0}, growth_rect = {0, 0, 0, 0},
1486 prod_rect = {0, 0, 0, 0}, size_rect = {0, 0, 0, 0},
1487 flag_rect = {0, 0, 0, 0}, occupy_rect = {0, 0, 0, 0},
1488 food_rect = {0, 0, 0, 0}, shield_rect = {0, 0, 0, 0},
1489 trade_routes_rect = {0,}, trade_rect = {0,};
1490 int width1 = 0, width2 = 0, height1 = 0, height2 = 0;
1491 struct sprite *bg = citybar->background;
1492 struct sprite *flag = get_city_flag_sprite(tileset, pcity);
1493 struct sprite *occupy = NULL;
1494 int bg_w, bg_h, x, y;
1495 const int canvas_x = canvas_x0 + tileset_tile_width(tileset) / 2 * map_zoom;
1496 const int canvas_y = canvas_y0 + tileset_citybar_offset_y(tileset) * map_zoom;
1497 const int border = 6;
1498 const enum client_font FONT_CITY_SIZE = FONT_CITY_NAME; /* TODO: new font */
1500 /* We can see the city's production or growth values if
1501 * we are observing or playing as the owner of the city. */
1502 const bool can_see_inside
1503 = (client_is_global_observer() || city_owner(pcity) == client_player());
1504 const bool should_draw_productions
1505 = can_see_inside && gui_options.draw_city_productions;
1506 const bool should_draw_growth = can_see_inside && gui_options.draw_city_growth;
1507 const bool should_draw_trade_routes = can_see_inside
1508 && gui_options.draw_city_trade_routes;
1509 const bool should_draw_lower_bar
1510 = should_draw_productions || should_draw_growth
1511 || should_draw_trade_routes;
1514 if (width != NULL) {
1515 *width = 0;
1517 if (height != NULL) {
1518 *height = 0;
1521 if (!gui_options.draw_city_names && !should_draw_lower_bar) {
1522 return;
1526 /* First: calculate rect dimensions (but not positioning). */
1528 get_sprite_dimensions(bg, &bg_w, &bg_h);
1529 get_city_mapview_name_and_growth(pcity, name, sizeof(name),
1530 growth, sizeof(growth), &growth_color, &production_color);
1532 if (gui_options.draw_city_names) {
1533 fc_snprintf(size, sizeof(size), "%d", city_size_get(pcity));
1535 get_text_size(&size_rect.w, &size_rect.h, FONT_CITY_SIZE, size);
1536 get_text_size(&name_rect.w, &name_rect.h, FONT_CITY_NAME, name);
1538 if (can_player_see_units_in_city(client.conn.playing, pcity)) {
1539 int count = unit_list_size(pcity->tile->units);
1541 count = CLIP(0, count, citybar->occupancy.size - 1);
1542 occupy = citybar->occupancy.p[count];
1543 } else {
1544 if (pcity->client.occupied) {
1545 occupy = citybar->occupied;
1546 } else {
1547 occupy = citybar->occupancy.p[0];
1551 get_sprite_dimensions(flag, &flag_rect.w, &flag_rect.h);
1552 get_sprite_dimensions(occupy, &occupy_rect.w, &occupy_rect.h);
1554 width1 = (flag_rect.w + occupy_rect.w + name_rect.w
1555 + 2 * border + size_rect.w);
1556 height1 = MAX(flag_rect.h,
1557 MAX(occupy_rect.h,
1558 MAX(name_rect.h + border,
1559 size_rect.h + border)));
1562 if (should_draw_lower_bar) {
1563 width2 = 0;
1564 height2 = 0;
1566 if (should_draw_productions) {
1567 get_city_mapview_production(pcity, prod, sizeof(prod));
1568 get_text_size(&prod_rect.w, &prod_rect.h, FONT_CITY_PROD, prod);
1570 get_sprite_dimensions(citybar->shields, &shield_rect.w, &shield_rect.h);
1571 width2 += shield_rect.w + prod_rect.w + border;
1572 height2 = MAX(height2, shield_rect.h);
1573 height2 = MAX(height2, prod_rect.h + border);
1576 if (should_draw_growth) {
1577 get_text_size(&growth_rect.w, &growth_rect.h, FONT_CITY_PROD, growth);
1578 get_sprite_dimensions(citybar->food, &food_rect.w, &food_rect.h);
1579 width2 += food_rect.w + growth_rect.w + border;
1580 height2 = MAX(height2, food_rect.h);
1581 height2 = MAX(height2, growth_rect.h + border);
1584 if (should_draw_trade_routes) {
1585 get_city_mapview_trade_routes(pcity, trade_routes,
1586 sizeof(trade_routes),
1587 &trade_routes_color);
1588 get_text_size(&trade_routes_rect.w, &trade_routes_rect.h,
1589 FONT_CITY_PROD, trade_routes);
1590 get_sprite_dimensions(citybar->trade, &trade_rect.w, &trade_rect.h);
1591 width2 += trade_rect.w + trade_routes_rect.w + border;
1592 height2 = MAX(height2, trade_rect.h);
1593 height2 = MAX(height2, trade_routes_rect.h + border);
1597 *width = MAX(width1, width2);
1598 *height = height1 + height2;
1601 /* Next fill in X and Y locations. */
1603 if (gui_options.draw_city_names) {
1604 flag_rect.x = canvas_x - *width / 2;
1605 flag_rect.y = canvas_y + (height1 - flag_rect.h) / 2;
1607 occupy_rect.x = flag_rect.x + flag_rect.w;
1608 occupy_rect.y = canvas_y + (height1 - occupy_rect.h) / 2;
1610 name_rect.x = canvas_x + (flag_rect.w + occupy_rect.w
1611 - name_rect.w - size_rect.w - border) / 2;
1612 name_rect.y = canvas_y + (height1 - name_rect.h) / 2;
1614 size_rect.x = canvas_x + (*width + 1) / 2 - size_rect.w - border / 2;
1615 size_rect.y = canvas_y + (height1 - size_rect.h) / 2;
1618 if (should_draw_lower_bar) {
1619 if (should_draw_productions) {
1620 shield_rect.x = canvas_x - *width / 2;
1621 shield_rect.y = canvas_y + height1 + (height2 - shield_rect.h) / 2;
1623 prod_rect.x = shield_rect.x + shield_rect.w + border / 2;
1624 prod_rect.y = canvas_y + height1 + (height2 - prod_rect.h) / 2;
1627 if (should_draw_trade_routes) {
1628 trade_routes_rect.x = canvas_x + (*width + 1) / 2
1629 - trade_routes_rect.w - border / 2;
1630 trade_routes_rect.y = canvas_y + height1
1631 + (height2 - trade_routes_rect.h) / 2;
1633 trade_rect.x = trade_routes_rect.x - border / 2 - trade_rect.w;
1634 trade_rect.y = canvas_y + height1 + (height2 - trade_rect.h) / 2;
1637 if (should_draw_growth) {
1638 growth_rect.x = canvas_x + (*width + 1) / 2
1639 - growth_rect.w - border / 2;
1640 if (trade_routes_rect.w > 0) {
1641 growth_rect.x = growth_rect.x
1642 - trade_routes_rect.w - border / 2 - trade_rect.w - border / 2;
1644 growth_rect.y = canvas_y + height1 + (height2 - growth_rect.h) / 2;
1646 food_rect.x = growth_rect.x - border / 2 - food_rect.w;
1647 food_rect.y = canvas_y + height1 + (height2 - food_rect.h) / 2;
1652 /* Now draw. */
1654 /* Draw the city bar's background. */
1655 for (x = 0; x < *width; x += bg_w) {
1656 for (y = 0; y < *height; y += bg_h) {
1657 canvas_put_sprite(pcanvas, (canvas_x - *width / 2 + x) / map_zoom,
1658 (canvas_y + y) / map_zoom,
1659 bg, 0, 0, *width - x, *height - y);
1663 owner_color = get_player_color(tileset, city_owner(pcity));
1665 if (gui_options.draw_city_names) {
1666 canvas_put_sprite_full(pcanvas, flag_rect.x / map_zoom, flag_rect.y / map_zoom,
1667 flag);
1668 canvas_put_line(pcanvas, owner_color, LINE_NORMAL,
1669 (flag_rect.x + flag_rect.w) / map_zoom - 1, canvas_y / map_zoom,
1670 0, height1);
1671 canvas_put_sprite_full(pcanvas,
1672 occupy_rect.x / map_zoom, occupy_rect.y / map_zoom,
1673 occupy);
1674 canvas_put_text(pcanvas, name_rect.x / map_zoom, name_rect.y / map_zoom,
1675 FONT_CITY_NAME,
1676 get_color(tileset, COLOR_MAPVIEW_CITYTEXT), name);
1677 canvas_put_rectangle(pcanvas, owner_color,
1678 (size_rect.x - border / 2) / map_zoom,
1679 canvas_y / map_zoom,
1680 size_rect.w + border, height1);
1682 /* Try to pick a color for city size text that contrasts with
1683 * player color */
1684 struct color *textcolors[2] = {
1685 get_color(tileset, COLOR_MAPVIEW_CITYTEXT),
1686 get_color(tileset, COLOR_MAPVIEW_CITYTEXT_DARK)
1689 canvas_put_text(pcanvas, size_rect.x / map_zoom, size_rect.y / map_zoom,
1690 FONT_CITY_NAME,
1691 color_best_contrast(owner_color, textcolors,
1692 ARRAY_SIZE(textcolors)), size);
1696 if (should_draw_lower_bar) {
1698 if (should_draw_productions) {
1699 canvas_put_sprite_full(pcanvas,
1700 shield_rect.x / map_zoom, shield_rect.y / map_zoom,
1701 citybar->shields);
1702 canvas_put_text(pcanvas, prod_rect.x / map_zoom, prod_rect.y / map_zoom,
1703 FONT_CITY_PROD,
1704 get_color(tileset, production_color), prod);
1707 if (should_draw_trade_routes) {
1708 canvas_put_sprite_full(pcanvas,
1709 trade_rect.x / map_zoom, trade_rect.y / map_zoom,
1710 citybar->trade);
1711 canvas_put_text(pcanvas,
1712 trade_routes_rect.x / map_zoom, trade_routes_rect.y / map_zoom,
1713 FONT_CITY_PROD,
1714 get_color(tileset, trade_routes_color), trade_routes);
1717 if (should_draw_growth) {
1718 canvas_put_sprite_full(pcanvas,
1719 food_rect.x / map_zoom, food_rect.y / map_zoom,
1720 citybar->food);
1721 canvas_put_text(pcanvas, growth_rect.x / map_zoom, growth_rect.y / map_zoom,
1722 FONT_CITY_PROD,
1723 get_color(tileset, growth_color), growth);
1727 /* Draw the city bar's outline. */
1728 canvas_put_line(pcanvas, owner_color, LINE_NORMAL,
1729 (canvas_x - *width / 2) / map_zoom, canvas_y / map_zoom,
1730 *width, 0);
1731 canvas_put_line(pcanvas, owner_color, LINE_NORMAL,
1732 (canvas_x - *width / 2) / map_zoom, canvas_y / map_zoom,
1733 0, *height);
1734 canvas_put_line(pcanvas, owner_color, LINE_NORMAL,
1735 (canvas_x - *width / 2) / map_zoom,
1736 (canvas_y + *height) / map_zoom - 1,
1737 *width, 0);
1738 canvas_put_line(pcanvas, owner_color, LINE_NORMAL,
1739 (canvas_x - *width / 2 + *width) / map_zoom,
1740 canvas_y / map_zoom,
1741 0, *height);
1743 /* Draw the dividing line if we drew both the
1744 * upper and lower parts. */
1745 if (gui_options.draw_city_names && should_draw_lower_bar) {
1746 canvas_put_line(pcanvas, owner_color, LINE_NORMAL,
1747 (canvas_x - *width / 2) / map_zoom,
1748 (canvas_y + height1) / map_zoom - 1,
1749 *width, 0);
1753 /****************************************************************************
1754 Draw a "small" city bar for the city. This is a subcase of show_city_desc
1755 (see that function for more info) for tilesets that do not have a full
1756 city bar.
1757 ****************************************************************************/
1758 static void show_small_citybar(struct canvas *pcanvas,
1759 int canvas_x, int canvas_y,
1760 struct city *pcity, int *width, int *height)
1762 static char name[512], growth[32], prod[512], trade_routes[32];
1763 enum color_std growth_color;
1764 enum color_std production_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 get_city_mapview_name_and_growth(pcity, name, sizeof(name),
1786 growth, sizeof(growth), &growth_color,
1787 &production_color);
1789 if (gui_options.draw_city_names) {
1790 int drawposx;
1792 /* HACK: put a character's worth of space between the two
1793 * strings if needed. */
1794 get_text_size(&spacer_width, NULL, FONT_CITY_NAME, "M");
1796 total_width = 0;
1797 total_height = 0;
1799 get_text_size(&name_rect.w, &name_rect.h, FONT_CITY_NAME, name);
1800 total_width += name_rect.w;
1801 total_height = MAX(total_height, name_rect.h);
1803 if (gui_options.draw_city_growth && can_see_inside) {
1804 get_text_size(&growth_rect.w, &growth_rect.h, FONT_CITY_PROD, growth);
1805 total_width += spacer_width + growth_rect.w;
1806 total_height = MAX(total_height, growth_rect.h);
1809 if (gui_options.draw_city_trade_routes && can_see_inside) {
1810 get_city_mapview_trade_routes(pcity, trade_routes,
1811 sizeof(trade_routes),
1812 &trade_routes_color);
1813 get_text_size(&trade_routes_rect.w, &trade_routes_rect.h,
1814 FONT_CITY_PROD, trade_routes);
1815 total_width += spacer_width + trade_routes_rect.w;
1816 total_height = MAX(total_height, trade_routes_rect.h);
1819 drawposx = canvas_x;
1820 drawposx -= total_width / 2;
1821 canvas_put_text(pcanvas, drawposx / map_zoom, canvas_y / map_zoom,
1822 FONT_CITY_NAME,
1823 get_color(tileset, COLOR_MAPVIEW_CITYTEXT), name);
1824 drawposx += name_rect.w;
1826 if (gui_options.draw_city_growth && can_see_inside) {
1827 drawposx += spacer_width;
1828 canvas_put_text(pcanvas, drawposx / map_zoom,
1829 (canvas_y + total_height - growth_rect.h) / map_zoom,
1830 FONT_CITY_PROD,
1831 get_color(tileset, growth_color), growth);
1832 drawposx += growth_rect.w;
1835 if (gui_options.draw_city_trade_routes && can_see_inside) {
1836 drawposx += spacer_width;
1837 canvas_put_text(pcanvas, drawposx / map_zoom,
1838 (canvas_y + total_height - trade_routes_rect.h) / map_zoom,
1839 FONT_CITY_PROD,
1840 get_color(tileset, trade_routes_color), trade_routes);
1841 drawposx += trade_routes_rect.w;
1844 canvas_y += total_height + 3;
1846 *width = MAX(*width, total_width);
1847 *height += total_height + 3;
1849 if (gui_options.draw_city_productions && can_see_inside) {
1850 get_city_mapview_production(pcity, prod, sizeof(prod));
1851 get_text_size(&prod_rect.w, &prod_rect.h, FONT_CITY_PROD, prod);
1853 total_width = prod_rect.w;
1854 total_height = prod_rect.h;
1856 canvas_put_text(pcanvas, (canvas_x - total_width / 2) / map_zoom,
1857 canvas_y / map_zoom,
1858 FONT_CITY_PROD,
1859 get_color(tileset, production_color), prod);
1861 canvas_y += total_height;
1862 *width = MAX(*width, total_width);
1863 *height += total_height;
1867 /****************************************************************************
1868 Draw a description for the given city. This description may include the
1869 name, turns-to-grow, production, and city turns-to-build (depending on
1870 client options).
1872 (canvas_x, canvas_y) gives the location on the given canvas at which to
1873 draw the description. This is the location of the city itself so the
1874 text must be drawn underneath it. pcity gives the city to be drawn,
1875 while (*width, *height) should be set by show_city_desc to contain the
1876 width and height of the text block (centered directly underneath the
1877 city's tile).
1878 ****************************************************************************/
1879 static void show_city_desc(struct canvas *pcanvas,
1880 int canvas_x, int canvas_y,
1881 struct city *pcity, int *width, int *height)
1883 if (gui_options.draw_full_citybar) {
1884 show_full_citybar(pcanvas, canvas_x, canvas_y, pcity, width, height);
1885 } else {
1886 show_small_citybar(pcanvas, canvas_x, canvas_y, pcity, width, height);
1890 /****************************************************************************
1891 Draw a label for the given tile.
1893 (canvas_x, canvas_y) gives the location on the given canvas at which to
1894 draw the label. This is the location of the tile itself so the
1895 text must be drawn underneath it. pcity gives the city to be drawn,
1896 while (*width, *height) should be set by show_tile_label to contain the
1897 width and height of the text block (centered directly underneath the
1898 city's tile).
1899 ****************************************************************************/
1900 static void show_tile_label(struct canvas *pcanvas,
1901 int canvas_x, int canvas_y,
1902 struct tile *ptile, int *width, int *height)
1904 const enum client_font FONT_TILE_LABEL = FONT_CITY_NAME; /* TODO: new font */
1905 #define COLOR_MAPVIEW_TILELABEL COLOR_MAPVIEW_CITYTEXT
1907 canvas_x += tileset_tile_width(tileset) / 2 * map_zoom;
1908 canvas_y += tileset_tilelabel_offset_y(tileset) * map_zoom;
1910 get_text_size(width, height, FONT_TILE_LABEL, ptile->label);
1912 canvas_put_text(pcanvas, (canvas_x - * width / 2) / map_zoom, canvas_y / map_zoom,
1913 FONT_TILE_LABEL,
1914 get_color(tileset, COLOR_MAPVIEW_TILELABEL), ptile->label);
1915 #undef COLOR_MAPVIEW_TILELABEL
1918 /**************************************************************************
1919 Show descriptions for all cities visible on the map canvas.
1920 **************************************************************************/
1921 void show_city_descriptions(int canvas_base_x, int canvas_base_y,
1922 int width_base, int height_base)
1924 const int dx = max_desc_width - tileset_tile_width(tileset) * map_zoom;
1925 const int dy = max_desc_height;
1926 const int offset_y = tileset_citybar_offset_y(tileset) * map_zoom;
1927 int new_max_width = max_desc_width, new_max_height = max_desc_height;
1929 if (gui_options.draw_full_citybar && !(gui_options.draw_city_names
1930 || gui_options.draw_city_productions
1931 || gui_options.draw_city_growth)) {
1932 return;
1935 if (!gui_options.draw_full_citybar && !(gui_options.draw_city_names
1936 || gui_options.draw_city_productions)) {
1937 return;
1940 /* A city description is shown below the city. It has a specified
1941 * maximum width and height (although these are only estimates). Thus
1942 * we need to update some tiles above the mapview and some to the left
1943 * and right.
1945 * /--W1--\ (W1 = tileset_tile_width(tileset))
1946 * -------- \
1947 * | CITY | H1 (H1 = tileset_tile_height(tileset))
1948 * | | /
1949 * ------------------ \
1950 * | DESCRIPTION | H2 (H2 = MAX_CITY_DESC_HEIGHT)
1951 * | | /
1952 * ------------------
1953 * \-------W2-------/ (W2 = MAX_CITY_DESC_WIDTH)
1955 * We must draw H2 extra pixels above and (W2 - W1) / 2 extra pixels
1956 * to each side of the mapview.
1958 gui_rect_iterate_coord(mapview.gui_x0 + canvas_base_x - dx / 2,
1959 mapview.gui_y0 + canvas_base_y - dy,
1960 width_base + dx, height_base + dy - offset_y,
1961 ptile, pedge, pcorner, gui_x, gui_y, map_zoom) {
1962 const int canvas_x = gui_x - mapview.gui_x0;
1963 const int canvas_y = gui_y - mapview.gui_y0;
1965 if (ptile && tile_city(ptile)) {
1966 int width = 0, height = 0;
1967 struct city *pcity = tile_city(ptile);
1969 show_city_desc(mapview.store, canvas_x, canvas_y,
1970 pcity, &width, &height);
1971 log_debug("Drawing %s.", city_name_get(pcity));
1973 if (width > max_desc_width || height > max_desc_height) {
1974 /* The update was incomplete! We queue a new update. Note that
1975 * this is recursively queueing an update within a dequeuing of an
1976 * update. This is allowed specifically because of the code in
1977 * unqueue_mapview_updates. See that function for more. */
1978 log_debug("Re-queuing %s.", city_name_get(pcity));
1979 update_city_description(pcity);
1981 new_max_width = MAX(width, new_max_width);
1982 new_max_height = MAX(height, new_max_height);
1984 } gui_rect_iterate_coord_end;
1986 /* We don't update the new max values until the end, so that the
1987 * check above to see what cities need redrawing will be complete. */
1988 max_desc_width = MAX(max_desc_width, new_max_width);
1989 max_desc_height = MAX(max_desc_height, new_max_height);
1992 /**************************************************************************
1993 Show labels for all tiles visible on the map canvas.
1994 **************************************************************************/
1995 void show_tile_labels(int canvas_base_x, int canvas_base_y,
1996 int width_base, int height_base)
1998 const int dx = max_label_width - tileset_tile_width(tileset) * map_zoom;
1999 const int dy = max_label_height;
2000 int new_max_width = max_label_width, new_max_height = max_label_height;
2002 gui_rect_iterate_coord(mapview.gui_x0 + canvas_base_x - dx / 2,
2003 mapview.gui_y0 + canvas_base_y - dy,
2004 width_base + dx, height_base + dy,
2005 ptile, pedge, pcorner, gui_x, gui_y, map_zoom) {
2006 const int canvas_x = gui_x - mapview.gui_x0;
2007 const int canvas_y = gui_y - mapview.gui_y0;
2009 if (ptile && ptile->label != NULL) {
2010 int width = 0, height = 0;
2012 show_tile_label(mapview.store, canvas_x, canvas_y,
2013 ptile, &width, &height);
2014 log_debug("Drawing label %s.", ptile->label);
2016 if (width > max_label_width || height > max_label_height) {
2017 /* The update was incomplete! We queue a new update. Note that
2018 * this is recursively queueing an update within a dequeuing of an
2019 * update. This is allowed specifically because of the code in
2020 * unqueue_mapview_updates. See that function for more. */
2021 log_debug("Re-queuing tile label %s drawing.", ptile->label);
2022 update_tile_label(ptile);
2024 new_max_width = MAX(width, new_max_width);
2025 new_max_height = MAX(height, new_max_height);
2027 } gui_rect_iterate_coord_end;
2029 /* We don't update the new max values until the end, so that the
2030 * check above to see what cities need redrawing will be complete. */
2031 max_label_width = MAX(max_label_width, new_max_width);
2032 max_label_height = MAX(max_label_height, new_max_height);
2035 /****************************************************************************
2036 Draw the goto route for the unit. Return TRUE if anything is drawn.
2038 This duplicates drawing code that is run during the hover state.
2039 ****************************************************************************/
2040 bool show_unit_orders(struct unit *punit)
2042 if (punit && unit_has_orders(punit)) {
2043 struct tile *ptile = unit_tile(punit);
2044 int i;
2046 for (i = 0; i < punit->orders.length; i++) {
2047 int idx = (punit->orders.index + i) % punit->orders.length;
2048 struct unit_order *order;
2050 if (punit->orders.index + i >= punit->orders.length
2051 && !punit->orders.repeat) {
2052 break;
2055 order = &punit->orders.list[idx];
2057 switch (order->order) {
2058 case ORDER_MOVE:
2059 draw_segment(ptile, order->dir);
2060 ptile = mapstep(ptile, order->dir);
2061 if (!ptile) {
2062 /* This shouldn't happen unless the server gives us invalid
2063 * data. To avoid disaster we need to break out of the
2064 * switch and the enclosing for loop. */
2065 fc_assert(NULL != ptile);
2066 i = punit->orders.length;
2068 break;
2069 default:
2070 /* TODO: graphics for other orders. */
2071 break;
2074 return TRUE;
2075 } else {
2076 return FALSE;
2080 /****************************************************************************
2081 Draw a goto line at the given location and direction. The line goes from
2082 the source tile to the adjacent tile in the given direction.
2083 ****************************************************************************/
2084 void draw_segment(struct tile *src_tile, enum direction8 dir)
2086 float canvas_x, canvas_y, canvas_dx, canvas_dy;
2088 /* Determine the source position of the segment. */
2089 (void) tile_to_canvas_pos(&canvas_x, &canvas_y, src_tile);
2090 canvas_x += tileset_tile_width(tileset) / 2 * map_zoom;
2091 canvas_y += tileset_tile_height(tileset) / 2 * map_zoom;
2093 /* Determine the vector of the segment. */
2094 map_to_gui_vector(tileset, map_zoom, &canvas_dx, &canvas_dy,
2095 DIR_DX[dir], DIR_DY[dir]);
2097 /* Draw the segment. */
2098 canvas_put_line(mapview.store,
2099 get_color(tileset, COLOR_MAPVIEW_GOTO), LINE_GOTO,
2100 canvas_x, canvas_y, canvas_dx, canvas_dy);
2102 /* The actual area drawn will extend beyond the base rectangle, since
2103 * the goto lines have width. */
2104 dirty_rect(MIN(canvas_x, canvas_x + canvas_dx) - GOTO_WIDTH,
2105 MIN(canvas_y, canvas_y + canvas_dy) - GOTO_WIDTH,
2106 ABS(canvas_dx) + 2 * GOTO_WIDTH,
2107 ABS(canvas_dy) + 2 * GOTO_WIDTH);
2109 /* It is possible that the mapview wraps between the source and dest
2110 * tiles. In this case they will not be next to each other; they'll be
2111 * on the opposite sides of the screen. If this happens then the dest
2112 * tile will not be updated. This is consistent with the mapview design
2113 * which fails when the size of the mapview approaches that of the map. */
2116 /****************************************************************************
2117 This function is called to decrease a unit's HP smoothly in battle
2118 when combat_animation is turned on.
2119 ****************************************************************************/
2120 void decrease_unit_hp_smooth(struct unit *punit0, int hp0,
2121 struct unit *punit1, int hp1)
2123 static struct timer *anim_timer = NULL;
2124 const struct sprite_vector *anim = get_unit_explode_animation(tileset);
2125 const int num_tiles_explode_unit = sprite_vector_size(anim);
2126 struct unit *losing_unit = (hp0 == 0 ? punit0 : punit1);
2127 float canvas_x, canvas_y;
2128 int i;
2130 set_units_in_combat(punit0, punit1);
2132 /* Make sure we don't start out with fewer HP than we're supposed to
2133 * end up with (which would cause the following loop to break). */
2134 punit0->hp = MAX(punit0->hp, hp0);
2135 punit1->hp = MAX(punit1->hp, hp1);
2137 unqueue_mapview_updates(TRUE);
2138 while (punit0->hp > hp0 || punit1->hp > hp1) {
2139 const int diff0 = punit0->hp - hp0, diff1 = punit1->hp - hp1;
2141 anim_timer = timer_renew(anim_timer, TIMER_USER, TIMER_ACTIVE);
2142 timer_start(anim_timer);
2144 if (fc_rand(diff0 + diff1) < diff0) {
2145 punit0->hp--;
2146 refresh_unit_mapcanvas(punit0, unit_tile(punit0), FALSE, FALSE);
2147 } else {
2148 punit1->hp--;
2149 refresh_unit_mapcanvas(punit1, unit_tile(punit1), FALSE, FALSE);
2152 unqueue_mapview_updates(TRUE);
2153 gui_flush();
2155 timer_usleep_since_start(anim_timer,
2156 gui_options.smooth_combat_step_msec * 1000ul);
2159 if (num_tiles_explode_unit > 0
2160 && tile_to_canvas_pos(&canvas_x, &canvas_y,
2161 unit_tile(losing_unit))) {
2162 refresh_unit_mapcanvas(losing_unit, unit_tile(losing_unit), FALSE, FALSE);
2163 unqueue_mapview_updates(FALSE);
2164 canvas_copy(mapview.tmp_store, mapview.store,
2165 canvas_x, canvas_y, canvas_x, canvas_y,
2166 tileset_tile_width(tileset) * map_zoom,
2167 tileset_tile_height(tileset) * map_zoom);
2169 for (i = 0; i < num_tiles_explode_unit; i++) {
2170 int w, h;
2171 struct sprite *sprite = *sprite_vector_get(anim, i);
2173 get_sprite_dimensions(sprite, &w, &h);
2174 anim_timer = timer_renew(anim_timer, TIMER_USER, TIMER_ACTIVE);
2175 timer_start(anim_timer);
2177 /* We first draw the explosion onto the unit and draw draw the
2178 * complete thing onto the map canvas window. This avoids
2179 * flickering. */
2180 canvas_copy(mapview.store, mapview.tmp_store,
2181 canvas_x, canvas_y, canvas_x, canvas_y,
2182 tileset_tile_width(tileset) * map_zoom,
2183 tileset_tile_height(tileset) * map_zoom);
2184 canvas_put_sprite_full(mapview.store,
2185 canvas_x + tileset_tile_width(tileset) / 2 * map_zoom
2186 - w / 2,
2187 canvas_y + tileset_tile_height(tileset) / 2 * map_zoom
2188 - h / 2,
2189 sprite);
2190 dirty_rect(canvas_x, canvas_y, tileset_tile_width(tileset) * map_zoom,
2191 tileset_tile_height(tileset) * map_zoom);
2193 flush_dirty();
2194 gui_flush();
2196 timer_usleep_since_start(anim_timer,
2197 gui_options.smooth_combat_step_msec * 2 * 1000ul);
2201 set_units_in_combat(NULL, NULL);
2202 refresh_unit_mapcanvas(punit0, unit_tile(punit0), TRUE, FALSE);
2203 refresh_unit_mapcanvas(punit1, unit_tile(punit1), TRUE, FALSE);
2206 /**************************************************************************
2207 Animates punit's "smooth" move from (x0, y0) to (x0+dx, y0+dy).
2208 Note: Works only for adjacent-tile moves.
2209 **************************************************************************/
2210 void move_unit_map_canvas(struct unit *punit,
2211 struct tile *src_tile, int dx, int dy)
2213 static struct timer *anim_timer = NULL;
2214 struct tile *dest_tile;
2215 int dest_x, dest_y, src_x, src_y;
2216 int prev_x = -1;
2217 int prev_y = -1;
2218 int tuw;
2219 int tuh;
2221 /* only works for adjacent-square moves */
2222 if (dx < -1 || dx > 1 || dy < -1 || dy > 1 || (dx == 0 && dy == 0)) {
2223 return;
2226 index_to_map_pos(&src_x, &src_y, tile_index(src_tile));
2227 dest_x = src_x + dx;
2228 dest_y = src_y + dy;
2229 dest_tile = map_pos_to_tile(dest_x, dest_y);
2230 if (!dest_tile) {
2231 return;
2234 if (tile_visible_mapcanvas(src_tile)
2235 || tile_visible_mapcanvas(dest_tile)) {
2236 float start_x, start_y;
2237 float canvas_dx, canvas_dy;
2238 double timing_sec = (double)gui_options.smooth_move_unit_msec / 1000.0;
2239 double mytime;
2241 fc_assert(gui_options.smooth_move_unit_msec > 0);
2243 map_to_gui_vector(tileset, map_zoom, &canvas_dx, &canvas_dy, dx, dy);
2245 tile_to_canvas_pos(&start_x, &start_y, src_tile);
2246 if (tileset_is_isometric(tileset) && tileset_hex_height(tileset) == 0) {
2247 start_y -= tileset_tile_height(tileset) / 2 * map_zoom;
2248 start_y -= (tileset_unit_height(tileset) - tileset_full_tile_height(tileset)) * map_zoom;
2251 /* Bring the backing store up to date, but don't flush. */
2252 unqueue_mapview_updates(FALSE);
2254 /* Start the timer (AFTER the unqueue above). */
2255 anim_timer = timer_renew(anim_timer, TIMER_USER, TIMER_ACTIVE);
2256 timer_start(anim_timer);
2258 tuw = tileset_unit_width(tileset) * map_zoom;
2259 tuh = tileset_unit_height(tileset) * map_zoom;
2261 do {
2262 int new_x, new_y;
2264 mytime = MIN(timer_read_seconds(anim_timer), timing_sec);
2266 new_x = start_x + canvas_dx * (mytime / timing_sec);
2267 new_y = start_y + canvas_dy * (mytime / timing_sec);
2269 if (new_x != prev_x || new_y != prev_y) {
2270 /* Backup the canvas store to the temp store. */
2271 canvas_copy(mapview.tmp_store, mapview.store,
2272 new_x, new_y, new_x, new_y,
2273 tuw, tuh);
2275 /* Draw */
2276 put_unit(punit, mapview.store, map_zoom, new_x, new_y);
2277 dirty_rect(new_x, new_y, tuw, tuh);
2279 /* Flush. */
2280 flush_dirty();
2281 gui_flush();
2283 /* Restore the backup. It won't take effect until the next flush. */
2284 canvas_copy(mapview.store, mapview.tmp_store,
2285 new_x, new_y, new_x, new_y,
2286 tuw, tuh);
2287 dirty_rect(new_x, new_y, tuw, tuh);
2289 prev_x = new_x;
2290 prev_y = new_y;
2291 } else {
2292 fc_usleep(500);
2294 } while (mytime < timing_sec);
2298 /**************************************************************************
2299 Find the "best" city/settlers to associate with the selected tile.
2300 a. If a visible city is working the tile, return that city.
2301 b. If another player's city is working the tile, return NULL.
2302 c. If any selected cities are within range, return the closest one.
2303 d. If any cities are within range, return the closest one.
2304 e. If any active (with color) settler could work it if they founded a
2305 city, choose the closest one (only if punit != NULL).
2306 f. If any settler could work it if they founded a city, choose the
2307 closest one (only if punit != NULL).
2308 g. If nobody can work it, return NULL.
2309 **************************************************************************/
2310 struct city *find_city_or_settler_near_tile(const struct tile *ptile,
2311 struct unit **punit)
2313 struct city *closest_city;
2314 struct city *pcity;
2315 struct unit *closest_settler = NULL, *best_settler = NULL;
2316 int max_rad = rs_max_city_radius_sq();
2318 if (punit) {
2319 *punit = NULL;
2322 /* Check if there is visible city working that tile */
2323 pcity = tile_worked(ptile);
2324 if (pcity && pcity->tile) {
2325 if (NULL == client.conn.playing
2326 || city_owner(pcity) == client.conn.playing) {
2327 /* rule a */
2328 return pcity;
2329 } else {
2330 /* rule b */
2331 return NULL;
2335 /* rule e */
2336 closest_city = NULL;
2338 /* check within maximum (squared) city radius */
2339 city_tile_iterate(max_rad, ptile, tile1) {
2340 pcity = tile_city(tile1);
2341 if (pcity
2342 && (NULL == client.conn.playing
2343 || city_owner(pcity) == client.conn.playing)
2344 && client_city_can_work_tile(pcity, tile1)) {
2346 * Note, we must explicitly check if the tile is workable (with
2347 * city_can_work_tile() above), since it is possible that another
2348 * city (perhaps an UNSEEN city) may be working it!
2351 if (mapdeco_is_highlight_set(city_tile(pcity))) {
2352 /* rule c */
2353 return pcity;
2355 if (!closest_city) {
2356 closest_city = pcity;
2359 } city_tile_iterate_end;
2361 /* rule d */
2362 if (closest_city || !punit) {
2363 return closest_city;
2366 if (!game.scenario.prevent_new_cities) {
2367 /* check within maximum (squared) city radius */
2368 city_tile_iterate(max_rad, ptile, tile1) {
2369 unit_list_iterate(tile1->units, psettler) {
2370 if ((NULL == client.conn.playing
2371 || unit_owner(psettler) == client.conn.playing)
2372 && unit_has_type_flag(psettler, UTYF_CITIES)
2373 && city_can_be_built_here(unit_tile(psettler), psettler)) {
2374 if (!closest_settler) {
2375 closest_settler = psettler;
2377 if (!best_settler && psettler->client.colored) {
2378 best_settler = psettler;
2381 } unit_list_iterate_end;
2382 } city_tile_iterate_end;
2384 if (best_settler) {
2385 /* Rule e */
2386 *punit = best_settler;
2387 } else if (closest_settler) {
2388 /* Rule f */
2389 *punit = closest_settler;
2393 /* rule g */
2394 return NULL;
2397 /**************************************************************************
2398 Find the nearest/best city that owns the tile.
2399 **************************************************************************/
2400 struct city *find_city_near_tile(const struct tile *ptile)
2402 return find_city_or_settler_near_tile(ptile, NULL);
2405 /**************************************************************************
2406 Append the buy cost of the current production of the given city to the
2407 already NULL-terminated buffer. Does nothing if draw_city_buycost is
2408 set to FALSE, or if it does not make sense to buy the current production
2409 (e.g. coinage).
2410 **************************************************************************/
2411 static void append_city_buycost_string(const struct city *pcity,
2412 char *buffer, int buffer_len)
2414 if (!pcity || !buffer || buffer_len < 1) {
2415 return;
2418 if (!gui_options.draw_city_buycost || !city_can_buy(pcity)) {
2419 return;
2422 cat_snprintf(buffer, buffer_len, "/%d",
2423 city_production_buy_gold_cost(pcity));
2426 /**************************************************************************
2427 Find the mapview city production text for the given city, and place it
2428 into the buffer.
2429 **************************************************************************/
2430 void get_city_mapview_production(struct city *pcity,
2431 char *buffer, size_t buffer_len)
2433 int turns;
2435 universal_name_translation(&pcity->production, buffer, buffer_len);
2437 if (city_production_has_flag(pcity, IF_GOLD)) {
2438 return;
2440 turns = city_production_turns_to_build(pcity, TRUE);
2442 if (999 < turns) {
2443 cat_snprintf(buffer, buffer_len, " -");
2444 } else {
2445 cat_snprintf(buffer, buffer_len, " %d", turns);
2448 append_city_buycost_string(pcity, buffer, buffer_len);
2451 /**************************************************************************
2452 Find the mapview city trade routes text for the given city, and place it
2453 into the buffer. Sets 'pcolor' to the preferred color the text should
2454 be drawn in if it is non-NULL.
2455 **************************************************************************/
2456 void get_city_mapview_trade_routes(struct city *pcity,
2457 char *trade_routes_buffer,
2458 size_t trade_routes_buffer_len,
2459 enum color_std *pcolor)
2461 int num_trade_routes = 0, i;
2462 int max_routes;
2464 if (!trade_routes_buffer || trade_routes_buffer_len <= 0) {
2465 return;
2468 if (!pcity) {
2469 trade_routes_buffer[0] = '\0';
2470 if (pcolor) {
2471 *pcolor = COLOR_MAPVIEW_CITYTEXT;
2473 return;
2476 for (i = 0; i < MAX_TRADE_ROUTES; i++) {
2477 if (pcity->trade[i] <= 0) {
2478 /* NB: pcity->trade_value[i] == 0 is a valid case. */
2479 continue;
2481 num_trade_routes++;
2484 max_routes = max_trade_routes(pcity);
2486 fc_snprintf(trade_routes_buffer, trade_routes_buffer_len,
2487 "%d/%d", num_trade_routes, max_routes);
2489 if (pcolor) {
2490 if (num_trade_routes == max_routes) {
2491 *pcolor = COLOR_MAPVIEW_TRADE_ROUTES_ALL_BUILT;
2492 } else if (num_trade_routes == 0) {
2493 *pcolor = COLOR_MAPVIEW_TRADE_ROUTES_NO_BUILT;
2494 } else {
2495 *pcolor = COLOR_MAPVIEW_TRADE_ROUTES_SOME_BUILT;
2500 /***************************************************************************/
2501 static enum update_type needed_updates = UPDATE_NONE;
2502 static bool callback_queued = FALSE;
2504 /* These values hold the tiles that need city, unit, or tile updates.
2505 * These different types of updates just tell what area need to be updated,
2506 * not necessarily what's sitting on the tile. A city update covers the
2507 * whole citymap area. A unit update covers just the "full" unit tile
2508 * area. A tile update covers the base tile plus half a tile in each
2509 * direction. */
2510 struct tile_list *tile_updates[TILE_UPDATE_COUNT];
2512 /****************************************************************************
2513 This callback is called during an idle moment to unqueue any pending
2514 mapview updates.
2515 ****************************************************************************/
2516 static void queue_callback(void *data)
2518 callback_queued = FALSE;
2519 unqueue_mapview_updates(TRUE);
2522 /****************************************************************************
2523 When a mapview update is queued this function should be called to prepare
2524 an idle-time callback to unqueue the updates.
2525 ****************************************************************************/
2526 static void queue_add_callback(void)
2528 if (!callback_queued) {
2529 callback_queued = TRUE;
2530 add_idle_callback(queue_callback, NULL);
2534 /**************************************************************************
2535 This function, along with unqueue_mapview_update(), helps in updating
2536 the mapview when a packet is received. Previously, we just called
2537 update_map_canvas when (for instance) a city update was received.
2538 Not only would this often end up with a lot of duplicated work, but it
2539 would also draw over the city descriptions, which would then just
2540 "disappear" from the mapview. The hack is to instead call
2541 queue_mapview_update in place of this update, and later (after all
2542 packets have been read) call unqueue_mapview_update. The functions
2543 don't track which areas of the screen need updating, rather when the
2544 unqueue is done we just update the whole visible mapqueue, and redraw
2545 the city descriptions.
2547 Using these functions, updates are done correctly, and are probably
2548 faster too. But it's a bit of a hack to insert this code into the
2549 packet-handling code.
2550 **************************************************************************/
2551 void queue_mapview_update(enum update_type update)
2553 if (can_client_change_view()) {
2554 needed_updates |= update;
2555 queue_add_callback();
2559 /**************************************************************************
2560 Queue this tile to be refreshed. The refresh will be done some time
2561 soon thereafter, and grouped with other needed refreshes.
2563 Note this should only be called for tiles. For cities or units use
2564 queue_mapview_xxx_update instead.
2565 **************************************************************************/
2566 void queue_mapview_tile_update(struct tile *ptile,
2567 enum tile_update_type type)
2569 if (can_client_change_view()) {
2570 if (!tile_updates[type]) {
2571 tile_updates[type] = tile_list_new();
2573 tile_list_append(tile_updates[type], ptile);
2574 queue_add_callback();
2578 /**************************************************************************
2579 See comment for queue_mapview_update().
2580 **************************************************************************/
2581 void unqueue_mapview_updates(bool write_to_screen)
2583 /* Calculate the area covered by each update type. The area array gives
2584 * the offset from the tile origin as well as the width and height of the
2585 * area to be updated. This is initialized each time when entering the
2586 * function from the existing tileset variables.
2588 * A TILE update covers the base tile (W x H) plus a half-tile in each
2589 * direction (for edge/corner graphics), making its area 2W x 2H.
2591 * A UNIT update covers a UW x UH area. This is centered horizontally
2592 * over the tile but extends up above the tile (e.g., units in iso-view).
2594 * A CITYMAP update covers the whole citymap of a tile. This includes
2595 * the citymap area itself plus an extra half-tile in each direction (for
2596 * edge/corner graphics).
2598 const float W = tileset_tile_width(tileset) * map_zoom;
2599 const float H = tileset_tile_height(tileset) * map_zoom;
2600 const float UW = tileset_unit_width(tileset) * map_zoom;
2601 const float UH = tileset_unit_height(tileset) * map_zoom;
2602 const float city_width = get_citydlg_canvas_width() * map_zoom + W;
2603 const float city_height = get_citydlg_canvas_height() * map_zoom + H;
2604 const struct {
2605 float dx, dy, w, h;
2606 } area[TILE_UPDATE_COUNT] = {
2607 {0, 0, W, H},
2608 {-W / 2, -H / 2, 2 * W, 2 * H},
2609 {(W - UW) / 2, H - UH, UW, UH},
2610 {-(max_desc_width - W) / 2, H, max_desc_width, max_desc_height},
2611 {-(city_width - W) / 2, -(city_height - H) / 2, city_width, city_height},
2612 {-(max_label_width - W) / 2, H, max_label_width, max_label_height}
2614 struct tile_list *my_tile_updates[TILE_UPDATE_COUNT];
2616 int i;
2618 if (!can_client_change_view()) {
2619 /* Double sanity check: make sure we don't unqueue an invalid update
2620 * after we've already detached. */
2621 return;
2624 log_debug("unqueue_mapview_update: needed_updates=%d",
2625 needed_updates);
2627 /* This code "pops" the lists of tile updates off of the static array and
2628 * stores them locally. This allows further updates to be queued within
2629 * the function itself (namely, within update_map_canvas). */
2630 for (i = 0; i < TILE_UPDATE_COUNT; i++) {
2631 my_tile_updates[i] = tile_updates[i];
2632 tile_updates[i] = NULL;
2635 if (map_exists()) {
2636 if ((needed_updates & UPDATE_MAP_CANVAS_VISIBLE)
2637 || (needed_updates & UPDATE_CITY_DESCRIPTIONS)
2638 || (needed_updates & UPDATE_TILE_LABELS)) {
2639 dirty_all();
2640 update_map_canvas(0, 0, mapview.store_width,
2641 mapview.store_height);
2642 /* Have to update the overview too, since some tiles may have changed. */
2643 refresh_overview_canvas();
2644 } else {
2645 int min_x = mapview.width, min_y = mapview.height;
2646 int max_x = 0, max_y = 0;
2648 for (i = 0; i < TILE_UPDATE_COUNT; i++) {
2649 if (my_tile_updates[i]) {
2650 tile_list_iterate(my_tile_updates[i], ptile) {
2651 float xl, yt;
2652 int xr, yb;
2654 (void) tile_to_canvas_pos(&xl, &yt, ptile);
2656 xl += area[i].dx;
2657 yt += area[i].dy;
2658 xr = xl + area[i].w;
2659 yb = yt + area[i].h;
2661 if (xr > 0 && xl < mapview.width
2662 && yb > 0 && yt < mapview.height) {
2663 min_x = MIN(min_x, xl);
2664 min_y = MIN(min_y, yt);
2665 max_x = MAX(max_x, xr);
2666 max_y = MAX(max_y, yb);
2669 /* FIXME: These overview updates should be batched as well.
2670 * Right now they account for as much as 90% of the runtime of
2671 * the unqueue. */
2672 overview_update_tile(ptile);
2673 } tile_list_iterate_end;
2677 if (min_x < max_x && min_y < max_y) {
2678 update_map_canvas(min_x, min_y, max_x - min_x, max_y - min_y);
2683 for (i = 0; i < TILE_UPDATE_COUNT; i++) {
2684 if (my_tile_updates[i]) {
2685 tile_list_destroy(my_tile_updates[i]);
2688 needed_updates = UPDATE_NONE;
2690 if (write_to_screen) {
2691 flush_dirty();
2692 flush_dirty_overview();
2696 /**************************************************************************
2697 Fill the two buffers which information about the city which is shown
2698 below it. It does not take draw_city_names/draw_city_growth into account.
2699 **************************************************************************/
2700 void get_city_mapview_name_and_growth(struct city *pcity,
2701 char *name_buffer,
2702 size_t name_buffer_len,
2703 char *growth_buffer,
2704 size_t growth_buffer_len,
2705 enum color_std *growth_color,
2706 enum color_std *production_color)
2708 fc_strlcpy(name_buffer, city_name_get(pcity), name_buffer_len);
2710 *production_color = COLOR_MAPVIEW_CITYTEXT;
2711 if (NULL == client.conn.playing
2712 || city_owner(pcity) == client.conn.playing) {
2713 int turns = city_turns_to_grow(pcity);
2715 if (turns == 0) {
2716 fc_snprintf(growth_buffer, growth_buffer_len, "X");
2717 } else if (turns == FC_INFINITY) {
2718 fc_snprintf(growth_buffer, growth_buffer_len, "-");
2719 } else {
2720 /* Negative turns means we're shrinking, but that's handled
2721 down below. */
2722 fc_snprintf(growth_buffer, growth_buffer_len, "%d", abs(turns));
2725 if (turns <= 0) {
2726 /* A blocked or shrinking city has its growth status shown in red. */
2727 *growth_color = COLOR_MAPVIEW_CITYGROWTH_BLOCKED;
2728 } else {
2729 *growth_color = COLOR_MAPVIEW_CITYTEXT;
2732 if (pcity->surplus[O_SHIELD] < 0) {
2733 *production_color = COLOR_MAPVIEW_CITYGROWTH_BLOCKED;
2735 } else {
2736 growth_buffer[0] = '\0';
2737 *growth_color = COLOR_MAPVIEW_CITYTEXT;
2741 /**************************************************************************
2742 Returns TRUE if cached drawing is possible. If the mapview is too large
2743 we have to turn it off.
2744 **************************************************************************/
2745 static bool can_do_cached_drawing(void)
2747 const int W = tileset_tile_width(tileset) * map_zoom;
2748 const int H = tileset_tile_height(tileset) * map_zoom;
2749 int w = mapview.store_width, h = mapview.store_height;
2751 /* If the mapview window is too large, cached drawing is not possible.
2753 * BACKGROUND: cached drawing occurrs when the mapview is scrolled just
2754 * a short distance. The majority of the mapview window can simply be
2755 * copied while the newly visible areas must be drawn from scratch. This
2756 * speeds up drawing significantly, especially when using the scrollbars
2757 * or mapview sliding.
2759 * When the mapview is larger than the map, however, some tiles may become
2760 * visible twice. In this case one instance of the tile will be drawn
2761 * while all others are drawn black. When this happens the cached drawing
2762 * system breaks since it assumes the mapview canvas is an "ideal" window
2763 * over the map. So black tiles may be scrolled from the edges of the
2764 * mapview into the center, while drawn tiles may be scrolled from the
2765 * center of the mapview out to the edges. The result is very bad.
2767 * There are a few different ways this could be solved. One way is simply
2768 * to turn off cached drawing, which is what we do now. If the mapview
2769 * window gets to be too large, the caching is disabled. Another would
2770 * be to prevent the window from getting too large in the first place -
2771 * but because the window boundaries aren't at an even tile this would
2772 * mean the entire map could never be shown. Yet another way would be
2773 * to draw tiles more than once if they are visible in multiple locations
2774 * on the mapview.
2776 * The logic below is complicated and determined in part by
2777 * trial-and-error. */
2778 if (!current_topo_has_flag(TF_WRAPX) && !current_topo_has_flag(TF_WRAPY)) {
2779 /* An unwrapping map: no limitation. On an unwrapping map no tile can
2780 * be visible twice so there's no problem. */
2781 return TRUE;
2783 if (XOR(current_topo_has_flag(TF_ISO) || current_topo_has_flag(TF_HEX),
2784 tileset_is_isometric(tileset))) {
2785 /* Non-matching. In this case the mapview does not line up with the
2786 * map's axis of wrapping. This will give very bad results for the
2787 * player!
2788 * We can never show more than half of the map.
2790 * We divide by 4 below because we have to divide by 2 twice. The
2791 * first division by 2 is because the square must be half the size
2792 * of the (width+height). The second division by two is because for
2793 * an iso-map, NATURAL_XXX has a scale of 2, whereas for iso-view
2794 * NORMAL_TILE_XXX has a scale of 2. */
2795 return (w <= (NATURAL_WIDTH + NATURAL_HEIGHT) * W / 4
2796 && h <= (NATURAL_WIDTH + NATURAL_HEIGHT) * H / 4);
2797 } else {
2798 /* Matching. */
2799 const int isofactor = (tileset_is_isometric(tileset) ? 2 : 1);
2800 const int isodiff = (tileset_is_isometric(tileset) ? 6 : 2);
2802 /* Now we can use the full width and height, with the exception of a small
2803 * area on each side. */
2804 if (current_topo_has_flag(TF_WRAPX)
2805 && w > (NATURAL_WIDTH - isodiff) * W / isofactor) {
2806 return FALSE;
2808 if (current_topo_has_flag(TF_WRAPY)
2809 && h > (NATURAL_HEIGHT - isodiff) * H / isofactor) {
2810 return FALSE;
2812 return TRUE;
2816 /**************************************************************************
2817 Called when we receive map dimensions. It initialized the mapview
2818 decorations.
2819 **************************************************************************/
2820 void mapdeco_init(void)
2822 /* HACK: this must be called on a map_info packet. */
2823 mapview.can_do_cached_drawing = can_do_cached_drawing();
2825 mapdeco_free();
2826 mapdeco_highlight_table = tile_hash_new();
2827 mapdeco_crosshair_table = tile_hash_new();
2828 mapdeco_gotoline_table = gotoline_hash_new();
2831 /**************************************************************************
2832 Free all memory used for map decorations.
2833 **************************************************************************/
2834 void mapdeco_free(void)
2836 if (mapdeco_highlight_table) {
2837 tile_hash_destroy(mapdeco_highlight_table);
2838 mapdeco_highlight_table = NULL;
2840 if (mapdeco_crosshair_table) {
2841 tile_hash_destroy(mapdeco_crosshair_table);
2842 mapdeco_crosshair_table = NULL;
2844 if (mapdeco_gotoline_table) {
2845 gotoline_hash_destroy(mapdeco_gotoline_table);
2846 mapdeco_gotoline_table = NULL;
2850 /**************************************************************************
2851 Set the given tile's map decoration as either highlighted or not,
2852 depending on the value of 'highlight'.
2853 **************************************************************************/
2854 void mapdeco_set_highlight(const struct tile *ptile, bool highlight)
2856 bool changed = FALSE;
2857 if (!ptile || !mapdeco_highlight_table) {
2858 return;
2861 if (highlight) {
2862 changed = tile_hash_insert(mapdeco_highlight_table, ptile, NULL);
2863 } else {
2864 changed = tile_hash_remove(mapdeco_highlight_table, ptile);
2867 if (changed) {
2868 /* FIXME: Remove the cast. */
2869 refresh_tile_mapcanvas((struct tile *) ptile, TRUE, FALSE);
2873 /**************************************************************************
2874 Return TRUE if the given tile is highlighted.
2875 **************************************************************************/
2876 bool mapdeco_is_highlight_set(const struct tile *ptile)
2878 if (!ptile || !mapdeco_highlight_table) {
2879 return FALSE;
2881 return tile_hash_lookup(mapdeco_highlight_table, ptile, NULL);
2884 /**************************************************************************
2885 Clears all highlighting. Marks the previously highlighted tiles as
2886 needing a mapview update.
2887 **************************************************************************/
2888 void mapdeco_clear_highlights(void)
2890 if (!mapdeco_highlight_table) {
2891 return;
2894 tile_hash_iterate(mapdeco_highlight_table, ptile) {
2895 refresh_tile_mapcanvas(ptile, TRUE, FALSE);
2896 } tile_hash_iterate_end;
2898 tile_hash_clear(mapdeco_highlight_table);
2901 /**************************************************************************
2902 Marks the given tile as having a "crosshair" map decoration.
2903 **************************************************************************/
2904 void mapdeco_set_crosshair(const struct tile *ptile, bool crosshair)
2906 bool changed;
2908 if (!mapdeco_crosshair_table || !ptile) {
2909 return;
2912 if (crosshair) {
2913 changed = tile_hash_insert(mapdeco_crosshair_table, ptile, NULL);
2914 } else {
2915 changed = tile_hash_remove(mapdeco_crosshair_table, ptile);
2918 if (changed) {
2919 /* FIXME: Remove the cast. */
2920 refresh_tile_mapcanvas((struct tile *) ptile, FALSE, FALSE);
2924 /**************************************************************************
2925 Returns TRUE if there is a "crosshair" decoration set at the given tile.
2926 **************************************************************************/
2927 bool mapdeco_is_crosshair_set(const struct tile *ptile)
2929 if (!mapdeco_crosshair_table || !ptile) {
2930 return FALSE;
2932 return tile_hash_lookup(mapdeco_crosshair_table, ptile, NULL);
2935 /**************************************************************************
2936 Clears all previous set tile crosshair decorations. Marks the affected
2937 tiles as needing a mapview update.
2938 **************************************************************************/
2939 void mapdeco_clear_crosshairs(void)
2941 if (!mapdeco_crosshair_table) {
2942 return;
2945 tile_hash_iterate(mapdeco_crosshair_table, ptile) {
2946 refresh_tile_mapcanvas(ptile, FALSE, FALSE);
2947 } tile_hash_iterate_end;
2949 tile_hash_clear(mapdeco_crosshair_table);
2952 /**************************************************************************
2953 Add a goto line from the given tile 'ptile' in the direction 'dir'. If
2954 there was no previously drawn line there, a mapview update is queued
2955 for the source and destination tiles.
2956 **************************************************************************/
2957 void mapdeco_add_gotoline(const struct tile *ptile, enum direction8 dir)
2959 struct gotoline_counter *pglc;
2960 const struct tile *ptile_dest;
2961 bool changed;
2963 if (!mapdeco_gotoline_table || !ptile
2964 || !(0 <= dir && dir <= direction8_max())) {
2965 return;
2967 ptile_dest = mapstep(ptile, dir);
2968 if (!ptile_dest) {
2969 return;
2972 if (!gotoline_hash_lookup(mapdeco_gotoline_table, ptile, &pglc)) {
2973 pglc = gotoline_counter_new();
2974 gotoline_hash_insert(mapdeco_gotoline_table, ptile, pglc);
2976 changed = (pglc->line_count[dir] < 1);
2977 pglc->line_count[dir]++;
2979 if (changed) {
2980 /* FIXME: Remove cast. */
2981 refresh_tile_mapcanvas((struct tile *) ptile, FALSE, FALSE);
2982 refresh_tile_mapcanvas((struct tile *) ptile_dest, FALSE, FALSE);
2986 /**************************************************************************
2987 Removes a goto line from the given tile 'ptile' going in the direction
2988 'dir'. If this was the last line there, a mapview update is queued to
2989 erase the drawn line.
2990 **************************************************************************/
2991 void mapdeco_remove_gotoline(const struct tile *ptile,
2992 enum direction8 dir)
2994 struct gotoline_counter *pglc;
2995 bool changed = FALSE;
2997 if (!mapdeco_gotoline_table || !ptile
2998 || !(0 <= dir && dir <= direction8_max())) {
2999 return;
3002 if (!gotoline_hash_lookup(mapdeco_gotoline_table, ptile, &pglc)) {
3003 return;
3006 pglc->line_count[dir]--;
3007 if (pglc->line_count[dir] <= 0) {
3008 pglc->line_count[dir] = 0;
3009 changed = TRUE;
3012 if (changed) {
3013 /* FIXME: Remove the casts. */
3014 refresh_tile_mapcanvas((struct tile *) ptile, FALSE, FALSE);
3015 ptile = mapstep(ptile, dir);
3016 if (ptile != NULL) {
3017 refresh_tile_mapcanvas((struct tile *) ptile, FALSE, FALSE);
3022 /**************************************************************************
3023 Set the map decorations for the given unit's goto route. A goto route
3024 consists of one or more goto lines, with each line being from the center
3025 of one tile to the center of another tile.
3026 **************************************************************************/
3027 void mapdeco_set_gotoroute(const struct unit *punit)
3029 const struct unit_order *porder;
3030 const struct tile *ptile;
3031 int i, ind;
3033 if (!punit || !unit_tile(punit) || !unit_has_orders(punit)
3034 || punit->orders.length < 1) {
3035 return;
3038 ptile = unit_tile(punit);
3040 for (i = 0; ptile != NULL && i < punit->orders.length; i++) {
3041 if (punit->orders.index + i >= punit->orders.length
3042 && !punit->orders.repeat) {
3043 break;
3046 ind = (punit->orders.index + i) % punit->orders.length;
3047 porder = &punit->orders.list[ind];
3048 if (porder->order != ORDER_MOVE) {
3049 /* FIXME: should display some indication of non-move orders here. */
3050 continue;
3053 mapdeco_add_gotoline(ptile, porder->dir);
3054 ptile = mapstep(ptile, porder->dir);
3058 /**************************************************************************
3059 Returns TRUE if a goto line should be drawn from the given tile in the
3060 given direction.
3061 **************************************************************************/
3062 bool mapdeco_is_gotoline_set(const struct tile *ptile,
3063 enum direction8 dir)
3065 struct gotoline_counter *pglc;
3067 if (!ptile || !(0 <= dir && dir <= direction8_max())
3068 || !mapdeco_gotoline_table) {
3069 return FALSE;
3072 if (!gotoline_hash_lookup(mapdeco_gotoline_table, ptile, &pglc)) {
3073 return FALSE;
3076 return pglc->line_count[dir] > 0;
3079 /**************************************************************************
3080 Clear all goto line map decorations and queues mapview updates for the
3081 affected tiles.
3082 **************************************************************************/
3083 void mapdeco_clear_gotoroutes(void)
3085 if (!mapdeco_gotoline_table) {
3086 return;
3089 gotoline_hash_iterate(mapdeco_gotoline_table, ptile, pglc) {
3090 refresh_tile_mapcanvas(ptile, FALSE, FALSE);
3091 adjc_dir_iterate(ptile, ptile_dest, dir) {
3092 if (pglc->line_count[dir] > 0) {
3093 refresh_tile_mapcanvas(ptile_dest, FALSE, FALSE);
3095 } adjc_dir_iterate_end;
3096 } gotoline_hash_iterate_end;
3097 gotoline_hash_clear(mapdeco_gotoline_table);
3100 /**************************************************************************
3101 Called if the map in the GUI is resized.
3103 Returns TRUE iff the canvas was redrawn.
3104 **************************************************************************/
3105 bool map_canvas_resized(int width, int height)
3107 int old_tile_width = mapview.tile_width;
3108 int old_tile_height = mapview.tile_height;
3109 int old_width = mapview.width, old_height = mapview.height;
3110 int tile_width = (width + tileset_tile_width(tileset) * map_zoom - 1) /
3111 (tileset_tile_width(tileset) * map_zoom);
3112 int tile_height = (height + tileset_tile_height(tileset) * map_zoom - 1) /
3113 (tileset_tile_height(tileset) * map_zoom);
3114 int full_width = tile_width * tileset_tile_width(tileset) * map_zoom;
3115 int full_height = tile_height * tileset_tile_height(tileset) * map_zoom;
3116 bool tile_size_changed, size_changed, redrawn = FALSE;
3118 /* Resized */
3120 /* Since a resize is only triggered when the tile_*** changes, the canvas
3121 * width and height must include the entire backing store - otherwise
3122 * small resizings may lead to undrawn tiles. */
3123 mapview.tile_width = tile_width;
3124 mapview.tile_height = tile_height;
3125 mapview.width = width;
3126 mapview.height = height;
3127 mapview.store_width = full_width;
3128 mapview.store_height = full_height;
3130 /* Check for what's changed. */
3131 tile_size_changed = (tile_width != old_tile_width
3132 || tile_height != old_tile_height);
3133 size_changed = (width != old_width || height != old_height);
3135 /* If the tile size has changed, resize the canvas. */
3136 if (tile_size_changed) {
3137 if (mapview.store) {
3138 canvas_free(mapview.store);
3139 canvas_free(mapview.tmp_store);
3141 mapview.store = canvas_create(full_width, full_height);
3142 canvas_set_zoom(mapview.store, map_zoom);
3143 canvas_put_rectangle(mapview.store,
3144 get_color(tileset, COLOR_MAPVIEW_UNKNOWN),
3145 0, 0, full_width, full_height);
3147 mapview.tmp_store = canvas_create(full_width, full_height);
3148 canvas_set_zoom(mapview.tmp_store, map_zoom);
3151 if (map_exists() && can_client_change_view()) {
3152 if (tile_size_changed) {
3153 if (center_tile != NULL) {
3154 int x_left, y_top;
3155 float gui_x, gui_y;
3157 index_to_map_pos(&x_left, &y_top, tile_index(center_tile));
3158 map_to_gui_pos(tileset, &gui_x, &gui_y, x_left, y_top);
3160 /* Put the center pixel of the tile at the exact center of the mapview. */
3161 gui_x -= (mapview.width - tileset_tile_width(tileset) * map_zoom) / 2;
3162 gui_y -= (mapview.height - tileset_tile_height(tileset) * map_zoom) / 2;
3164 calc_mapview_origin(&gui_x, &gui_y);
3165 mapview.gui_x0 = gui_x;
3166 mapview.gui_y0 = gui_y;
3168 update_map_canvas_visible();
3169 center_tile_overviewcanvas();
3170 unqueue_mapview_updates(TRUE);
3171 redrawn = TRUE;
3174 /* If the width/height has changed, update the scrollbars even if
3175 * the backing store is not resized. */
3176 if (size_changed) {
3177 update_map_canvas_scrollbars_size();
3178 update_map_canvas_scrollbars();
3182 mapview.can_do_cached_drawing = can_do_cached_drawing();
3184 return redrawn;
3187 /**************************************************************************
3188 Sets up data for the mapview and overview.
3189 **************************************************************************/
3190 void init_mapcanvas_and_overview(void)
3192 /* Create a dummy map to make sure mapview.store is never NULL. */
3193 map_canvas_resized(1, 1);
3196 /**************************************************************************
3197 Frees resources allocated for mapview and overview
3198 **************************************************************************/
3199 void free_mapcanvas_and_overview(void)
3201 canvas_free(mapview.store);
3202 canvas_free(mapview.tmp_store);
3205 /****************************************************************************
3206 Return the desired width of the spaceship canvas.
3207 ****************************************************************************/
3208 void get_spaceship_dimensions(int *width, int *height)
3210 struct sprite *sprite
3211 = get_spaceship_sprite(tileset, SPACESHIP_HABITATION);
3213 get_sprite_dimensions(sprite, width, height);
3214 *width *= 7;
3215 *height *= 7;
3218 /****************************************************************************
3219 Draw the spaceship onto the canvas.
3220 ****************************************************************************/
3221 void put_spaceship(struct canvas *pcanvas, int canvas_x, int canvas_y,
3222 const struct player *pplayer)
3224 int i, x, y;
3225 const struct player_spaceship *ship = &pplayer->spaceship;
3226 int w, h;
3227 struct sprite *spr;
3228 struct tileset *t = tileset;
3230 spr = get_spaceship_sprite(t, SPACESHIP_HABITATION);
3231 get_sprite_dimensions(spr, &w, &h);
3233 canvas_put_rectangle(pcanvas,
3234 get_color(tileset, COLOR_SPACESHIP_BACKGROUND),
3235 0, 0, w * 7, h * 7);
3237 for (i = 0; i < NUM_SS_MODULES; i++) {
3238 const int j = i / 3;
3239 const int k = i % 3;
3241 if ((k == 0 && j >= ship->habitation)
3242 || (k == 1 && j >= ship->life_support)
3243 || (k == 2 && j >= ship->solar_panels)) {
3244 continue;
3246 x = modules_info[i].x * w / 4 - w / 2;
3247 y = modules_info[i].y * h / 4 - h / 2;
3249 spr = (k == 0 ? get_spaceship_sprite(t, SPACESHIP_HABITATION)
3250 : k == 1 ? get_spaceship_sprite(t, SPACESHIP_LIFE_SUPPORT)
3251 : get_spaceship_sprite(t, SPACESHIP_SOLAR_PANEL));
3252 canvas_put_sprite_full(pcanvas, x, y, spr);
3255 for (i = 0; i < NUM_SS_COMPONENTS; i++) {
3256 const int j = i / 2;
3257 const int k = i % 2;
3259 if ((k == 0 && j >= ship->fuel)
3260 || (k == 1 && j >= ship->propulsion)) {
3261 continue;
3263 x = components_info[i].x * w / 4 - w / 2;
3264 y = components_info[i].y * h / 4 - h / 2;
3266 spr = ((k == 0) ? get_spaceship_sprite(t, SPACESHIP_FUEL)
3267 : get_spaceship_sprite(t, SPACESHIP_PROPULSION));
3269 canvas_put_sprite_full(pcanvas, x, y, spr);
3271 if (k && ship->state == SSHIP_LAUNCHED) {
3272 spr = get_spaceship_sprite(t, SPACESHIP_EXHAUST);
3273 canvas_put_sprite_full(pcanvas, x + w, y, spr);
3277 for (i = 0; i < NUM_SS_STRUCTURALS; i++) {
3278 if (!BV_ISSET(ship->structure, i)) {
3279 continue;
3281 x = structurals_info[i].x * w / 4 - w / 2;
3282 y = structurals_info[i].y * h / 4 - h / 2;
3284 spr = get_spaceship_sprite(t, SPACESHIP_STRUCTURAL);
3285 canvas_put_sprite_full(pcanvas, x, y, spr);
3289 /****************************************************************************
3290 Map link mark module: it makes link marks when a link is sent by chating,
3291 or restore a mark with clicking a link on the chatline.
3292 ****************************************************************************/
3293 struct link_mark {
3294 enum text_link_type type; /* The target type. */
3295 int id; /* The city or unit id, or tile index. */
3296 int turn_counter; /* The turn counter before it disappears. */
3299 #define SPECLIST_TAG link_mark
3300 #define SPECLIST_TYPE struct link_mark
3301 #include "speclist.h"
3302 #define link_marks_iterate(pmark) \
3303 TYPED_LIST_ITERATE(struct link_mark, link_marks, pmark)
3304 #define link_marks_iterate_end LIST_ITERATE_END
3306 static struct link_mark_list *link_marks = NULL;
3308 /**********************************************************************
3309 Find a link mark in the list.
3310 ***********************************************************************/
3311 static struct link_mark *link_mark_find(enum text_link_type type, int id)
3313 link_marks_iterate(pmark) {
3314 if (pmark->type == type && pmark->id == id) {
3315 return pmark;
3317 } link_marks_iterate_end;
3319 return NULL;
3322 /**********************************************************************
3323 Create a new link mark.
3324 ***********************************************************************/
3325 static struct link_mark *link_mark_new(enum text_link_type type,
3326 int id, int turns)
3328 struct link_mark *pmark = fc_malloc(sizeof(struct link_mark));
3330 pmark->type = type;
3331 pmark->id = id;
3332 pmark->turn_counter = turns;
3334 return pmark;
3337 /**********************************************************************
3338 Remove a link mark.
3339 ***********************************************************************/
3340 static void link_mark_destroy(struct link_mark *pmark)
3342 free(pmark);
3345 /**********************************************************************
3346 Returns the location of the pointed mark.
3347 ***********************************************************************/
3348 static struct tile *link_mark_tile(const struct link_mark *pmark)
3350 switch (pmark->type) {
3351 case TLT_CITY:
3353 struct city *pcity = game_city_by_number(pmark->id);
3354 return pcity ? pcity->tile : NULL;
3356 case TLT_TILE:
3357 return index_to_tile(pmark->id);
3358 case TLT_UNIT:
3360 struct unit *punit = game_unit_by_number(pmark->id);
3361 return punit ? unit_tile(punit) : NULL;
3364 return NULL;
3367 /**********************************************************************
3368 Returns the color of the pointed mark.
3369 ***********************************************************************/
3370 static struct color *link_mark_color(const struct link_mark *pmark)
3372 switch (pmark->type) {
3373 case TLT_CITY:
3374 return get_color(tileset, COLOR_MAPVIEW_CITY_LINK);
3375 case TLT_TILE:
3376 return get_color(tileset, COLOR_MAPVIEW_TILE_LINK);
3377 case TLT_UNIT:
3378 return get_color(tileset, COLOR_MAPVIEW_UNIT_LINK);
3380 return NULL;
3383 /**********************************************************************
3384 Print a link mark.
3385 ***********************************************************************/
3386 static void link_mark_draw(const struct link_mark *pmark)
3388 int width = tileset_tile_width(tileset) * map_zoom;
3389 int height = tileset_tile_height(tileset) * map_zoom;
3390 int xd = width / 20, yd = height / 20;
3391 int xlen = width / 3, ylen = height / 3;
3392 float canvas_x, canvas_y;
3393 int x_left, x_right, y_top, y_bottom;
3394 struct tile *ptile = link_mark_tile(pmark);
3395 struct color *pcolor = link_mark_color(pmark);
3397 if (!ptile || !tile_to_canvas_pos(&canvas_x, &canvas_y, ptile)) {
3398 return;
3401 x_left = canvas_x + xd;
3402 x_right = canvas_x + width - xd;
3403 y_top = canvas_y + yd;
3404 y_bottom = canvas_y + height - yd;
3406 canvas_put_line(mapview.store, pcolor, LINE_TILE_FRAME, x_left, y_top, xlen, 0);
3407 canvas_put_line(mapview.store, pcolor, LINE_TILE_FRAME, x_left, y_top, 0, ylen);
3409 canvas_put_line(mapview.store, pcolor, LINE_TILE_FRAME, x_right, y_top, -xlen, 0);
3410 canvas_put_line(mapview.store, pcolor, LINE_TILE_FRAME, x_right, y_top, 0, ylen);
3412 canvas_put_line(mapview.store, pcolor, LINE_TILE_FRAME, x_left, y_bottom, xlen, 0);
3413 canvas_put_line(mapview.store, pcolor, LINE_TILE_FRAME, x_left, y_bottom, 0, -ylen);
3415 canvas_put_line(mapview.store, pcolor, LINE_TILE_FRAME, x_right, y_bottom, -xlen, 0);
3416 canvas_put_line(mapview.store, pcolor, LINE_TILE_FRAME, x_right, y_bottom, 0, -ylen);
3419 /**********************************************************************
3420 Initialize the link marks.
3421 ***********************************************************************/
3422 void link_marks_init(void)
3424 if (link_marks) {
3425 link_marks_free();
3428 link_marks = link_mark_list_new_full(link_mark_destroy);
3431 /**********************************************************************
3432 Free the link marks.
3433 ***********************************************************************/
3434 void link_marks_free(void)
3436 if (!link_marks) {
3437 return;
3440 link_mark_list_destroy(link_marks);
3441 link_marks = NULL;
3444 /**********************************************************************
3445 Draw all link marks.
3446 ***********************************************************************/
3447 void link_marks_draw_all(void)
3449 link_marks_iterate(pmark) {
3450 link_mark_draw(pmark);
3451 } link_marks_iterate_end;
3454 /**********************************************************************
3455 Clear all visible links.
3456 ***********************************************************************/
3457 void link_marks_clear_all(void)
3459 link_mark_list_clear(link_marks);
3460 update_map_canvas_visible();
3463 /**********************************************************************
3464 Clear all visible links.
3465 ***********************************************************************/
3466 void link_marks_decrease_turn_counters(void)
3468 link_marks_iterate(pmark) {
3469 if (--pmark->turn_counter <= 0) {
3470 link_mark_list_remove(link_marks, pmark);
3472 } link_marks_iterate_end;
3474 /* update_map_canvas_visible(); not needed here. */
3477 /**********************************************************************
3478 Add a visible link for 2 turns.
3479 ***********************************************************************/
3480 void link_mark_add_new(enum text_link_type type, int id)
3482 struct link_mark *pmark = link_mark_find(type, id);
3483 struct tile *ptile;
3485 if (pmark) {
3486 /* Already displayed, but maybe increase the turn counter. */
3487 pmark->turn_counter = MAX(pmark->turn_counter, 2);
3488 return;
3491 pmark = link_mark_new(type, id, 2);
3492 link_mark_list_append(link_marks, pmark);
3493 ptile = link_mark_tile(pmark);
3494 if (ptile && tile_visible_mapcanvas(ptile)) {
3495 refresh_tile_mapcanvas(ptile, FALSE, FALSE);
3499 /**********************************************************************
3500 Add a visible link for 1 turn.
3501 ***********************************************************************/
3502 void link_mark_restore(enum text_link_type type, int id)
3504 struct link_mark *pmark;
3505 struct tile *ptile;
3507 if (link_mark_find(type, id)) {
3508 return;
3511 pmark = link_mark_new(type, id, 1);
3512 link_mark_list_append(link_marks, pmark);
3513 ptile = link_mark_tile(pmark);
3514 if (ptile && tile_visible_mapcanvas(ptile)) {
3515 refresh_tile_mapcanvas(ptile, FALSE, FALSE);
3519 /**********************************************************************
3520 Are the topology and tileset compatible?
3521 ***********************************************************************/
3522 bool tileset_map_topo_compatible(int topology_id, struct tileset *tset)
3524 int tileset_topology;
3526 if (tileset_hex_width(tset) > 0) {
3527 fc_assert(tileset_is_isometric(tset));
3528 tileset_topology = TF_HEX | TF_ISO;
3529 } else if (tileset_hex_height(tset) > 0) {
3530 fc_assert(tileset_is_isometric(tset));
3531 tileset_topology = TF_HEX;
3532 } else if (tileset_is_isometric(tset)) {
3533 tileset_topology = TF_ISO;
3534 } else {
3535 tileset_topology = 0;
3538 return ((topology_id & (TF_HEX | TF_ISO)) == tileset_topology);