Stop sharing requirement_unit_state_ereq().
[freeciv.git] / client / mapview_common.c
blobcf90a3fa34b2723dccd1430afab942478f49fd63
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, wld.map.xsize);
444 if (current_topo_has_flag(TF_WRAPY)) {
445 nat_y = FC_WRAP(nat_y, wld.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 create_line_at_mouse_pos();
589 case HOVER_NONE:
590 case HOVER_PARADROP:
591 case HOVER_ACT_SEL_TGT:
592 break;
594 if (rectangle_active) {
595 update_rect_at_mouse_pos();
599 /****************************************************************************
600 Adjust mapview origin values. Returns TRUE iff values are different from
601 current mapview.
602 ****************************************************************************/
603 static bool calc_mapview_origin(float *gui_x0, float *gui_y0)
605 float xmin, ymin, xmax, ymax;
606 int xsize, ysize;
608 /* Normalize (wrap) the mapview origin. */
609 normalize_gui_pos(tileset, gui_x0, gui_y0);
611 /* First wrap/clip the position. Wrapping is done in native positions
612 * while clipping is done in scroll (native) positions. */
613 get_mapview_scroll_window(&xmin, &ymin, &xmax, &ymax, &xsize, &ysize);
615 if (!current_topo_has_flag(TF_WRAPX)) {
616 *gui_x0 = CLIP(xmin, *gui_x0, xmax - xsize);
619 if (!current_topo_has_flag(TF_WRAPY)) {
620 *gui_y0 = CLIP(ymin, *gui_y0, ymax - ysize);
623 if (mapview.gui_x0 == *gui_x0 && mapview.gui_y0 == *gui_y0) {
624 return FALSE;
627 return TRUE;
630 /****************************************************************************
631 Change the mapview origin, clip it, and update everything.
632 ****************************************************************************/
633 void set_mapview_origin(float gui_x0, float gui_y0)
635 if (!calc_mapview_origin(&gui_x0, &gui_y0)) {
636 return;
639 if (can_slide && gui_options.smooth_center_slide_msec > 0) {
640 int start_x = mapview.gui_x0, start_y = mapview.gui_y0;
641 float diff_x, diff_y;
642 double timing_sec = (double)gui_options.smooth_center_slide_msec / 1000.0;
643 double currtime;
644 static struct timer *anim_timer;
645 int frames = 0;
647 /* We track the average FPS, which is used to predict how long the
648 * next draw will take. We start with a 100 FPS estimate - this
649 * value will quickly become irrelevant as the correct value is
650 * calculated, but it's needed to give an estimate of the FPS for
651 * the first draw.
653 * Note that the initial value shouldn't be larger than the sliding
654 * time, or we'll jump straight to the last frame. The FPS should
655 * therefore be a "high" estimate. */
656 static double total_frames = 0.01;
657 static double total_time = 0.0001;
659 gui_distance_vector(tileset,
660 &diff_x, &diff_y, start_x, start_y, gui_x0, gui_y0);
661 anim_timer = timer_renew(anim_timer, TIMER_USER, TIMER_ACTIVE);
662 timer_start(anim_timer);
664 unqueue_mapview_updates(TRUE);
666 do {
667 double mytime;
669 /* Get the current time, and add on the average 1/FPS, which is the
670 * expected time this frame will take. This is done so that the
671 * frame's position is calculated from the expected time when the
672 * frame will complete, rather than the time when the frame drawing
673 * is started. */
674 currtime = timer_read_seconds(anim_timer);
675 currtime += total_time / total_frames;
677 mytime = MIN(currtime, timing_sec);
678 base_set_mapview_origin(start_x + diff_x * (mytime / timing_sec),
679 start_y + diff_y * (mytime / timing_sec));
680 flush_dirty();
681 gui_flush();
682 frames++;
683 } while (currtime < timing_sec);
685 currtime = timer_read_seconds(anim_timer);
686 total_frames += frames;
687 total_time += currtime;
688 log_debug("Got %d frames in %f seconds: %f FPS (avg %f).",
689 frames, currtime, (double)frames / currtime,
690 total_frames / total_time);
692 /* A very small decay factor to make things more accurate when something
693 * changes (mapview size, tileset change, etc.). This gives a
694 * half-life of 68 slides. */
695 total_frames *= 0.99;
696 total_time *= 0.99;
697 } else {
698 base_set_mapview_origin(gui_x0, gui_y0);
701 update_map_canvas_scrollbars();
704 /****************************************************************************
705 Return the scroll dimensions of the clipping window for the mapview window..
707 Imagine the entire map in scroll coordinates. It is a rectangle. Now
708 imagine the mapview "window" sliding around through this rectangle. How
709 far can it slide? In most cases it has to be able to slide past the
710 ends of the map rectangle so that it's capable of reaching the whole
711 area.
713 This function gives constraints on how far the window is allowed to
714 slide. xmin and ymin are the minimum values for the window origin.
715 xsize and ysize give the scroll dimensions of the mapview window.
716 xmax and ymax give the maximum values that the bottom/left ends of the
717 window may reach. The constraints, therefore, are that:
719 get_mapview_scroll_pos(&scroll_x, &scroll_y);
720 xmin <= scroll_x < xmax - xsize
721 ymin <= scroll_y < ymax - ysize
723 This function should be used anywhere and everywhere that scrolling is
724 constrained.
726 Note that scroll coordinates, not map coordinates, are used. Currently
727 these correspond to native coordinates.
728 ****************************************************************************/
729 void get_mapview_scroll_window(float *xmin, float *ymin,
730 float *xmax, float *ymax,
731 int *xsize, int *ysize)
733 int diff;
735 *xsize = mapview.width;
736 *ysize = mapview.height;
738 if (MAP_IS_ISOMETRIC == tileset_is_isometric(tileset)) {
739 /* If the map and view line up, it's easy. */
740 NATIVE_TO_MAP_POS(xmin, ymin, 0, 0);
741 map_to_gui_pos(tileset, xmin, ymin, *xmin, *ymin);
743 NATIVE_TO_MAP_POS(xmax, ymax, wld.map.xsize - 1, wld.map.ysize - 1);
744 map_to_gui_pos(tileset, xmax, ymax, *xmax, *ymax);
745 *xmax += tileset_tile_width(tileset) * map_zoom;
746 *ymax += tileset_tile_height(tileset) * map_zoom;
748 /* To be able to center on positions near the edges, we have to be
749 * allowed to scroll all the way to those edges. To allow wrapping the
750 * clipping boundary needs to extend past the edge - a half-tile in
751 * iso-view or a full tile in non-iso view. The above math already has
752 * taken care of some of this so all that's left is to fix the corner
753 * cases. */
754 if (current_topo_has_flag(TF_WRAPX)) {
755 *xmax += *xsize;
757 /* We need to be able to scroll a little further to the left. */
758 *xmin -= tileset_tile_width(tileset) * map_zoom;
760 if (current_topo_has_flag(TF_WRAPY)) {
761 *ymax += *ysize;
763 /* We need to be able to scroll a little further up. */
764 *ymin -= tileset_tile_height(tileset) * map_zoom;
766 } else {
767 /* Otherwise it's hard. Very hard. Impossible, in fact. This is just
768 * an approximation - a huge bounding box. */
769 float gui_x1, gui_y1, gui_x2, gui_y2, gui_x3, gui_y3, gui_x4, gui_y4;
770 int map_x, map_y;
772 NATIVE_TO_MAP_POS(&map_x, &map_y, 0, 0);
773 map_to_gui_pos(tileset, &gui_x1, &gui_y1, map_x, map_y);
775 NATIVE_TO_MAP_POS(&map_x, &map_y, wld.map.xsize - 1, 0);
776 map_to_gui_pos(tileset, &gui_x2, &gui_y2, map_x, map_y);
778 NATIVE_TO_MAP_POS(&map_x, &map_y, 0, wld.map.ysize - 1);
779 map_to_gui_pos(tileset, &gui_x3, &gui_y3, map_x, map_y);
781 NATIVE_TO_MAP_POS(&map_x, &map_y, wld.map.xsize - 1, wld.map.ysize - 1);
782 map_to_gui_pos(tileset, &gui_x4, &gui_y4, map_x, map_y);
784 *xmin = MIN(gui_x1, MIN(gui_x2, gui_x3)) - mapview.width / 2;
785 *ymin = MIN(gui_y1, MIN(gui_y2, gui_y3)) - mapview.height / 2;
787 *xmax = MAX(gui_x4, MAX(gui_x2, gui_x3)) + mapview.width / 2;
788 *ymax = MAX(gui_y4, MAX(gui_y2, gui_y3)) + mapview.height / 2;
791 /* Make sure the scroll window is big enough to hold the mapview. If
792 * not scrolling will be very ugly and the GUI may become confused. */
793 diff = *xsize - (*xmax - *xmin);
794 if (diff > 0) {
795 *xmin -= diff / 2;
796 *xmax += (diff + 1) / 2;
799 diff = *ysize - (*ymax - *ymin);
800 if (diff > 0) {
801 *ymin -= diff / 2;
802 *ymax += (diff + 1) / 2;
805 log_debug("x: %f<-%d->%f; y: %f<-%f->%d",
806 *xmin, *xsize, *xmax, *ymin, *ymax, *ysize);
809 /****************************************************************************
810 Find the scroll step for the mapview. This is the amount to scroll (in
811 scroll coordinates) on each "step". See also get_mapview_scroll_window.
812 ****************************************************************************/
813 void get_mapview_scroll_step(int *xstep, int *ystep)
815 *xstep = tileset_tile_width(tileset) * map_zoom;
816 *ystep = tileset_tile_height(tileset) * map_zoom;
818 if (tileset_is_isometric(tileset)) {
819 *xstep /= 2;
820 *ystep /= 2;
824 /****************************************************************************
825 Find the current scroll position (origin) of the mapview.
826 ****************************************************************************/
827 void get_mapview_scroll_pos(int *scroll_x, int *scroll_y)
829 *scroll_x = mapview.gui_x0;
830 *scroll_y = mapview.gui_y0;
833 /****************************************************************************
834 Set the scroll position (origin) of the mapview, and update the GUI.
835 ****************************************************************************/
836 void set_mapview_scroll_pos(int scroll_x, int scroll_y)
838 int gui_x0 = scroll_x, gui_y0 = scroll_y;
840 can_slide = FALSE;
841 set_mapview_origin(gui_x0, gui_y0);
842 can_slide = TRUE;
845 /**************************************************************************
846 Finds the current center tile of the mapcanvas.
847 **************************************************************************/
848 struct tile *get_center_tile_mapcanvas(void)
850 return canvas_pos_to_nearest_tile(mapview.width / 2,
851 mapview.height / 2);
854 /**************************************************************************
855 Centers the mapview around (map_x, map_y).
856 **************************************************************************/
857 void center_tile_mapcanvas(struct tile *ptile)
859 float gui_x, gui_y;
860 int tile_x, tile_y;
861 static bool first = TRUE;
863 if (first && can_slide) {
864 return;
866 first = FALSE;
868 index_to_map_pos(&tile_x, &tile_y, tile_index(ptile));
869 map_to_gui_pos(tileset, &gui_x, &gui_y, tile_x, tile_y);
871 /* Put the center pixel of the tile at the exact center of the mapview. */
872 gui_x -= (mapview.width - tileset_tile_width(tileset) * map_zoom) / 2;
873 gui_y -= (mapview.height - tileset_tile_height(tileset) * map_zoom) / 2;
875 set_mapview_origin(gui_x, gui_y);
877 center_tile = ptile;
880 /**************************************************************************
881 Return TRUE iff the given map position has a tile visible on the
882 map canvas.
883 **************************************************************************/
884 bool tile_visible_mapcanvas(struct tile *ptile)
886 float dummy_x, dummy_y; /* well, it needs two pointers... */
888 return tile_to_canvas_pos(&dummy_x, &dummy_y, ptile);
891 /**************************************************************************
892 Return TRUE iff the given map position has a tile visible within the
893 interior of the map canvas. This information is used to determine
894 when we need to recenter the map canvas.
896 The logic of this function is simple: if a tile is within 1.5 tiles
897 of a border of the canvas and that border is not aligned with the
898 edge of the map, then the tile is on the "border" of the map canvas.
900 This function is only correct for the current topology.
901 **************************************************************************/
902 bool tile_visible_and_not_on_border_mapcanvas(struct tile *ptile)
904 float canvas_x, canvas_y;
905 float xmin, ymin, xmax, ymax;
906 int xsize, ysize, scroll_x, scroll_y;
907 const int border_x = (tileset_is_isometric(tileset) ? tileset_tile_width(tileset) / 2 * map_zoom
908 : 2 * tileset_tile_width(tileset) * map_zoom);
909 const int border_y = (tileset_is_isometric(tileset) ? tileset_tile_height(tileset) / 2 * map_zoom
910 : 2 * tileset_tile_height(tileset) * map_zoom);
911 bool same = (tileset_is_isometric(tileset) == MAP_IS_ISOMETRIC);
913 get_mapview_scroll_window(&xmin, &ymin, &xmax, &ymax, &xsize, &ysize);
914 get_mapview_scroll_pos(&scroll_x, &scroll_y);
916 if (!tile_to_canvas_pos(&canvas_x, &canvas_y, ptile)) {
917 /* The tile isn't visible at all. */
918 return FALSE;
921 /* For each direction: if the tile is too close to the mapview border
922 * in that direction, and scrolling can get us any closer to the
923 * border, then it's a border tile. We can only really check the
924 * scrolling when the mapview window lines up with the map. */
925 if (canvas_x < border_x
926 && (!same || scroll_x > xmin || current_topo_has_flag(TF_WRAPX))) {
927 return FALSE;
929 if (canvas_y < border_y
930 && (!same || scroll_y > ymin || current_topo_has_flag(TF_WRAPY))) {
931 return FALSE;
933 if (canvas_x + tileset_tile_width(tileset) * map_zoom > mapview.width - border_x
934 && (!same || scroll_x + xsize < xmax || current_topo_has_flag(TF_WRAPX))) {
935 return FALSE;
937 if (canvas_y + tileset_tile_height(tileset) * map_zoom > mapview.height - border_y
938 && (!same || scroll_y + ysize < ymax || current_topo_has_flag(TF_WRAPY))) {
939 return FALSE;
942 return TRUE;
945 /**************************************************************************
946 Draw an array of drawn sprites onto the canvas.
947 **************************************************************************/
948 void put_drawn_sprites(struct canvas *pcanvas, float zoom,
949 int canvas_x, int canvas_y,
950 int count, struct drawn_sprite *pdrawn,
951 bool fog)
953 int i;
955 for (i = 0; i < count; i++) {
956 if (!pdrawn[i].sprite) {
957 /* This can happen, although it should probably be avoided. */
958 continue;
961 if (fog && pdrawn[i].foggable) {
962 canvas_put_sprite_fogged(pcanvas,
963 canvas_x / zoom + pdrawn[i].offset_x,
964 canvas_y / zoom + pdrawn[i].offset_y,
965 pdrawn[i].sprite,
966 TRUE,
967 canvas_x, canvas_y);
968 } else {
969 /* We avoid calling canvas_put_sprite_fogged, even though it
970 * should be a valid thing to do, because gui-gtk-2.0 doesn't have
971 * a full implementation. */
972 canvas_put_sprite_full(pcanvas,
973 canvas_x / zoom + pdrawn[i].offset_x,
974 canvas_y / zoom + pdrawn[i].offset_y,
975 pdrawn[i].sprite);
980 /**************************************************************************
981 Draw one layer of a tile, edge, corner, unit, and/or city onto the
982 canvas at the given position.
983 **************************************************************************/
984 void put_one_element(struct canvas *pcanvas, float zoom,
985 enum mapview_layer layer,
986 const struct tile *ptile,
987 const struct tile_edge *pedge,
988 const struct tile_corner *pcorner,
989 const struct unit *punit, const struct city *pcity,
990 int canvas_x, int canvas_y,
991 const struct city *citymode,
992 const struct unit_type *putype)
994 struct drawn_sprite tile_sprs[80];
995 int count = fill_sprite_array(tileset, tile_sprs, layer,
996 ptile, pedge, pcorner,
997 punit, pcity, citymode, putype);
998 bool fog = (ptile && gui_options.draw_fog_of_war
999 && TILE_KNOWN_UNSEEN == client_tile_get_known(ptile));
1001 /*** Draw terrain and specials ***/
1002 put_drawn_sprites(pcanvas, zoom, canvas_x, canvas_y, count, tile_sprs, fog);
1005 /*****************************************************************************
1006 Draw the given unit onto the canvas store at the given location. The area
1007 of drawing is tileset_unit_height(tileset) x tileset_unit_width(tileset).
1008 *****************************************************************************/
1009 void put_unit(const struct unit *punit, struct canvas *pcanvas, float zoom,
1010 int canvas_x, int canvas_y)
1012 canvas_y += (tileset_unit_height(tileset) - tileset_tile_height(tileset)) * zoom;
1013 mapview_layer_iterate(layer) {
1014 put_one_element(pcanvas, zoom, layer, NULL, NULL, NULL,
1015 punit, NULL, canvas_x, canvas_y, NULL, NULL);
1016 } mapview_layer_iterate_end;
1019 /*****************************************************************************
1020 Draw the given unit onto the canvas store at the given location. The area
1021 of drawing is tileset_unit_height(tileset) x tileset_unit_width(tileset).
1022 *****************************************************************************/
1023 void put_unittype(const struct unit_type *putype, struct canvas *pcanvas, float zoom,
1024 int canvas_x, int canvas_y)
1026 canvas_y += (tileset_unit_height(tileset) - tileset_tile_height(tileset)) * zoom;
1027 mapview_layer_iterate(layer) {
1028 put_one_element(pcanvas, zoom, layer, NULL, NULL, NULL,
1029 NULL, NULL, canvas_x, canvas_y, NULL, putype);
1030 } mapview_layer_iterate_end;
1033 /**************************************************************************
1034 Draw the given city onto the canvas store at the given location. The
1035 area of drawing is
1036 tileset_full_tile_height(tileset) x tileset_full_tile_width(tileset).
1037 **************************************************************************/
1038 void put_city(struct city *pcity, struct canvas *pcanvas, float zoom,
1039 int canvas_x, int canvas_y)
1041 canvas_y += (tileset_full_tile_height(tileset) - tileset_tile_height(tileset)) * zoom;
1042 mapview_layer_iterate(layer) {
1043 put_one_element(pcanvas, zoom, layer,
1044 NULL, NULL, NULL, NULL, pcity,
1045 canvas_x, canvas_y, NULL, NULL);
1046 } mapview_layer_iterate_end;
1049 /**************************************************************************
1050 Draw the given tile terrain onto the canvas store at the given location.
1051 The area of drawing is
1052 tileset_full_tile_height(tileset) x tileset_full_tile_width(tileset)
1053 (even though most tiles are not this tall).
1054 **************************************************************************/
1055 void put_terrain(struct tile *ptile, struct canvas *pcanvas, float zoom,
1056 int canvas_x, int canvas_y)
1058 /* Use full tile height, even for terrains. */
1059 canvas_y += (tileset_full_tile_height(tileset) - tileset_tile_height(tileset)) * zoom;
1060 mapview_layer_iterate(layer) {
1061 put_one_element(pcanvas, zoom, layer, ptile, NULL, NULL, NULL, NULL,
1062 canvas_x, canvas_y, NULL, NULL);
1063 } mapview_layer_iterate_end;
1066 /****************************************************************************
1067 Draw food, gold, and shield upkeep values on the unit.
1069 The proper way to do this is probably something like what Civ II does
1070 (one sprite drawn N times on top of itself), but we just use separate
1071 sprites (limiting the number of combinations).
1072 ****************************************************************************/
1073 void put_unit_city_overlays(struct unit *punit,
1074 struct canvas *pcanvas,
1075 int canvas_x, int canvas_y, int *upkeep_cost,
1076 int happy_cost)
1078 struct sprite *sprite;
1080 sprite = get_unit_unhappy_sprite(tileset, punit, happy_cost);
1081 if (sprite) {
1082 canvas_put_sprite_full(pcanvas, canvas_x, canvas_y, sprite);
1085 output_type_iterate(o) {
1086 sprite = get_unit_upkeep_sprite(tileset, o, punit, upkeep_cost);
1087 if (sprite) {
1088 canvas_put_sprite_full(pcanvas, canvas_x, canvas_y, sprite);
1090 } output_type_iterate_end;
1094 * pcity->client.color_index is an index into the city_colors array.
1095 * When toggle_city_color is called the city's coloration is toggled. When
1096 * a city is newly colored its color is taken from color_index and
1097 * color_index is moved forward one position. Each color in the array
1098 * tells what color the citymap will be drawn on the mapview.
1100 * This array can be added to without breaking anything elsewhere.
1102 static int color_index = 0;
1103 #define NUM_CITY_COLORS tileset_num_city_colors(tileset)
1106 /****************************************************************************
1107 Toggle the city color. This cycles through the possible colors for the
1108 citymap as shown on the mapview. These colors are listed in the
1109 city_colors array; above.
1110 ****************************************************************************/
1111 void toggle_city_color(struct city *pcity)
1113 if (pcity->client.colored) {
1114 pcity->client.colored = FALSE;
1115 } else {
1116 pcity->client.colored = TRUE;
1117 pcity->client.color_index = color_index;
1118 color_index = (color_index + 1) % NUM_CITY_COLORS;
1121 refresh_city_mapcanvas(pcity, pcity->tile, TRUE, FALSE);
1124 /****************************************************************************
1125 Toggle the unit color. This cycles through the possible colors for the
1126 citymap as shown on the mapview. These colors are listed in the
1127 city_colors array; above.
1128 ****************************************************************************/
1129 void toggle_unit_color(struct unit *punit)
1131 if (punit->client.colored) {
1132 punit->client.colored = FALSE;
1133 } else {
1134 punit->client.colored = TRUE;
1135 punit->client.color_index = color_index;
1136 color_index = (color_index + 1) % NUM_CITY_COLORS;
1139 refresh_unit_mapcanvas(punit, unit_tile(punit), TRUE, FALSE);
1142 /****************************************************************************
1143 Animate the nuke explosion at map(x, y).
1144 ****************************************************************************/
1145 void put_nuke_mushroom_pixmaps(struct tile *ptile)
1147 float canvas_x, canvas_y;
1148 struct sprite *mysprite = get_nuke_explode_sprite(tileset);
1149 int width, height;
1151 /* We can't count on the return value of tile_to_canvas_pos since the
1152 * sprite may span multiple tiles. */
1153 (void) tile_to_canvas_pos(&canvas_x, &canvas_y, ptile);
1154 get_sprite_dimensions(mysprite, &width, &height);
1156 canvas_x += (tileset_tile_width(tileset) - width) / 2 * map_zoom;
1157 canvas_y += (tileset_tile_height(tileset) - height) / 2 * map_zoom;
1159 /* Make sure everything is flushed and synced before proceeding. First
1160 * we update everything to the store, but don't write this to screen.
1161 * Then add the nuke graphic to the store. Finally flush everything to
1162 * the screen and wait 1 second. */
1163 unqueue_mapview_updates(FALSE);
1165 canvas_put_sprite_full(mapview.store, canvas_x, canvas_y, mysprite);
1166 dirty_rect(canvas_x, canvas_y, width, height);
1168 flush_dirty();
1169 gui_flush();
1171 fc_usleep(1000000);
1173 update_map_canvas_visible();
1176 /**************************************************************************
1177 Draw some or all of a tile onto the canvas.
1178 **************************************************************************/
1179 static void put_one_tile(struct canvas *pcanvas, enum mapview_layer layer,
1180 struct tile *ptile, int canvas_x, int canvas_y,
1181 const struct city *citymode)
1183 if (client_tile_get_known(ptile) != TILE_UNKNOWN
1184 || (editor_is_active() && editor_tile_is_selected(ptile))) {
1185 put_one_element(pcanvas, map_zoom, layer, ptile, NULL, NULL,
1186 get_drawable_unit(tileset, ptile, citymode),
1187 tile_city(ptile), canvas_x, canvas_y, citymode, NULL);
1191 /**************************************************************************
1192 Depending on where ptile1 and ptile2 are on the map canvas, a trade route
1193 line may need to be drawn as two disjointed line segments. This function
1194 fills the given line array 'lines' with the necessary line segments.
1196 The return value is the number of line segments that need to be drawn.
1198 NB: It is assumed ptile1 and ptile2 are already consistently ordered.
1199 NB: 'lines' must be able to hold least MAX_TRADE_ROUTE_DRAW_LINES
1200 elements.
1201 **************************************************************************/
1202 static int trade_route_to_canvas_lines(const struct tile *ptile1,
1203 const struct tile *ptile2,
1204 struct trade_route_line *lines)
1206 int dx, dy;
1208 if (!ptile1 || !ptile2 || !lines) {
1209 return 0;
1212 base_map_distance_vector(&dx, &dy, TILE_XY(ptile1), TILE_XY(ptile2));
1213 map_to_gui_pos(tileset, &lines[0].width, &lines[0].height, dx, dy);
1215 /* FIXME: Remove these casts. */
1216 tile_to_canvas_pos(&lines[0].x, &lines[0].y, (struct tile *)ptile1);
1217 tile_to_canvas_pos(&lines[1].x, &lines[1].y, (struct tile *)ptile2);
1219 if (lines[1].x - lines[0].x == lines[0].width
1220 && lines[1].y - lines[0].y == lines[0].height) {
1221 return 1;
1224 lines[1].width = -lines[0].width;
1225 lines[1].height = -lines[0].height;
1226 return 2;
1229 /**************************************************************************
1230 Draw a colored trade route line from one tile to another.
1231 **************************************************************************/
1232 static void draw_trade_route_line(const struct tile *ptile1,
1233 const struct tile *ptile2,
1234 enum color_std color)
1236 struct trade_route_line lines[MAX_TRADE_ROUTE_DRAW_LINES];
1237 int line_count, i;
1238 struct color *pcolor;
1240 if (!ptile1 || !ptile2) {
1241 return;
1244 pcolor = get_color(tileset, color);
1245 if (!pcolor) {
1246 return;
1249 /* Order the source and destination tiles consistently
1250 * so that if a line is drawn twice it does not produce
1251 * ugly effects due to dashes not lining up. */
1252 if (tile_index(ptile2) > tile_index(ptile1)) {
1253 const struct tile *tmp;
1254 tmp = ptile1;
1255 ptile1 = ptile2;
1256 ptile2 = tmp;
1259 line_count = trade_route_to_canvas_lines(ptile1, ptile2, lines);
1260 for (i = 0; i < line_count; i++) {
1261 canvas_put_line(mapview.store, pcolor, LINE_BORDER,
1262 lines[i].x + tileset_tile_width(tileset) / 2 * map_zoom,
1263 lines[i].y + tileset_tile_height(tileset) / 2 * map_zoom,
1264 lines[i].width, lines[i].height);
1268 /**************************************************************************
1269 Draw all trade routes for the given city.
1270 **************************************************************************/
1271 static void draw_trade_routes_for_city(const struct city *pcity_src)
1273 if (!pcity_src) {
1274 return;
1277 trade_partners_iterate(pcity_src, pcity_dest) {
1278 draw_trade_route_line(city_tile(pcity_src), city_tile(pcity_dest),
1279 COLOR_MAPVIEW_TRADE_ROUTE_LINE);
1280 } trade_partners_iterate_end;
1283 /**************************************************************************
1284 Draw trade routes between cities as lines on the main map canvas.
1285 **************************************************************************/
1286 static void draw_trade_routes(void)
1288 if (!gui_options.draw_city_trade_routes) {
1289 return;
1292 if (client_is_global_observer()) {
1293 cities_iterate(pcity) {
1294 draw_trade_routes_for_city(pcity);
1295 } cities_iterate_end;
1296 } else {
1297 struct player *pplayer = client_player();
1298 if (!pplayer) {
1299 return;
1301 city_list_iterate(pplayer->cities, pcity) {
1302 draw_trade_routes_for_city(pcity);
1303 } city_list_iterate_end;
1307 /**************************************************************************
1308 Update (refresh) the map canvas starting at the given tile (in map
1309 coordinates) and with the given dimensions (also in map coordinates).
1311 In non-iso view, this is easy. In iso view, we have to use the
1312 Painter's Algorithm to draw the tiles in back first. When we draw
1313 a tile, we tell the GUI which part of the tile to draw - which is
1314 necessary unless we have an extra buffering step.
1316 After refreshing the backing store tile-by-tile, we write the store
1317 out to the display if write_to_screen is specified.
1319 x, y, width, and height are in map coordinates; they need not be
1320 normalized or even real.
1321 **************************************************************************/
1322 void update_map_canvas(int canvas_x, int canvas_y, int width, int height)
1324 int gui_x0, gui_y0;
1325 bool full;
1326 struct canvas *tmp;
1328 canvas_x = MAX(canvas_x, 0);
1329 canvas_y = MAX(canvas_y, 0);
1330 width = MIN(mapview.store_width - canvas_x, width);
1331 height = MIN(mapview.store_height - canvas_y, height);
1333 gui_x0 = mapview.gui_x0 + canvas_x;
1334 gui_y0 = mapview.gui_y0 + canvas_y;
1335 full = (canvas_x == 0 && canvas_y == 0
1336 && width == mapview.store_width
1337 && height == mapview.store_height);
1339 log_debug("update_map_canvas(pos=(%d,%d), size=(%d,%d))",
1340 canvas_x, canvas_y, width, height);
1342 /* If a full redraw is done, we just draw everything onto the canvas.
1343 * However if a partial redraw is done we draw everything onto the
1344 * tmp_canvas then copy *just* the area of update onto the canvas. */
1345 if (!full) {
1346 /* Swap store and tmp_store. */
1347 tmp = mapview.store;
1348 mapview.store = mapview.tmp_store;
1349 mapview.tmp_store = tmp;
1352 /* Clear the area. This is necessary since some parts of the rectangle
1353 * may not actually have any tiles drawn on them. This will happen when
1354 * the mapview is large enough so that the tile is visible in multiple
1355 * locations. In this case it will only be drawn in one place.
1357 * Of course it's necessary to draw to the whole area to cover up any old
1358 * drawing that was done there. */
1359 canvas_put_rectangle(mapview.store,
1360 get_color(tileset, COLOR_MAPVIEW_UNKNOWN),
1361 canvas_x, canvas_y, width, height);
1363 mapview_layer_iterate(layer) {
1364 if (layer == LAYER_TILELABEL) {
1365 show_tile_labels(canvas_x, canvas_y, width, height);
1367 if (layer == LAYER_CITYBAR) {
1368 show_city_descriptions(canvas_x, canvas_y, width, height);
1369 continue;
1371 gui_rect_iterate_coord(gui_x0, gui_y0, width,
1372 height + (tileset_is_isometric(tileset)
1373 ? (tileset_tile_height(tileset) / 2 * map_zoom) : 0),
1374 ptile, pedge, pcorner, gui_x, gui_y, map_zoom) {
1375 const int cx = gui_x - mapview.gui_x0, cy = gui_y - mapview.gui_y0;
1377 if (ptile) {
1378 put_one_tile(mapview.store, layer, ptile, cx, cy, NULL);
1379 } else if (pedge) {
1380 put_one_element(mapview.store, map_zoom, layer, NULL, pedge, NULL,
1381 NULL, NULL, cx, cy, NULL, NULL);
1382 } else if (pcorner) {
1383 put_one_element(mapview.store, map_zoom, layer, NULL, NULL, pcorner,
1384 NULL, NULL, cx, cy, NULL, NULL);
1385 } else {
1386 /* This can happen, for instance for unreal tiles. */
1388 } gui_rect_iterate_coord_end;
1389 } mapview_layer_iterate_end;
1391 draw_trade_routes();
1392 link_marks_draw_all();
1394 /* Draw the goto lines on top of the whole thing. This is done last as
1395 * we want it completely on top.
1397 * Note that a pixel right on the border of a tile may actually contain a
1398 * goto line from an adjacent tile. Thus we draw any extra goto lines
1399 * from adjacent tiles (if they're close enough). */
1400 gui_rect_iterate(gui_x0 - GOTO_WIDTH, gui_y0 - GOTO_WIDTH,
1401 width + 2 * GOTO_WIDTH, height + 2 * GOTO_WIDTH,
1402 ptile, pedge, pcorner, map_zoom) {
1403 if (!ptile) {
1404 continue;
1406 adjc_dir_base_iterate(ptile, dir) {
1407 if (mapdeco_is_gotoline_set(ptile, dir)) {
1408 draw_segment(ptile, dir);
1410 } adjc_dir_base_iterate_end;
1411 } gui_rect_iterate_end;
1413 if (!full) {
1414 /* Swap store and tmp_store back. */
1415 tmp = mapview.store;
1416 mapview.store = mapview.tmp_store;
1417 mapview.tmp_store = tmp;
1419 /* And copy store to tmp_store. */
1420 canvas_copy(mapview.store, mapview.tmp_store,
1421 canvas_x, canvas_y, canvas_x, canvas_y, width, height);
1424 dirty_rect(canvas_x, canvas_y, width, height);
1427 /**************************************************************************
1428 Update (only) the visible part of the map
1429 **************************************************************************/
1430 void update_map_canvas_visible(void)
1432 queue_mapview_update(UPDATE_MAP_CANVAS_VISIBLE);
1435 /* The maximum city description width and height. This gives the dimensions
1436 * of a rectangle centered directly beneath the tile a city is on, that
1437 * contains the city description.
1439 * These values are increased when drawing is done. This may mean that
1440 * the change (from increasing the value) won't take place until the
1441 * next redraw. */
1442 static int max_desc_width = 0, max_desc_height = 0;
1444 /* Same for tile labels */
1445 static int max_label_width = 0, max_label_height = 0 ;
1447 /**************************************************************************
1448 Update the city description for the given city.
1449 **************************************************************************/
1450 void update_city_description(struct city *pcity)
1452 queue_mapview_tile_update(pcity->tile, TILE_UPDATE_CITY_DESC);
1455 /**************************************************************************
1456 Update the label for the given tile
1457 **************************************************************************/
1458 void update_tile_label(struct tile *ptile)
1460 queue_mapview_tile_update(ptile, TILE_UPDATE_TILE_LABEL);
1463 /****************************************************************************
1464 Draw a "full" city bar for the city. This is a subcase of show_city_desc
1465 (see that function for more info) for tilesets that have a full city bar.
1466 ****************************************************************************/
1467 static void show_full_citybar(struct canvas *pcanvas,
1468 const int canvas_x0, const int canvas_y0,
1469 struct city *pcity, int *width, int *height)
1471 const struct citybar_sprites *citybar = get_citybar_sprites(tileset);
1472 static char name[512], growth[32], prod[512], size[32], trade_routes[32];
1473 enum color_std growth_color;
1474 enum color_std production_color;
1475 /* trade_routes_color initialized just to get rid off gcc warning
1476 * on optimization level 3 when it misdiagnoses that it would be used
1477 * uninitialized otherwise. Funny thing here is that warning would
1478 * go away also by *not* setting it to values other than
1479 * COLOR_MAPVIEW_CITYTEXT in get_city_mapview_trade_routes() */
1480 enum color_std trade_routes_color = COLOR_MAPVIEW_CITYTEXT;
1481 struct color *owner_color;
1482 struct {
1483 int x, y, w, h;
1484 } name_rect = {0, 0, 0, 0}, growth_rect = {0, 0, 0, 0},
1485 prod_rect = {0, 0, 0, 0}, size_rect = {0, 0, 0, 0},
1486 flag_rect = {0, 0, 0, 0}, occupy_rect = {0, 0, 0, 0},
1487 food_rect = {0, 0, 0, 0}, shield_rect = {0, 0, 0, 0},
1488 trade_routes_rect = {0,}, trade_rect = {0,};
1489 int width1 = 0, width2 = 0, height1 = 0, height2 = 0;
1490 struct sprite *bg = citybar->background;
1491 struct sprite *flag = get_city_flag_sprite(tileset, pcity);
1492 struct sprite *occupy = NULL;
1493 int bg_w, bg_h, x, y;
1494 const int canvas_x = canvas_x0 + tileset_tile_width(tileset) / 2 * map_zoom;
1495 const int canvas_y = canvas_y0 + tileset_citybar_offset_y(tileset) * map_zoom;
1496 const int border = 6;
1497 const enum client_font FONT_CITY_SIZE = FONT_CITY_NAME; /* TODO: new font */
1499 /* We can see the city's production or growth values if
1500 * we are observing or playing as the owner of the city. */
1501 const bool can_see_inside
1502 = (client_is_global_observer() || city_owner(pcity) == client_player());
1503 const bool should_draw_productions
1504 = can_see_inside && gui_options.draw_city_productions;
1505 const bool should_draw_growth = can_see_inside && gui_options.draw_city_growth;
1506 const bool should_draw_trade_routes = can_see_inside
1507 && gui_options.draw_city_trade_routes;
1508 const bool should_draw_lower_bar
1509 = should_draw_productions || should_draw_growth
1510 || should_draw_trade_routes;
1513 if (width != NULL) {
1514 *width = 0;
1516 if (height != NULL) {
1517 *height = 0;
1520 if (!gui_options.draw_city_names && !should_draw_lower_bar) {
1521 return;
1525 /* First: calculate rect dimensions (but not positioning). */
1527 get_sprite_dimensions(bg, &bg_w, &bg_h);
1528 get_city_mapview_name_and_growth(pcity, name, sizeof(name),
1529 growth, sizeof(growth), &growth_color, &production_color);
1531 if (gui_options.draw_city_names) {
1532 fc_snprintf(size, sizeof(size), "%d", city_size_get(pcity));
1534 get_text_size(&size_rect.w, &size_rect.h, FONT_CITY_SIZE, size);
1535 get_text_size(&name_rect.w, &name_rect.h, FONT_CITY_NAME, name);
1537 if (can_player_see_units_in_city(client.conn.playing, pcity)) {
1538 int count = unit_list_size(pcity->tile->units);
1540 count = CLIP(0, count, citybar->occupancy.size - 1);
1541 occupy = citybar->occupancy.p[count];
1542 } else {
1543 if (pcity->client.occupied) {
1544 occupy = citybar->occupied;
1545 } else {
1546 occupy = citybar->occupancy.p[0];
1550 get_sprite_dimensions(flag, &flag_rect.w, &flag_rect.h);
1551 get_sprite_dimensions(occupy, &occupy_rect.w, &occupy_rect.h);
1553 width1 = (flag_rect.w + occupy_rect.w + name_rect.w
1554 + 2 * border + size_rect.w);
1555 height1 = MAX(flag_rect.h,
1556 MAX(occupy_rect.h,
1557 MAX(name_rect.h + border,
1558 size_rect.h + border)));
1561 if (should_draw_lower_bar) {
1562 width2 = 0;
1563 height2 = 0;
1565 if (should_draw_productions) {
1566 get_city_mapview_production(pcity, prod, sizeof(prod));
1567 get_text_size(&prod_rect.w, &prod_rect.h, FONT_CITY_PROD, prod);
1569 get_sprite_dimensions(citybar->shields, &shield_rect.w, &shield_rect.h);
1570 width2 += shield_rect.w + prod_rect.w + border;
1571 height2 = MAX(height2, shield_rect.h);
1572 height2 = MAX(height2, prod_rect.h + border);
1575 if (should_draw_growth) {
1576 get_text_size(&growth_rect.w, &growth_rect.h, FONT_CITY_PROD, growth);
1577 get_sprite_dimensions(citybar->food, &food_rect.w, &food_rect.h);
1578 width2 += food_rect.w + growth_rect.w + border;
1579 height2 = MAX(height2, food_rect.h);
1580 height2 = MAX(height2, growth_rect.h + border);
1583 if (should_draw_trade_routes) {
1584 get_city_mapview_trade_routes(pcity, trade_routes,
1585 sizeof(trade_routes),
1586 &trade_routes_color);
1587 get_text_size(&trade_routes_rect.w, &trade_routes_rect.h,
1588 FONT_CITY_PROD, trade_routes);
1589 get_sprite_dimensions(citybar->trade, &trade_rect.w, &trade_rect.h);
1590 width2 += trade_rect.w + trade_routes_rect.w + border;
1591 height2 = MAX(height2, trade_rect.h);
1592 height2 = MAX(height2, trade_routes_rect.h + border);
1596 *width = MAX(width1, width2);
1597 *height = height1 + height2;
1600 /* Next fill in X and Y locations. */
1602 if (gui_options.draw_city_names) {
1603 flag_rect.x = canvas_x - *width / 2;
1604 flag_rect.y = canvas_y + (height1 - flag_rect.h) / 2;
1606 occupy_rect.x = flag_rect.x + flag_rect.w;
1607 occupy_rect.y = canvas_y + (height1 - occupy_rect.h) / 2;
1609 name_rect.x = canvas_x + (flag_rect.w + occupy_rect.w
1610 - name_rect.w - size_rect.w - border) / 2;
1611 name_rect.y = canvas_y + (height1 - name_rect.h) / 2;
1613 size_rect.x = canvas_x + (*width + 1) / 2 - size_rect.w - border / 2;
1614 size_rect.y = canvas_y + (height1 - size_rect.h) / 2;
1617 if (should_draw_lower_bar) {
1618 if (should_draw_productions) {
1619 shield_rect.x = canvas_x - *width / 2;
1620 shield_rect.y = canvas_y + height1 + (height2 - shield_rect.h) / 2;
1622 prod_rect.x = shield_rect.x + shield_rect.w + border / 2;
1623 prod_rect.y = canvas_y + height1 + (height2 - prod_rect.h) / 2;
1626 if (should_draw_trade_routes) {
1627 trade_routes_rect.x = canvas_x + (*width + 1) / 2
1628 - trade_routes_rect.w - border / 2;
1629 trade_routes_rect.y = canvas_y + height1
1630 + (height2 - trade_routes_rect.h) / 2;
1632 trade_rect.x = trade_routes_rect.x - border / 2 - trade_rect.w;
1633 trade_rect.y = canvas_y + height1 + (height2 - trade_rect.h) / 2;
1636 if (should_draw_growth) {
1637 growth_rect.x = canvas_x + (*width + 1) / 2
1638 - growth_rect.w - border / 2;
1639 if (trade_routes_rect.w > 0) {
1640 growth_rect.x = growth_rect.x
1641 - trade_routes_rect.w - border / 2 - trade_rect.w - border / 2;
1643 growth_rect.y = canvas_y + height1 + (height2 - growth_rect.h) / 2;
1645 food_rect.x = growth_rect.x - border / 2 - food_rect.w;
1646 food_rect.y = canvas_y + height1 + (height2 - food_rect.h) / 2;
1651 /* Now draw. */
1653 /* Draw the city bar's background. */
1654 for (x = 0; x < *width; x += bg_w) {
1655 for (y = 0; y < *height; y += bg_h) {
1656 canvas_put_sprite(pcanvas, (canvas_x - *width / 2 + x) / map_zoom,
1657 (canvas_y + y) / map_zoom,
1658 bg, 0, 0, *width - x, *height - y);
1662 owner_color = get_player_color(tileset, city_owner(pcity));
1664 if (gui_options.draw_city_names) {
1665 canvas_put_sprite_full(pcanvas, flag_rect.x / map_zoom, flag_rect.y / map_zoom,
1666 flag);
1667 canvas_put_line(pcanvas, owner_color, LINE_NORMAL,
1668 (flag_rect.x + flag_rect.w) / map_zoom - 1, canvas_y / map_zoom,
1669 0, height1);
1670 canvas_put_sprite_full(pcanvas,
1671 occupy_rect.x / map_zoom, occupy_rect.y / map_zoom,
1672 occupy);
1673 canvas_put_text(pcanvas, name_rect.x / map_zoom, name_rect.y / map_zoom,
1674 FONT_CITY_NAME,
1675 get_color(tileset, COLOR_MAPVIEW_CITYTEXT), name);
1676 canvas_put_rectangle(pcanvas, owner_color,
1677 (size_rect.x - border / 2) / map_zoom,
1678 canvas_y / map_zoom,
1679 size_rect.w + border, height1);
1681 /* Try to pick a color for city size text that contrasts with
1682 * player color */
1683 struct color *textcolors[2] = {
1684 get_color(tileset, COLOR_MAPVIEW_CITYTEXT),
1685 get_color(tileset, COLOR_MAPVIEW_CITYTEXT_DARK)
1688 canvas_put_text(pcanvas, size_rect.x / map_zoom, size_rect.y / map_zoom,
1689 FONT_CITY_NAME,
1690 color_best_contrast(owner_color, textcolors,
1691 ARRAY_SIZE(textcolors)), size);
1695 if (should_draw_lower_bar) {
1697 if (should_draw_productions) {
1698 canvas_put_sprite_full(pcanvas,
1699 shield_rect.x / map_zoom, shield_rect.y / map_zoom,
1700 citybar->shields);
1701 canvas_put_text(pcanvas, prod_rect.x / map_zoom, prod_rect.y / map_zoom,
1702 FONT_CITY_PROD,
1703 get_color(tileset, production_color), prod);
1706 if (should_draw_trade_routes) {
1707 canvas_put_sprite_full(pcanvas,
1708 trade_rect.x / map_zoom, trade_rect.y / map_zoom,
1709 citybar->trade);
1710 canvas_put_text(pcanvas,
1711 trade_routes_rect.x / map_zoom, trade_routes_rect.y / map_zoom,
1712 FONT_CITY_PROD,
1713 get_color(tileset, trade_routes_color), trade_routes);
1716 if (should_draw_growth) {
1717 canvas_put_sprite_full(pcanvas,
1718 food_rect.x / map_zoom, food_rect.y / map_zoom,
1719 citybar->food);
1720 canvas_put_text(pcanvas, growth_rect.x / map_zoom, growth_rect.y / map_zoom,
1721 FONT_CITY_PROD,
1722 get_color(tileset, growth_color), growth);
1726 /* Draw the city bar's outline. */
1727 canvas_put_line(pcanvas, owner_color, LINE_NORMAL,
1728 (canvas_x - *width / 2) / map_zoom, canvas_y / map_zoom,
1729 *width, 0);
1730 canvas_put_line(pcanvas, owner_color, LINE_NORMAL,
1731 (canvas_x - *width / 2) / map_zoom, canvas_y / map_zoom,
1732 0, *height);
1733 canvas_put_line(pcanvas, owner_color, LINE_NORMAL,
1734 (canvas_x - *width / 2) / map_zoom,
1735 (canvas_y + *height) / map_zoom - 1,
1736 *width, 0);
1737 canvas_put_line(pcanvas, owner_color, LINE_NORMAL,
1738 (canvas_x - *width / 2 + *width) / map_zoom,
1739 canvas_y / map_zoom,
1740 0, *height);
1742 /* Draw the dividing line if we drew both the
1743 * upper and lower parts. */
1744 if (gui_options.draw_city_names && should_draw_lower_bar) {
1745 canvas_put_line(pcanvas, owner_color, LINE_NORMAL,
1746 (canvas_x - *width / 2) / map_zoom,
1747 (canvas_y + height1) / map_zoom - 1,
1748 *width, 0);
1752 /****************************************************************************
1753 Draw a "small" city bar for the city. This is a subcase of show_city_desc
1754 (see that function for more info) for tilesets that do not have a full
1755 city bar.
1756 ****************************************************************************/
1757 static void show_small_citybar(struct canvas *pcanvas,
1758 int canvas_x, int canvas_y,
1759 struct city *pcity, int *width, int *height)
1761 static char name[512], growth[32], prod[512], trade_routes[32];
1762 enum color_std growth_color;
1763 enum color_std production_color;
1764 /* trade_routes_color initialized just to get rid off gcc warning
1765 * on optimization level 3 when it misdiagnoses that it would be used
1766 * uninitialized otherwise. Funny thing here is that warning would
1767 * go away also by *not* setting it to values other than
1768 * COLOR_MAPVIEW_CITYTEXT in get_city_mapview_trade_routes() */
1769 enum color_std trade_routes_color = COLOR_MAPVIEW_CITYTEXT;
1770 struct {
1771 int x, y, w, h;
1772 } name_rect = {0, 0, 0, 0}, growth_rect = {0, 0, 0, 0},
1773 prod_rect = {0, 0, 0, 0}, trade_routes_rect = {0,};
1774 int total_width, total_height;
1775 int spacer_width = 0;
1776 const bool can_see_inside = (client_is_global_observer()
1777 || city_owner(pcity) == client_player());
1779 *width = *height = 0;
1781 canvas_x += tileset_tile_width(tileset) / 2 * map_zoom;
1782 canvas_y += tileset_citybar_offset_y(tileset) * map_zoom;
1784 get_city_mapview_name_and_growth(pcity, name, sizeof(name),
1785 growth, sizeof(growth), &growth_color,
1786 &production_color);
1788 if (gui_options.draw_city_names) {
1789 int drawposx;
1791 /* HACK: put a character's worth of space between the two
1792 * strings if needed. */
1793 get_text_size(&spacer_width, NULL, FONT_CITY_NAME, "M");
1795 total_width = 0;
1796 total_height = 0;
1798 get_text_size(&name_rect.w, &name_rect.h, FONT_CITY_NAME, name);
1799 total_width += name_rect.w;
1800 total_height = MAX(total_height, name_rect.h);
1802 if (gui_options.draw_city_growth && can_see_inside) {
1803 get_text_size(&growth_rect.w, &growth_rect.h, FONT_CITY_PROD, growth);
1804 total_width += spacer_width + growth_rect.w;
1805 total_height = MAX(total_height, growth_rect.h);
1808 if (gui_options.draw_city_trade_routes && can_see_inside) {
1809 get_city_mapview_trade_routes(pcity, trade_routes,
1810 sizeof(trade_routes),
1811 &trade_routes_color);
1812 get_text_size(&trade_routes_rect.w, &trade_routes_rect.h,
1813 FONT_CITY_PROD, trade_routes);
1814 total_width += spacer_width + trade_routes_rect.w;
1815 total_height = MAX(total_height, trade_routes_rect.h);
1818 drawposx = canvas_x;
1819 drawposx -= total_width / 2;
1820 canvas_put_text(pcanvas, drawposx / map_zoom, canvas_y / map_zoom,
1821 FONT_CITY_NAME,
1822 get_color(tileset, COLOR_MAPVIEW_CITYTEXT), name);
1823 drawposx += name_rect.w;
1825 if (gui_options.draw_city_growth && can_see_inside) {
1826 drawposx += spacer_width;
1827 canvas_put_text(pcanvas, drawposx / map_zoom,
1828 (canvas_y + total_height - growth_rect.h) / map_zoom,
1829 FONT_CITY_PROD,
1830 get_color(tileset, growth_color), growth);
1831 drawposx += growth_rect.w;
1834 if (gui_options.draw_city_trade_routes && can_see_inside) {
1835 drawposx += spacer_width;
1836 canvas_put_text(pcanvas, drawposx / map_zoom,
1837 (canvas_y + total_height - trade_routes_rect.h) / map_zoom,
1838 FONT_CITY_PROD,
1839 get_color(tileset, trade_routes_color), trade_routes);
1840 drawposx += trade_routes_rect.w;
1843 canvas_y += total_height + 3;
1845 *width = MAX(*width, total_width);
1846 *height += total_height + 3;
1848 if (gui_options.draw_city_productions && can_see_inside) {
1849 get_city_mapview_production(pcity, prod, sizeof(prod));
1850 get_text_size(&prod_rect.w, &prod_rect.h, FONT_CITY_PROD, prod);
1852 total_width = prod_rect.w;
1853 total_height = prod_rect.h;
1855 canvas_put_text(pcanvas, (canvas_x - total_width / 2) / map_zoom,
1856 canvas_y / map_zoom,
1857 FONT_CITY_PROD,
1858 get_color(tileset, production_color), prod);
1860 canvas_y += total_height;
1861 *width = MAX(*width, total_width);
1862 *height += total_height;
1866 /****************************************************************************
1867 Draw a description for the given city. This description may include the
1868 name, turns-to-grow, production, and city turns-to-build (depending on
1869 client options).
1871 (canvas_x, canvas_y) gives the location on the given canvas at which to
1872 draw the description. This is the location of the city itself so the
1873 text must be drawn underneath it. pcity gives the city to be drawn,
1874 while (*width, *height) should be set by show_city_desc to contain the
1875 width and height of the text block (centered directly underneath the
1876 city's tile).
1877 ****************************************************************************/
1878 static void show_city_desc(struct canvas *pcanvas,
1879 int canvas_x, int canvas_y,
1880 struct city *pcity, int *width, int *height)
1882 if (gui_options.draw_full_citybar) {
1883 show_full_citybar(pcanvas, canvas_x, canvas_y, pcity, width, height);
1884 } else {
1885 show_small_citybar(pcanvas, canvas_x, canvas_y, pcity, width, height);
1889 /****************************************************************************
1890 Draw a label for the given tile.
1892 (canvas_x, canvas_y) gives the location on the given canvas at which to
1893 draw the label. This is the location of the tile itself so the
1894 text must be drawn underneath it. pcity gives the city to be drawn,
1895 while (*width, *height) should be set by show_tile_label to contain the
1896 width and height of the text block (centered directly underneath the
1897 city's tile).
1898 ****************************************************************************/
1899 static void show_tile_label(struct canvas *pcanvas,
1900 int canvas_x, int canvas_y,
1901 struct tile *ptile, int *width, int *height)
1903 const enum client_font FONT_TILE_LABEL = FONT_CITY_NAME; /* TODO: new font */
1904 #define COLOR_MAPVIEW_TILELABEL COLOR_MAPVIEW_CITYTEXT
1906 canvas_x += tileset_tile_width(tileset) / 2 * map_zoom;
1907 canvas_y += tileset_tilelabel_offset_y(tileset) * map_zoom;
1909 get_text_size(width, height, FONT_TILE_LABEL, ptile->label);
1911 canvas_put_text(pcanvas, (canvas_x - * width / 2) / map_zoom, canvas_y / map_zoom,
1912 FONT_TILE_LABEL,
1913 get_color(tileset, COLOR_MAPVIEW_TILELABEL), ptile->label);
1914 #undef COLOR_MAPVIEW_TILELABEL
1917 /**************************************************************************
1918 Show descriptions for all cities visible on the map canvas.
1919 **************************************************************************/
1920 void show_city_descriptions(int canvas_base_x, int canvas_base_y,
1921 int width_base, int height_base)
1923 const int dx = max_desc_width - tileset_tile_width(tileset) * map_zoom;
1924 const int dy = max_desc_height;
1925 const int offset_y = tileset_citybar_offset_y(tileset) * map_zoom;
1926 int new_max_width = max_desc_width, new_max_height = max_desc_height;
1928 if (gui_options.draw_full_citybar && !(gui_options.draw_city_names
1929 || gui_options.draw_city_productions
1930 || gui_options.draw_city_growth)) {
1931 return;
1934 if (!gui_options.draw_full_citybar && !(gui_options.draw_city_names
1935 || gui_options.draw_city_productions)) {
1936 return;
1939 /* A city description is shown below the city. It has a specified
1940 * maximum width and height (although these are only estimates). Thus
1941 * we need to update some tiles above the mapview and some to the left
1942 * and right.
1944 * /--W1--\ (W1 = tileset_tile_width(tileset))
1945 * -------- \
1946 * | CITY | H1 (H1 = tileset_tile_height(tileset))
1947 * | | /
1948 * ------------------ \
1949 * | DESCRIPTION | H2 (H2 = MAX_CITY_DESC_HEIGHT)
1950 * | | /
1951 * ------------------
1952 * \-------W2-------/ (W2 = MAX_CITY_DESC_WIDTH)
1954 * We must draw H2 extra pixels above and (W2 - W1) / 2 extra pixels
1955 * to each side of the mapview.
1957 gui_rect_iterate_coord(mapview.gui_x0 + canvas_base_x - dx / 2,
1958 mapview.gui_y0 + canvas_base_y - dy,
1959 width_base + dx, height_base + dy - offset_y,
1960 ptile, pedge, pcorner, gui_x, gui_y, map_zoom) {
1961 const int canvas_x = gui_x - mapview.gui_x0;
1962 const int canvas_y = gui_y - mapview.gui_y0;
1964 if (ptile && tile_city(ptile)) {
1965 int width = 0, height = 0;
1966 struct city *pcity = tile_city(ptile);
1968 show_city_desc(mapview.store, canvas_x, canvas_y,
1969 pcity, &width, &height);
1970 log_debug("Drawing %s.", city_name_get(pcity));
1972 if (width > max_desc_width || height > max_desc_height) {
1973 /* The update was incomplete! We queue a new update. Note that
1974 * this is recursively queueing an update within a dequeuing of an
1975 * update. This is allowed specifically because of the code in
1976 * unqueue_mapview_updates. See that function for more. */
1977 log_debug("Re-queuing %s.", city_name_get(pcity));
1978 update_city_description(pcity);
1980 new_max_width = MAX(width, new_max_width);
1981 new_max_height = MAX(height, new_max_height);
1983 } gui_rect_iterate_coord_end;
1985 /* We don't update the new max values until the end, so that the
1986 * check above to see what cities need redrawing will be complete. */
1987 max_desc_width = MAX(max_desc_width, new_max_width);
1988 max_desc_height = MAX(max_desc_height, new_max_height);
1991 /**************************************************************************
1992 Show labels for all tiles visible on the map canvas.
1993 **************************************************************************/
1994 void show_tile_labels(int canvas_base_x, int canvas_base_y,
1995 int width_base, int height_base)
1997 const int dx = max_label_width - tileset_tile_width(tileset) * map_zoom;
1998 const int dy = max_label_height;
1999 int new_max_width = max_label_width, new_max_height = max_label_height;
2001 gui_rect_iterate_coord(mapview.gui_x0 + canvas_base_x - dx / 2,
2002 mapview.gui_y0 + canvas_base_y - dy,
2003 width_base + dx, height_base + dy,
2004 ptile, pedge, pcorner, gui_x, gui_y, map_zoom) {
2005 const int canvas_x = gui_x - mapview.gui_x0;
2006 const int canvas_y = gui_y - mapview.gui_y0;
2008 if (ptile && ptile->label != NULL) {
2009 int width = 0, height = 0;
2011 show_tile_label(mapview.store, canvas_x, canvas_y,
2012 ptile, &width, &height);
2013 log_debug("Drawing label %s.", ptile->label);
2015 if (width > max_label_width || height > max_label_height) {
2016 /* The update was incomplete! We queue a new update. Note that
2017 * this is recursively queueing an update within a dequeuing of an
2018 * update. This is allowed specifically because of the code in
2019 * unqueue_mapview_updates. See that function for more. */
2020 log_debug("Re-queuing tile label %s drawing.", ptile->label);
2021 update_tile_label(ptile);
2023 new_max_width = MAX(width, new_max_width);
2024 new_max_height = MAX(height, new_max_height);
2026 } gui_rect_iterate_coord_end;
2028 /* We don't update the new max values until the end, so that the
2029 * check above to see what cities need redrawing will be complete. */
2030 max_label_width = MAX(max_label_width, new_max_width);
2031 max_label_height = MAX(max_label_height, new_max_height);
2034 /****************************************************************************
2035 Draw the goto route for the unit. Return TRUE if anything is drawn.
2037 This duplicates drawing code that is run during the hover state.
2038 ****************************************************************************/
2039 bool show_unit_orders(struct unit *punit)
2041 if (punit && unit_has_orders(punit)) {
2042 struct tile *ptile = unit_tile(punit);
2043 int i;
2045 for (i = 0; i < punit->orders.length; i++) {
2046 int idx = (punit->orders.index + i) % punit->orders.length;
2047 struct unit_order *order;
2049 if (punit->orders.index + i >= punit->orders.length
2050 && !punit->orders.repeat) {
2051 break;
2054 order = &punit->orders.list[idx];
2056 switch (order->order) {
2057 case ORDER_MOVE:
2058 draw_segment(ptile, order->dir);
2059 ptile = mapstep(ptile, order->dir);
2060 if (!ptile) {
2061 /* This shouldn't happen unless the server gives us invalid
2062 * data. To avoid disaster we need to break out of the
2063 * switch and the enclosing for loop. */
2064 fc_assert(NULL != ptile);
2065 i = punit->orders.length;
2067 break;
2068 default:
2069 /* TODO: graphics for other orders. */
2070 break;
2073 return TRUE;
2074 } else {
2075 return FALSE;
2079 /****************************************************************************
2080 Draw a goto line at the given location and direction. The line goes from
2081 the source tile to the adjacent tile in the given direction.
2082 ****************************************************************************/
2083 void draw_segment(struct tile *src_tile, enum direction8 dir)
2085 float canvas_x, canvas_y, canvas_dx, canvas_dy;
2087 /* Determine the source position of the segment. */
2088 (void) tile_to_canvas_pos(&canvas_x, &canvas_y, src_tile);
2089 canvas_x += tileset_tile_width(tileset) / 2 * map_zoom;
2090 canvas_y += tileset_tile_height(tileset) / 2 * map_zoom;
2092 /* Determine the vector of the segment. */
2093 map_to_gui_vector(tileset, map_zoom, &canvas_dx, &canvas_dy,
2094 DIR_DX[dir], DIR_DY[dir]);
2096 /* Draw the segment. */
2097 canvas_put_line(mapview.store,
2098 get_color(tileset, COLOR_MAPVIEW_GOTO), LINE_GOTO,
2099 canvas_x, canvas_y, canvas_dx, canvas_dy);
2101 /* The actual area drawn will extend beyond the base rectangle, since
2102 * the goto lines have width. */
2103 dirty_rect(MIN(canvas_x, canvas_x + canvas_dx) - GOTO_WIDTH,
2104 MIN(canvas_y, canvas_y + canvas_dy) - GOTO_WIDTH,
2105 ABS(canvas_dx) + 2 * GOTO_WIDTH,
2106 ABS(canvas_dy) + 2 * GOTO_WIDTH);
2108 /* It is possible that the mapview wraps between the source and dest
2109 * tiles. In this case they will not be next to each other; they'll be
2110 * on the opposite sides of the screen. If this happens then the dest
2111 * tile will not be updated. This is consistent with the mapview design
2112 * which fails when the size of the mapview approaches that of the map. */
2115 /****************************************************************************
2116 This function is called to decrease a unit's HP smoothly in battle
2117 when combat_animation is turned on.
2118 ****************************************************************************/
2119 void decrease_unit_hp_smooth(struct unit *punit0, int hp0,
2120 struct unit *punit1, int hp1)
2122 static struct timer *anim_timer = NULL;
2123 const struct sprite_vector *anim = get_unit_explode_animation(tileset);
2124 const int num_tiles_explode_unit = sprite_vector_size(anim);
2125 struct unit *losing_unit = (hp0 == 0 ? punit0 : punit1);
2126 float canvas_x, canvas_y;
2127 int i;
2129 set_units_in_combat(punit0, punit1);
2131 /* Make sure we don't start out with fewer HP than we're supposed to
2132 * end up with (which would cause the following loop to break). */
2133 punit0->hp = MAX(punit0->hp, hp0);
2134 punit1->hp = MAX(punit1->hp, hp1);
2136 unqueue_mapview_updates(TRUE);
2137 while (punit0->hp > hp0 || punit1->hp > hp1) {
2138 const int diff0 = punit0->hp - hp0, diff1 = punit1->hp - hp1;
2140 anim_timer = timer_renew(anim_timer, TIMER_USER, TIMER_ACTIVE);
2141 timer_start(anim_timer);
2143 if (fc_rand(diff0 + diff1) < diff0) {
2144 punit0->hp--;
2145 refresh_unit_mapcanvas(punit0, unit_tile(punit0), FALSE, FALSE);
2146 } else {
2147 punit1->hp--;
2148 refresh_unit_mapcanvas(punit1, unit_tile(punit1), FALSE, FALSE);
2151 unqueue_mapview_updates(TRUE);
2152 gui_flush();
2154 timer_usleep_since_start(anim_timer,
2155 gui_options.smooth_combat_step_msec * 1000ul);
2158 if (num_tiles_explode_unit > 0
2159 && tile_to_canvas_pos(&canvas_x, &canvas_y,
2160 unit_tile(losing_unit))) {
2161 refresh_unit_mapcanvas(losing_unit, unit_tile(losing_unit), FALSE, FALSE);
2162 unqueue_mapview_updates(FALSE);
2163 canvas_copy(mapview.tmp_store, mapview.store,
2164 canvas_x, canvas_y, canvas_x, canvas_y,
2165 tileset_tile_width(tileset) * map_zoom,
2166 tileset_tile_height(tileset) * map_zoom);
2168 for (i = 0; i < num_tiles_explode_unit; i++) {
2169 int w, h;
2170 struct sprite *sprite = *sprite_vector_get(anim, i);
2172 get_sprite_dimensions(sprite, &w, &h);
2173 anim_timer = timer_renew(anim_timer, TIMER_USER, TIMER_ACTIVE);
2174 timer_start(anim_timer);
2176 /* We first draw the explosion onto the unit and draw draw the
2177 * complete thing onto the map canvas window. This avoids
2178 * flickering. */
2179 canvas_copy(mapview.store, mapview.tmp_store,
2180 canvas_x, canvas_y, canvas_x, canvas_y,
2181 tileset_tile_width(tileset) * map_zoom,
2182 tileset_tile_height(tileset) * map_zoom);
2183 canvas_put_sprite_full(mapview.store,
2184 canvas_x + tileset_tile_width(tileset) / 2 * map_zoom
2185 - w / 2,
2186 canvas_y + tileset_tile_height(tileset) / 2 * map_zoom
2187 - h / 2,
2188 sprite);
2189 dirty_rect(canvas_x, canvas_y, tileset_tile_width(tileset) * map_zoom,
2190 tileset_tile_height(tileset) * map_zoom);
2192 flush_dirty();
2193 gui_flush();
2195 timer_usleep_since_start(anim_timer,
2196 gui_options.smooth_combat_step_msec * 2 * 1000ul);
2200 set_units_in_combat(NULL, NULL);
2201 refresh_unit_mapcanvas(punit0, unit_tile(punit0), TRUE, FALSE);
2202 refresh_unit_mapcanvas(punit1, unit_tile(punit1), TRUE, FALSE);
2205 /**************************************************************************
2206 Animates punit's "smooth" move from (x0, y0) to (x0+dx, y0+dy).
2207 Note: Works only for adjacent-tile moves.
2208 **************************************************************************/
2209 void move_unit_map_canvas(struct unit *punit,
2210 struct tile *src_tile, int dx, int dy)
2212 static struct timer *anim_timer = NULL;
2213 struct tile *dest_tile;
2214 int dest_x, dest_y, src_x, src_y;
2215 int prev_x = -1;
2216 int prev_y = -1;
2217 int tuw;
2218 int tuh;
2220 /* only works for adjacent-square moves */
2221 if (dx < -1 || dx > 1 || dy < -1 || dy > 1 || (dx == 0 && dy == 0)) {
2222 return;
2225 index_to_map_pos(&src_x, &src_y, tile_index(src_tile));
2226 dest_x = src_x + dx;
2227 dest_y = src_y + dy;
2228 dest_tile = map_pos_to_tile(dest_x, dest_y);
2229 if (!dest_tile) {
2230 return;
2233 if (tile_visible_mapcanvas(src_tile)
2234 || tile_visible_mapcanvas(dest_tile)) {
2235 float start_x, start_y;
2236 float canvas_dx, canvas_dy;
2237 double timing_sec = (double)gui_options.smooth_move_unit_msec / 1000.0;
2238 double mytime;
2240 fc_assert(gui_options.smooth_move_unit_msec > 0);
2242 map_to_gui_vector(tileset, map_zoom, &canvas_dx, &canvas_dy, dx, dy);
2244 tile_to_canvas_pos(&start_x, &start_y, src_tile);
2245 if (tileset_is_isometric(tileset) && tileset_hex_height(tileset) == 0) {
2246 start_y -= tileset_tile_height(tileset) / 2 * map_zoom;
2247 start_y -= (tileset_unit_height(tileset) - tileset_full_tile_height(tileset)) * map_zoom;
2250 /* Bring the backing store up to date, but don't flush. */
2251 unqueue_mapview_updates(FALSE);
2253 /* Start the timer (AFTER the unqueue above). */
2254 anim_timer = timer_renew(anim_timer, TIMER_USER, TIMER_ACTIVE);
2255 timer_start(anim_timer);
2257 tuw = tileset_unit_width(tileset) * map_zoom;
2258 tuh = tileset_unit_height(tileset) * map_zoom;
2260 do {
2261 int new_x, new_y;
2263 mytime = MIN(timer_read_seconds(anim_timer), timing_sec);
2265 new_x = start_x + canvas_dx * (mytime / timing_sec);
2266 new_y = start_y + canvas_dy * (mytime / timing_sec);
2268 if (new_x != prev_x || new_y != prev_y) {
2269 /* Backup the canvas store to the temp store. */
2270 canvas_copy(mapview.tmp_store, mapview.store,
2271 new_x, new_y, new_x, new_y,
2272 tuw, tuh);
2274 /* Draw */
2275 put_unit(punit, mapview.store, map_zoom, new_x, new_y);
2276 dirty_rect(new_x, new_y, tuw, tuh);
2278 /* Flush. */
2279 flush_dirty();
2280 gui_flush();
2282 /* Restore the backup. It won't take effect until the next flush. */
2283 canvas_copy(mapview.store, mapview.tmp_store,
2284 new_x, new_y, new_x, new_y,
2285 tuw, tuh);
2286 dirty_rect(new_x, new_y, tuw, tuh);
2288 prev_x = new_x;
2289 prev_y = new_y;
2290 } else {
2291 fc_usleep(500);
2293 } while (mytime < timing_sec);
2297 /**************************************************************************
2298 Find the "best" city/settlers to associate with the selected tile.
2299 a. If a visible city is working the tile, return that city.
2300 b. If another player's city is working the tile, return NULL.
2301 c. If any selected cities are within range, return the closest one.
2302 d. If any cities are within range, return the closest one.
2303 e. If any active (with color) settler could work it if they founded a
2304 city, choose the closest one (only if punit != NULL).
2305 f. If any settler could work it if they founded a city, choose the
2306 closest one (only if punit != NULL).
2307 g. If nobody can work it, return NULL.
2308 **************************************************************************/
2309 struct city *find_city_or_settler_near_tile(const struct tile *ptile,
2310 struct unit **punit)
2312 struct city *closest_city;
2313 struct city *pcity;
2314 struct unit *closest_settler = NULL, *best_settler = NULL;
2315 int max_rad = rs_max_city_radius_sq();
2317 if (punit) {
2318 *punit = NULL;
2321 /* Check if there is visible city working that tile */
2322 pcity = tile_worked(ptile);
2323 if (pcity && pcity->tile) {
2324 if (NULL == client.conn.playing
2325 || city_owner(pcity) == client.conn.playing) {
2326 /* rule a */
2327 return pcity;
2328 } else {
2329 /* rule b */
2330 return NULL;
2334 /* rule e */
2335 closest_city = NULL;
2337 /* check within maximum (squared) city radius */
2338 city_tile_iterate(max_rad, ptile, tile1) {
2339 pcity = tile_city(tile1);
2340 if (pcity
2341 && (NULL == client.conn.playing
2342 || city_owner(pcity) == client.conn.playing)
2343 && client_city_can_work_tile(pcity, tile1)) {
2345 * Note, we must explicitly check if the tile is workable (with
2346 * city_can_work_tile() above), since it is possible that another
2347 * city (perhaps an UNSEEN city) may be working it!
2350 if (mapdeco_is_highlight_set(city_tile(pcity))) {
2351 /* rule c */
2352 return pcity;
2354 if (!closest_city) {
2355 closest_city = pcity;
2358 } city_tile_iterate_end;
2360 /* rule d */
2361 if (closest_city || !punit) {
2362 return closest_city;
2365 if (!game.scenario.prevent_new_cities) {
2366 /* check within maximum (squared) city radius */
2367 city_tile_iterate(max_rad, ptile, tile1) {
2368 unit_list_iterate(tile1->units, psettler) {
2369 if ((NULL == client.conn.playing
2370 || unit_owner(psettler) == client.conn.playing)
2371 && unit_can_do_action(psettler, ACTION_FOUND_CITY)
2372 && city_can_be_built_here(unit_tile(psettler), psettler)) {
2373 if (!closest_settler) {
2374 closest_settler = psettler;
2376 if (!best_settler && psettler->client.colored) {
2377 best_settler = psettler;
2380 } unit_list_iterate_end;
2381 } city_tile_iterate_end;
2383 if (best_settler) {
2384 /* Rule e */
2385 *punit = best_settler;
2386 } else if (closest_settler) {
2387 /* Rule f */
2388 *punit = closest_settler;
2392 /* rule g */
2393 return NULL;
2396 /**************************************************************************
2397 Find the nearest/best city that owns the tile.
2398 **************************************************************************/
2399 struct city *find_city_near_tile(const struct tile *ptile)
2401 return find_city_or_settler_near_tile(ptile, NULL);
2404 /**************************************************************************
2405 Append the buy cost of the current production of the given city to the
2406 already NULL-terminated buffer. Does nothing if draw_city_buycost is
2407 set to FALSE, or if it does not make sense to buy the current production
2408 (e.g. coinage).
2409 **************************************************************************/
2410 static void append_city_buycost_string(const struct city *pcity,
2411 char *buffer, int buffer_len)
2413 if (!pcity || !buffer || buffer_len < 1) {
2414 return;
2417 if (!gui_options.draw_city_buycost || !city_can_buy(pcity)) {
2418 return;
2421 cat_snprintf(buffer, buffer_len, "/%d",
2422 city_production_buy_gold_cost(pcity));
2425 /**************************************************************************
2426 Find the mapview city production text for the given city, and place it
2427 into the buffer.
2428 **************************************************************************/
2429 void get_city_mapview_production(struct city *pcity,
2430 char *buffer, size_t buffer_len)
2432 int turns;
2434 universal_name_translation(&pcity->production, buffer, buffer_len);
2436 if (city_production_has_flag(pcity, IF_GOLD)) {
2437 return;
2439 turns = city_production_turns_to_build(pcity, TRUE);
2441 if (999 < turns) {
2442 cat_snprintf(buffer, buffer_len, " -");
2443 } else {
2444 cat_snprintf(buffer, buffer_len, " %d", turns);
2447 append_city_buycost_string(pcity, buffer, buffer_len);
2450 /**************************************************************************
2451 Find the mapview city trade routes text for the given city, and place it
2452 into the buffer. Sets 'pcolor' to the preferred color the text should
2453 be drawn in if it is non-NULL.
2454 **************************************************************************/
2455 void get_city_mapview_trade_routes(struct city *pcity,
2456 char *trade_routes_buffer,
2457 size_t trade_routes_buffer_len,
2458 enum color_std *pcolor)
2460 int num_trade_routes;
2461 int max_routes;
2463 if (!trade_routes_buffer || trade_routes_buffer_len <= 0) {
2464 return;
2467 if (!pcity) {
2468 trade_routes_buffer[0] = '\0';
2469 if (pcolor) {
2470 *pcolor = COLOR_MAPVIEW_CITYTEXT;
2472 return;
2475 num_trade_routes = trade_route_list_size(pcity->routes);
2476 max_routes = max_trade_routes(pcity);
2478 fc_snprintf(trade_routes_buffer, trade_routes_buffer_len,
2479 "%d/%d", num_trade_routes, max_routes);
2481 if (pcolor) {
2482 if (num_trade_routes == max_routes) {
2483 *pcolor = COLOR_MAPVIEW_TRADE_ROUTES_ALL_BUILT;
2484 } else if (num_trade_routes == 0) {
2485 *pcolor = COLOR_MAPVIEW_TRADE_ROUTES_NO_BUILT;
2486 } else {
2487 *pcolor = COLOR_MAPVIEW_TRADE_ROUTES_SOME_BUILT;
2492 /***************************************************************************/
2493 static enum update_type needed_updates = UPDATE_NONE;
2494 static bool callback_queued = FALSE;
2496 /* These values hold the tiles that need city, unit, or tile updates.
2497 * These different types of updates just tell what area need to be updated,
2498 * not necessarily what's sitting on the tile. A city update covers the
2499 * whole citymap area. A unit update covers just the "full" unit tile
2500 * area. A tile update covers the base tile plus half a tile in each
2501 * direction. */
2502 struct tile_list *tile_updates[TILE_UPDATE_COUNT];
2504 /****************************************************************************
2505 This callback is called during an idle moment to unqueue any pending
2506 mapview updates.
2507 ****************************************************************************/
2508 static void queue_callback(void *data)
2510 callback_queued = FALSE;
2511 unqueue_mapview_updates(TRUE);
2514 /****************************************************************************
2515 When a mapview update is queued this function should be called to prepare
2516 an idle-time callback to unqueue the updates.
2517 ****************************************************************************/
2518 static void queue_add_callback(void)
2520 if (!callback_queued) {
2521 callback_queued = TRUE;
2522 add_idle_callback(queue_callback, NULL);
2526 /**************************************************************************
2527 This function, along with unqueue_mapview_update(), helps in updating
2528 the mapview when a packet is received. Previously, we just called
2529 update_map_canvas when (for instance) a city update was received.
2530 Not only would this often end up with a lot of duplicated work, but it
2531 would also draw over the city descriptions, which would then just
2532 "disappear" from the mapview. The hack is to instead call
2533 queue_mapview_update in place of this update, and later (after all
2534 packets have been read) call unqueue_mapview_update. The functions
2535 don't track which areas of the screen need updating, rather when the
2536 unqueue is done we just update the whole visible mapqueue, and redraw
2537 the city descriptions.
2539 Using these functions, updates are done correctly, and are probably
2540 faster too. But it's a bit of a hack to insert this code into the
2541 packet-handling code.
2542 **************************************************************************/
2543 void queue_mapview_update(enum update_type update)
2545 if (can_client_change_view()) {
2546 needed_updates |= update;
2547 queue_add_callback();
2551 /**************************************************************************
2552 Queue this tile to be refreshed. The refresh will be done some time
2553 soon thereafter, and grouped with other needed refreshes.
2555 Note this should only be called for tiles. For cities or units use
2556 queue_mapview_xxx_update instead.
2557 **************************************************************************/
2558 void queue_mapview_tile_update(struct tile *ptile,
2559 enum tile_update_type type)
2561 if (can_client_change_view()) {
2562 if (!tile_updates[type]) {
2563 tile_updates[type] = tile_list_new();
2565 tile_list_append(tile_updates[type], ptile);
2566 queue_add_callback();
2570 /**************************************************************************
2571 See comment for queue_mapview_update().
2572 **************************************************************************/
2573 void unqueue_mapview_updates(bool write_to_screen)
2575 /* Calculate the area covered by each update type. The area array gives
2576 * the offset from the tile origin as well as the width and height of the
2577 * area to be updated. This is initialized each time when entering the
2578 * function from the existing tileset variables.
2580 * A TILE update covers the base tile (W x H) plus a half-tile in each
2581 * direction (for edge/corner graphics), making its area 2W x 2H.
2583 * A UNIT update covers a UW x UH area. This is centered horizontally
2584 * over the tile but extends up above the tile (e.g., units in iso-view).
2586 * A CITYMAP update covers the whole citymap of a tile. This includes
2587 * the citymap area itself plus an extra half-tile in each direction (for
2588 * edge/corner graphics).
2590 const float W = tileset_tile_width(tileset) * map_zoom;
2591 const float H = tileset_tile_height(tileset) * map_zoom;
2592 const float UW = tileset_unit_width(tileset) * map_zoom;
2593 const float UH = tileset_unit_height(tileset) * map_zoom;
2594 const float city_width = get_citydlg_canvas_width() * map_zoom + W;
2595 const float city_height = get_citydlg_canvas_height() * map_zoom + H;
2596 const struct {
2597 float dx, dy, w, h;
2598 } area[TILE_UPDATE_COUNT] = {
2599 {0, 0, W, H},
2600 {-W / 2, -H / 2, 2 * W, 2 * H},
2601 {(W - UW) / 2, H - UH, UW, UH},
2602 {-(max_desc_width - W) / 2, H, max_desc_width, max_desc_height},
2603 {-(city_width - W) / 2, -(city_height - H) / 2, city_width, city_height},
2604 {-(max_label_width - W) / 2, H, max_label_width, max_label_height}
2606 struct tile_list *my_tile_updates[TILE_UPDATE_COUNT];
2608 int i;
2610 if (!can_client_change_view()) {
2611 /* Double sanity check: make sure we don't unqueue an invalid update
2612 * after we've already detached. */
2613 return;
2616 log_debug("unqueue_mapview_update: needed_updates=%d",
2617 needed_updates);
2619 /* This code "pops" the lists of tile updates off of the static array and
2620 * stores them locally. This allows further updates to be queued within
2621 * the function itself (namely, within update_map_canvas). */
2622 for (i = 0; i < TILE_UPDATE_COUNT; i++) {
2623 my_tile_updates[i] = tile_updates[i];
2624 tile_updates[i] = NULL;
2627 if (!map_is_empty()) {
2628 if ((needed_updates & UPDATE_MAP_CANVAS_VISIBLE)
2629 || (needed_updates & UPDATE_CITY_DESCRIPTIONS)
2630 || (needed_updates & UPDATE_TILE_LABELS)) {
2631 dirty_all();
2632 update_map_canvas(0, 0, mapview.store_width,
2633 mapview.store_height);
2634 /* Have to update the overview too, since some tiles may have changed. */
2635 refresh_overview_canvas();
2636 } else {
2637 int min_x = mapview.width, min_y = mapview.height;
2638 int max_x = 0, max_y = 0;
2640 for (i = 0; i < TILE_UPDATE_COUNT; i++) {
2641 if (my_tile_updates[i]) {
2642 tile_list_iterate(my_tile_updates[i], ptile) {
2643 float xl, yt;
2644 int xr, yb;
2646 (void) tile_to_canvas_pos(&xl, &yt, ptile);
2648 xl += area[i].dx;
2649 yt += area[i].dy;
2650 xr = xl + area[i].w;
2651 yb = yt + area[i].h;
2653 if (xr > 0 && xl < mapview.width
2654 && yb > 0 && yt < mapview.height) {
2655 min_x = MIN(min_x, xl);
2656 min_y = MIN(min_y, yt);
2657 max_x = MAX(max_x, xr);
2658 max_y = MAX(max_y, yb);
2661 /* FIXME: These overview updates should be batched as well.
2662 * Right now they account for as much as 90% of the runtime of
2663 * the unqueue. */
2664 overview_update_tile(ptile);
2665 } tile_list_iterate_end;
2669 if (min_x < max_x && min_y < max_y) {
2670 update_map_canvas(min_x, min_y, max_x - min_x, max_y - min_y);
2675 for (i = 0; i < TILE_UPDATE_COUNT; i++) {
2676 if (my_tile_updates[i]) {
2677 tile_list_destroy(my_tile_updates[i]);
2680 needed_updates = UPDATE_NONE;
2682 if (write_to_screen) {
2683 flush_dirty();
2684 flush_dirty_overview();
2688 /**************************************************************************
2689 Fill the two buffers which information about the city which is shown
2690 below it. It does not take draw_city_names/draw_city_growth into account.
2691 **************************************************************************/
2692 void get_city_mapview_name_and_growth(struct city *pcity,
2693 char *name_buffer,
2694 size_t name_buffer_len,
2695 char *growth_buffer,
2696 size_t growth_buffer_len,
2697 enum color_std *growth_color,
2698 enum color_std *production_color)
2700 fc_strlcpy(name_buffer, city_name_get(pcity), name_buffer_len);
2702 *production_color = COLOR_MAPVIEW_CITYTEXT;
2703 if (NULL == client.conn.playing
2704 || city_owner(pcity) == client.conn.playing) {
2705 int turns = city_turns_to_grow(pcity);
2707 if (turns == 0) {
2708 fc_snprintf(growth_buffer, growth_buffer_len, "X");
2709 } else if (turns == FC_INFINITY) {
2710 fc_snprintf(growth_buffer, growth_buffer_len, "-");
2711 } else {
2712 /* Negative turns means we're shrinking, but that's handled
2713 down below. */
2714 fc_snprintf(growth_buffer, growth_buffer_len, "%d", abs(turns));
2717 if (turns <= 0) {
2718 /* A blocked or shrinking city has its growth status shown in red. */
2719 *growth_color = COLOR_MAPVIEW_CITYGROWTH_BLOCKED;
2720 } else {
2721 *growth_color = COLOR_MAPVIEW_CITYTEXT;
2724 if (pcity->surplus[O_SHIELD] < 0) {
2725 *production_color = COLOR_MAPVIEW_CITYPROD_NEGATIVE;
2727 } else {
2728 growth_buffer[0] = '\0';
2729 *growth_color = COLOR_MAPVIEW_CITYTEXT;
2733 /**************************************************************************
2734 Returns TRUE if cached drawing is possible. If the mapview is too large
2735 we have to turn it off.
2736 **************************************************************************/
2737 static bool can_do_cached_drawing(void)
2739 const int W = tileset_tile_width(tileset) * map_zoom;
2740 const int H = tileset_tile_height(tileset) * map_zoom;
2741 int w = mapview.store_width, h = mapview.store_height;
2743 /* If the mapview window is too large, cached drawing is not possible.
2745 * BACKGROUND: cached drawing occurrs when the mapview is scrolled just
2746 * a short distance. The majority of the mapview window can simply be
2747 * copied while the newly visible areas must be drawn from scratch. This
2748 * speeds up drawing significantly, especially when using the scrollbars
2749 * or mapview sliding.
2751 * When the mapview is larger than the map, however, some tiles may become
2752 * visible twice. In this case one instance of the tile will be drawn
2753 * while all others are drawn black. When this happens the cached drawing
2754 * system breaks since it assumes the mapview canvas is an "ideal" window
2755 * over the map. So black tiles may be scrolled from the edges of the
2756 * mapview into the center, while drawn tiles may be scrolled from the
2757 * center of the mapview out to the edges. The result is very bad.
2759 * There are a few different ways this could be solved. One way is simply
2760 * to turn off cached drawing, which is what we do now. If the mapview
2761 * window gets to be too large, the caching is disabled. Another would
2762 * be to prevent the window from getting too large in the first place -
2763 * but because the window boundaries aren't at an even tile this would
2764 * mean the entire map could never be shown. Yet another way would be
2765 * to draw tiles more than once if they are visible in multiple locations
2766 * on the mapview.
2768 * The logic below is complicated and determined in part by
2769 * trial-and-error. */
2770 if (!current_topo_has_flag(TF_WRAPX) && !current_topo_has_flag(TF_WRAPY)) {
2771 /* An unwrapping map: no limitation. On an unwrapping map no tile can
2772 * be visible twice so there's no problem. */
2773 return TRUE;
2775 if (XOR(current_topo_has_flag(TF_ISO) || current_topo_has_flag(TF_HEX),
2776 tileset_is_isometric(tileset))) {
2777 /* Non-matching. In this case the mapview does not line up with the
2778 * map's axis of wrapping. This will give very bad results for the
2779 * player!
2780 * We can never show more than half of the map.
2782 * We divide by 4 below because we have to divide by 2 twice. The
2783 * first division by 2 is because the square must be half the size
2784 * of the (width+height). The second division by two is because for
2785 * an iso-map, NATURAL_XXX has a scale of 2, whereas for iso-view
2786 * NORMAL_TILE_XXX has a scale of 2. */
2787 return (w <= (NATURAL_WIDTH + NATURAL_HEIGHT) * W / 4
2788 && h <= (NATURAL_WIDTH + NATURAL_HEIGHT) * H / 4);
2789 } else {
2790 /* Matching. */
2791 const int isofactor = (tileset_is_isometric(tileset) ? 2 : 1);
2792 const int isodiff = (tileset_is_isometric(tileset) ? 6 : 2);
2794 /* Now we can use the full width and height, with the exception of a small
2795 * area on each side. */
2796 if (current_topo_has_flag(TF_WRAPX)
2797 && w > (NATURAL_WIDTH - isodiff) * W / isofactor) {
2798 return FALSE;
2800 if (current_topo_has_flag(TF_WRAPY)
2801 && h > (NATURAL_HEIGHT - isodiff) * H / isofactor) {
2802 return FALSE;
2804 return TRUE;
2808 /**************************************************************************
2809 Called when we receive map dimensions. It initialized the mapview
2810 decorations.
2811 **************************************************************************/
2812 void mapdeco_init(void)
2814 /* HACK: this must be called on a map_info packet. */
2815 mapview.can_do_cached_drawing = can_do_cached_drawing();
2817 mapdeco_free();
2818 mapdeco_highlight_table = tile_hash_new();
2819 mapdeco_crosshair_table = tile_hash_new();
2820 mapdeco_gotoline_table = gotoline_hash_new();
2823 /**************************************************************************
2824 Free all memory used for map decorations.
2825 **************************************************************************/
2826 void mapdeco_free(void)
2828 if (mapdeco_highlight_table) {
2829 tile_hash_destroy(mapdeco_highlight_table);
2830 mapdeco_highlight_table = NULL;
2832 if (mapdeco_crosshair_table) {
2833 tile_hash_destroy(mapdeco_crosshair_table);
2834 mapdeco_crosshair_table = NULL;
2836 if (mapdeco_gotoline_table) {
2837 gotoline_hash_destroy(mapdeco_gotoline_table);
2838 mapdeco_gotoline_table = NULL;
2842 /**************************************************************************
2843 Set the given tile's map decoration as either highlighted or not,
2844 depending on the value of 'highlight'.
2845 **************************************************************************/
2846 void mapdeco_set_highlight(const struct tile *ptile, bool highlight)
2848 bool changed = FALSE;
2849 if (!ptile || !mapdeco_highlight_table) {
2850 return;
2853 if (highlight) {
2854 changed = tile_hash_insert(mapdeco_highlight_table, ptile, NULL);
2855 } else {
2856 changed = tile_hash_remove(mapdeco_highlight_table, ptile);
2859 if (changed) {
2860 /* FIXME: Remove the cast. */
2861 refresh_tile_mapcanvas((struct tile *) ptile, TRUE, FALSE);
2865 /**************************************************************************
2866 Return TRUE if the given tile is highlighted.
2867 **************************************************************************/
2868 bool mapdeco_is_highlight_set(const struct tile *ptile)
2870 if (!ptile || !mapdeco_highlight_table) {
2871 return FALSE;
2873 return tile_hash_lookup(mapdeco_highlight_table, ptile, NULL);
2876 /**************************************************************************
2877 Clears all highlighting. Marks the previously highlighted tiles as
2878 needing a mapview update.
2879 **************************************************************************/
2880 void mapdeco_clear_highlights(void)
2882 if (!mapdeco_highlight_table) {
2883 return;
2886 tile_hash_iterate(mapdeco_highlight_table, ptile) {
2887 refresh_tile_mapcanvas(ptile, TRUE, FALSE);
2888 } tile_hash_iterate_end;
2890 tile_hash_clear(mapdeco_highlight_table);
2893 /**************************************************************************
2894 Marks the given tile as having a "crosshair" map decoration.
2895 **************************************************************************/
2896 void mapdeco_set_crosshair(const struct tile *ptile, bool crosshair)
2898 bool changed;
2900 if (!mapdeco_crosshair_table || !ptile) {
2901 return;
2904 if (crosshair) {
2905 changed = tile_hash_insert(mapdeco_crosshair_table, ptile, NULL);
2906 } else {
2907 changed = tile_hash_remove(mapdeco_crosshair_table, ptile);
2910 if (changed) {
2911 /* FIXME: Remove the cast. */
2912 refresh_tile_mapcanvas((struct tile *) ptile, FALSE, FALSE);
2916 /**************************************************************************
2917 Returns TRUE if there is a "crosshair" decoration set at the given tile.
2918 **************************************************************************/
2919 bool mapdeco_is_crosshair_set(const struct tile *ptile)
2921 if (!mapdeco_crosshair_table || !ptile) {
2922 return FALSE;
2924 return tile_hash_lookup(mapdeco_crosshair_table, ptile, NULL);
2927 /**************************************************************************
2928 Clears all previous set tile crosshair decorations. Marks the affected
2929 tiles as needing a mapview update.
2930 **************************************************************************/
2931 void mapdeco_clear_crosshairs(void)
2933 if (!mapdeco_crosshair_table) {
2934 return;
2937 tile_hash_iterate(mapdeco_crosshair_table, ptile) {
2938 refresh_tile_mapcanvas(ptile, FALSE, FALSE);
2939 } tile_hash_iterate_end;
2941 tile_hash_clear(mapdeco_crosshair_table);
2944 /**************************************************************************
2945 Add a goto line from the given tile 'ptile' in the direction 'dir'. If
2946 there was no previously drawn line there, a mapview update is queued
2947 for the source and destination tiles.
2948 **************************************************************************/
2949 void mapdeco_add_gotoline(const struct tile *ptile, enum direction8 dir)
2951 struct gotoline_counter *pglc;
2952 const struct tile *ptile_dest;
2953 bool changed;
2955 if (!mapdeco_gotoline_table || !ptile
2956 || !(0 <= dir && dir <= direction8_max())) {
2957 return;
2959 ptile_dest = mapstep(ptile, dir);
2960 if (!ptile_dest) {
2961 return;
2964 if (!gotoline_hash_lookup(mapdeco_gotoline_table, ptile, &pglc)) {
2965 pglc = gotoline_counter_new();
2966 gotoline_hash_insert(mapdeco_gotoline_table, ptile, pglc);
2968 changed = (pglc->line_count[dir] < 1);
2969 pglc->line_count[dir]++;
2971 if (changed) {
2972 /* FIXME: Remove cast. */
2973 refresh_tile_mapcanvas((struct tile *) ptile, FALSE, FALSE);
2974 refresh_tile_mapcanvas((struct tile *) ptile_dest, FALSE, FALSE);
2978 /**************************************************************************
2979 Removes a goto line from the given tile 'ptile' going in the direction
2980 'dir'. If this was the last line there, a mapview update is queued to
2981 erase the drawn line.
2982 **************************************************************************/
2983 void mapdeco_remove_gotoline(const struct tile *ptile,
2984 enum direction8 dir)
2986 struct gotoline_counter *pglc;
2987 bool changed = FALSE;
2989 if (!mapdeco_gotoline_table || !ptile
2990 || !(0 <= dir && dir <= direction8_max())) {
2991 return;
2994 if (!gotoline_hash_lookup(mapdeco_gotoline_table, ptile, &pglc)) {
2995 return;
2998 pglc->line_count[dir]--;
2999 if (pglc->line_count[dir] <= 0) {
3000 pglc->line_count[dir] = 0;
3001 changed = TRUE;
3004 if (changed) {
3005 /* FIXME: Remove the casts. */
3006 refresh_tile_mapcanvas((struct tile *) ptile, FALSE, FALSE);
3007 ptile = mapstep(ptile, dir);
3008 if (ptile != NULL) {
3009 refresh_tile_mapcanvas((struct tile *) ptile, FALSE, FALSE);
3014 /**************************************************************************
3015 Set the map decorations for the given unit's goto route. A goto route
3016 consists of one or more goto lines, with each line being from the center
3017 of one tile to the center of another tile.
3018 **************************************************************************/
3019 void mapdeco_set_gotoroute(const struct unit *punit)
3021 const struct unit_order *porder;
3022 const struct tile *ptile;
3023 int i, ind;
3025 if (!punit || !unit_tile(punit) || !unit_has_orders(punit)
3026 || punit->orders.length < 1) {
3027 return;
3030 ptile = unit_tile(punit);
3032 for (i = 0; ptile != NULL && i < punit->orders.length; i++) {
3033 if (punit->orders.index + i >= punit->orders.length
3034 && !punit->orders.repeat) {
3035 break;
3038 ind = (punit->orders.index + i) % punit->orders.length;
3039 porder = &punit->orders.list[ind];
3040 if (porder->order != ORDER_MOVE) {
3041 /* FIXME: should display some indication of non-move orders here. */
3042 continue;
3045 mapdeco_add_gotoline(ptile, porder->dir);
3046 ptile = mapstep(ptile, porder->dir);
3050 /**************************************************************************
3051 Returns TRUE if a goto line should be drawn from the given tile in the
3052 given direction.
3053 **************************************************************************/
3054 bool mapdeco_is_gotoline_set(const struct tile *ptile,
3055 enum direction8 dir)
3057 struct gotoline_counter *pglc;
3059 if (!ptile || !(0 <= dir && dir <= direction8_max())
3060 || !mapdeco_gotoline_table) {
3061 return FALSE;
3064 if (!gotoline_hash_lookup(mapdeco_gotoline_table, ptile, &pglc)) {
3065 return FALSE;
3068 return pglc->line_count[dir] > 0;
3071 /**************************************************************************
3072 Clear all goto line map decorations and queues mapview updates for the
3073 affected tiles.
3074 **************************************************************************/
3075 void mapdeco_clear_gotoroutes(void)
3077 if (!mapdeco_gotoline_table) {
3078 return;
3081 gotoline_hash_iterate(mapdeco_gotoline_table, ptile, pglc) {
3082 refresh_tile_mapcanvas(ptile, FALSE, FALSE);
3083 adjc_dir_iterate(ptile, ptile_dest, dir) {
3084 if (pglc->line_count[dir] > 0) {
3085 refresh_tile_mapcanvas(ptile_dest, FALSE, FALSE);
3087 } adjc_dir_iterate_end;
3088 } gotoline_hash_iterate_end;
3089 gotoline_hash_clear(mapdeco_gotoline_table);
3092 /**************************************************************************
3093 Called if the map in the GUI is resized.
3095 Returns TRUE iff the canvas was redrawn.
3096 **************************************************************************/
3097 bool map_canvas_resized(int width, int height)
3099 int old_tile_width = mapview.tile_width;
3100 int old_tile_height = mapview.tile_height;
3101 int old_width = mapview.width, old_height = mapview.height;
3102 int tile_width = (width + tileset_tile_width(tileset) * map_zoom - 1) /
3103 (tileset_tile_width(tileset) * map_zoom);
3104 int tile_height = (height + tileset_tile_height(tileset) * map_zoom - 1) /
3105 (tileset_tile_height(tileset) * map_zoom);
3106 int full_width = tile_width * tileset_tile_width(tileset) * map_zoom;
3107 int full_height = tile_height * tileset_tile_height(tileset) * map_zoom;
3108 bool tile_size_changed, size_changed, redrawn = FALSE;
3110 /* Resized */
3112 /* Since a resize is only triggered when the tile_*** changes, the canvas
3113 * width and height must include the entire backing store - otherwise
3114 * small resizings may lead to undrawn tiles. */
3115 mapview.tile_width = tile_width;
3116 mapview.tile_height = tile_height;
3117 mapview.width = width;
3118 mapview.height = height;
3119 mapview.store_width = full_width;
3120 mapview.store_height = full_height;
3122 /* Check for what's changed. */
3123 tile_size_changed = (tile_width != old_tile_width
3124 || tile_height != old_tile_height);
3125 size_changed = (width != old_width || height != old_height);
3127 /* If the tile size has changed, resize the canvas. */
3128 if (tile_size_changed) {
3129 if (mapview.store) {
3130 canvas_free(mapview.store);
3131 canvas_free(mapview.tmp_store);
3133 mapview.store = canvas_create(full_width, full_height);
3134 canvas_set_zoom(mapview.store, map_zoom);
3135 canvas_put_rectangle(mapview.store,
3136 get_color(tileset, COLOR_MAPVIEW_UNKNOWN),
3137 0, 0, full_width, full_height);
3139 mapview.tmp_store = canvas_create(full_width, full_height);
3140 canvas_set_zoom(mapview.tmp_store, map_zoom);
3143 if (!map_is_empty() && can_client_change_view()) {
3144 if (tile_size_changed) {
3145 if (center_tile != NULL) {
3146 int x_left, y_top;
3147 float gui_x, gui_y;
3149 index_to_map_pos(&x_left, &y_top, tile_index(center_tile));
3150 map_to_gui_pos(tileset, &gui_x, &gui_y, x_left, y_top);
3152 /* Put the center pixel of the tile at the exact center of the mapview. */
3153 gui_x -= (mapview.width - tileset_tile_width(tileset) * map_zoom) / 2;
3154 gui_y -= (mapview.height - tileset_tile_height(tileset) * map_zoom) / 2;
3156 calc_mapview_origin(&gui_x, &gui_y);
3157 mapview.gui_x0 = gui_x;
3158 mapview.gui_y0 = gui_y;
3160 update_map_canvas_visible();
3161 center_tile_overviewcanvas();
3162 unqueue_mapview_updates(TRUE);
3163 redrawn = TRUE;
3166 /* If the width/height has changed, update the scrollbars even if
3167 * the backing store is not resized. */
3168 if (size_changed) {
3169 update_map_canvas_scrollbars_size();
3170 update_map_canvas_scrollbars();
3174 mapview.can_do_cached_drawing = can_do_cached_drawing();
3176 return redrawn;
3179 /**************************************************************************
3180 Sets up data for the mapview and overview.
3181 **************************************************************************/
3182 void init_mapcanvas_and_overview(void)
3184 /* Create a dummy map to make sure mapview.store is never NULL. */
3185 map_canvas_resized(1, 1);
3188 /**************************************************************************
3189 Frees resources allocated for mapview and overview
3190 **************************************************************************/
3191 void free_mapcanvas_and_overview(void)
3193 canvas_free(mapview.store);
3194 canvas_free(mapview.tmp_store);
3197 /****************************************************************************
3198 Return the desired width of the spaceship canvas.
3199 ****************************************************************************/
3200 void get_spaceship_dimensions(int *width, int *height)
3202 struct sprite *sprite
3203 = get_spaceship_sprite(tileset, SPACESHIP_HABITATION);
3205 get_sprite_dimensions(sprite, width, height);
3206 *width *= 7;
3207 *height *= 7;
3210 /****************************************************************************
3211 Draw the spaceship onto the canvas.
3212 ****************************************************************************/
3213 void put_spaceship(struct canvas *pcanvas, int canvas_x, int canvas_y,
3214 const struct player *pplayer)
3216 int i, x, y;
3217 const struct player_spaceship *ship = &pplayer->spaceship;
3218 int w, h;
3219 struct sprite *spr;
3220 struct tileset *t = tileset;
3222 spr = get_spaceship_sprite(t, SPACESHIP_HABITATION);
3223 get_sprite_dimensions(spr, &w, &h);
3225 canvas_put_rectangle(pcanvas,
3226 get_color(tileset, COLOR_SPACESHIP_BACKGROUND),
3227 0, 0, w * 7, h * 7);
3229 for (i = 0; i < NUM_SS_MODULES; i++) {
3230 const int j = i / 3;
3231 const int k = i % 3;
3233 if ((k == 0 && j >= ship->habitation)
3234 || (k == 1 && j >= ship->life_support)
3235 || (k == 2 && j >= ship->solar_panels)) {
3236 continue;
3238 x = modules_info[i].x * w / 4 - w / 2;
3239 y = modules_info[i].y * h / 4 - h / 2;
3241 spr = (k == 0 ? get_spaceship_sprite(t, SPACESHIP_HABITATION)
3242 : k == 1 ? get_spaceship_sprite(t, SPACESHIP_LIFE_SUPPORT)
3243 : get_spaceship_sprite(t, SPACESHIP_SOLAR_PANEL));
3244 canvas_put_sprite_full(pcanvas, x, y, spr);
3247 for (i = 0; i < NUM_SS_COMPONENTS; i++) {
3248 const int j = i / 2;
3249 const int k = i % 2;
3251 if ((k == 0 && j >= ship->fuel)
3252 || (k == 1 && j >= ship->propulsion)) {
3253 continue;
3255 x = components_info[i].x * w / 4 - w / 2;
3256 y = components_info[i].y * h / 4 - h / 2;
3258 spr = ((k == 0) ? get_spaceship_sprite(t, SPACESHIP_FUEL)
3259 : get_spaceship_sprite(t, SPACESHIP_PROPULSION));
3261 canvas_put_sprite_full(pcanvas, x, y, spr);
3263 if (k && ship->state == SSHIP_LAUNCHED) {
3264 spr = get_spaceship_sprite(t, SPACESHIP_EXHAUST);
3265 canvas_put_sprite_full(pcanvas, x + w, y, spr);
3269 for (i = 0; i < NUM_SS_STRUCTURALS; i++) {
3270 if (!BV_ISSET(ship->structure, i)) {
3271 continue;
3273 x = structurals_info[i].x * w / 4 - w / 2;
3274 y = structurals_info[i].y * h / 4 - h / 2;
3276 spr = get_spaceship_sprite(t, SPACESHIP_STRUCTURAL);
3277 canvas_put_sprite_full(pcanvas, x, y, spr);
3281 /****************************************************************************
3282 Map link mark module: it makes link marks when a link is sent by chating,
3283 or restore a mark with clicking a link on the chatline.
3284 ****************************************************************************/
3285 struct link_mark {
3286 enum text_link_type type; /* The target type. */
3287 int id; /* The city or unit id, or tile index. */
3288 int turn_counter; /* The turn counter before it disappears. */
3291 #define SPECLIST_TAG link_mark
3292 #define SPECLIST_TYPE struct link_mark
3293 #include "speclist.h"
3294 #define link_marks_iterate(pmark) \
3295 TYPED_LIST_ITERATE(struct link_mark, link_marks, pmark)
3296 #define link_marks_iterate_end LIST_ITERATE_END
3298 static struct link_mark_list *link_marks = NULL;
3300 /**********************************************************************
3301 Find a link mark in the list.
3302 ***********************************************************************/
3303 static struct link_mark *link_mark_find(enum text_link_type type, int id)
3305 link_marks_iterate(pmark) {
3306 if (pmark->type == type && pmark->id == id) {
3307 return pmark;
3309 } link_marks_iterate_end;
3311 return NULL;
3314 /**********************************************************************
3315 Create a new link mark.
3316 ***********************************************************************/
3317 static struct link_mark *link_mark_new(enum text_link_type type,
3318 int id, int turns)
3320 struct link_mark *pmark = fc_malloc(sizeof(struct link_mark));
3322 pmark->type = type;
3323 pmark->id = id;
3324 pmark->turn_counter = turns;
3326 return pmark;
3329 /**********************************************************************
3330 Remove a link mark.
3331 ***********************************************************************/
3332 static void link_mark_destroy(struct link_mark *pmark)
3334 free(pmark);
3337 /**********************************************************************
3338 Returns the location of the pointed mark.
3339 ***********************************************************************/
3340 static struct tile *link_mark_tile(const struct link_mark *pmark)
3342 switch (pmark->type) {
3343 case TLT_CITY:
3345 struct city *pcity = game_city_by_number(pmark->id);
3346 return pcity ? pcity->tile : NULL;
3348 case TLT_TILE:
3349 return index_to_tile(&(wld.map), pmark->id);
3350 case TLT_UNIT:
3352 struct unit *punit = game_unit_by_number(pmark->id);
3353 return punit ? unit_tile(punit) : NULL;
3356 return NULL;
3359 /**********************************************************************
3360 Returns the color of the pointed mark.
3361 ***********************************************************************/
3362 static struct color *link_mark_color(const struct link_mark *pmark)
3364 switch (pmark->type) {
3365 case TLT_CITY:
3366 return get_color(tileset, COLOR_MAPVIEW_CITY_LINK);
3367 case TLT_TILE:
3368 return get_color(tileset, COLOR_MAPVIEW_TILE_LINK);
3369 case TLT_UNIT:
3370 return get_color(tileset, COLOR_MAPVIEW_UNIT_LINK);
3372 return NULL;
3375 /**********************************************************************
3376 Print a link mark.
3377 ***********************************************************************/
3378 static void link_mark_draw(const struct link_mark *pmark)
3380 int width = tileset_tile_width(tileset) * map_zoom;
3381 int height = tileset_tile_height(tileset) * map_zoom;
3382 int xd = width / 20, yd = height / 20;
3383 int xlen = width / 3, ylen = height / 3;
3384 float canvas_x, canvas_y;
3385 int x_left, x_right, y_top, y_bottom;
3386 struct tile *ptile = link_mark_tile(pmark);
3387 struct color *pcolor = link_mark_color(pmark);
3389 if (!ptile || !tile_to_canvas_pos(&canvas_x, &canvas_y, ptile)) {
3390 return;
3393 x_left = canvas_x + xd;
3394 x_right = canvas_x + width - xd;
3395 y_top = canvas_y + yd;
3396 y_bottom = canvas_y + height - yd;
3398 canvas_put_line(mapview.store, pcolor, LINE_TILE_FRAME, x_left, y_top, xlen, 0);
3399 canvas_put_line(mapview.store, pcolor, LINE_TILE_FRAME, x_left, y_top, 0, ylen);
3401 canvas_put_line(mapview.store, pcolor, LINE_TILE_FRAME, x_right, y_top, -xlen, 0);
3402 canvas_put_line(mapview.store, pcolor, LINE_TILE_FRAME, x_right, y_top, 0, ylen);
3404 canvas_put_line(mapview.store, pcolor, LINE_TILE_FRAME, x_left, y_bottom, xlen, 0);
3405 canvas_put_line(mapview.store, pcolor, LINE_TILE_FRAME, x_left, y_bottom, 0, -ylen);
3407 canvas_put_line(mapview.store, pcolor, LINE_TILE_FRAME, x_right, y_bottom, -xlen, 0);
3408 canvas_put_line(mapview.store, pcolor, LINE_TILE_FRAME, x_right, y_bottom, 0, -ylen);
3411 /**********************************************************************
3412 Initialize the link marks.
3413 ***********************************************************************/
3414 void link_marks_init(void)
3416 if (link_marks) {
3417 link_marks_free();
3420 link_marks = link_mark_list_new_full(link_mark_destroy);
3423 /**********************************************************************
3424 Free the link marks.
3425 ***********************************************************************/
3426 void link_marks_free(void)
3428 if (!link_marks) {
3429 return;
3432 link_mark_list_destroy(link_marks);
3433 link_marks = NULL;
3436 /**********************************************************************
3437 Draw all link marks.
3438 ***********************************************************************/
3439 void link_marks_draw_all(void)
3441 link_marks_iterate(pmark) {
3442 link_mark_draw(pmark);
3443 } link_marks_iterate_end;
3446 /**********************************************************************
3447 Clear all visible links.
3448 ***********************************************************************/
3449 void link_marks_clear_all(void)
3451 link_mark_list_clear(link_marks);
3452 update_map_canvas_visible();
3455 /**********************************************************************
3456 Clear all visible links.
3457 ***********************************************************************/
3458 void link_marks_decrease_turn_counters(void)
3460 link_marks_iterate(pmark) {
3461 if (--pmark->turn_counter <= 0) {
3462 link_mark_list_remove(link_marks, pmark);
3464 } link_marks_iterate_end;
3466 /* update_map_canvas_visible(); not needed here. */
3469 /**********************************************************************
3470 Add a visible link for 2 turns.
3471 ***********************************************************************/
3472 void link_mark_add_new(enum text_link_type type, int id)
3474 struct link_mark *pmark = link_mark_find(type, id);
3475 struct tile *ptile;
3477 if (pmark) {
3478 /* Already displayed, but maybe increase the turn counter. */
3479 pmark->turn_counter = MAX(pmark->turn_counter, 2);
3480 return;
3483 pmark = link_mark_new(type, id, 2);
3484 link_mark_list_append(link_marks, pmark);
3485 ptile = link_mark_tile(pmark);
3486 if (ptile && tile_visible_mapcanvas(ptile)) {
3487 refresh_tile_mapcanvas(ptile, FALSE, FALSE);
3491 /**********************************************************************
3492 Add a visible link for 1 turn.
3493 ***********************************************************************/
3494 void link_mark_restore(enum text_link_type type, int id)
3496 struct link_mark *pmark;
3497 struct tile *ptile;
3499 if (link_mark_find(type, id)) {
3500 return;
3503 pmark = link_mark_new(type, id, 1);
3504 link_mark_list_append(link_marks, pmark);
3505 ptile = link_mark_tile(pmark);
3506 if (ptile && tile_visible_mapcanvas(ptile)) {
3507 refresh_tile_mapcanvas(ptile, FALSE, FALSE);
3511 /**********************************************************************
3512 Are the topology and tileset compatible?
3513 ***********************************************************************/
3514 bool tileset_map_topo_compatible(int topology_id, struct tileset *tset)
3516 int tileset_topology;
3518 if (tileset_hex_width(tset) > 0) {
3519 fc_assert(tileset_is_isometric(tset));
3520 tileset_topology = TF_HEX | TF_ISO;
3521 } else if (tileset_hex_height(tset) > 0) {
3522 fc_assert(tileset_is_isometric(tset));
3523 tileset_topology = TF_HEX;
3524 } else if (tileset_is_isometric(tset)) {
3525 tileset_topology = TF_ISO;
3526 } else {
3527 tileset_topology = 0;
3530 return ((topology_id & (TF_HEX | TF_ISO)) == tileset_topology);