Freeciv-3.4
Loading...
Searching...
No Matches
mapview_common.c
Go to the documentation of this file.
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.
7
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***********************************************************************/
13
14#ifdef HAVE_CONFIG_H
15#include <fc_config.h>
16#endif
17
18/* utility */
19#include "fcintl.h"
20#include "log.h"
21#include "rand.h"
22#include "support.h"
23#include "timing.h"
24
25/* common */
26#include "featured_text.h"
27#include "game.h"
28#include "map.h"
29#include "traderoutes.h"
30#include "unitlist.h"
31
32/* client/include */
33#include "graphics_g.h"
34#include "gui_main_g.h"
35#include "mapctrl_g.h"
36#include "mapview_g.h"
37
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 "svgflag.h"
47#include "tilespec.h"
48#include "zoom.h"
49
50#include "mapview_common.h"
51
52
53struct tile_hash *mapdeco_highlight_table;
54struct tile_hash *mapdeco_crosshair_table;
55
59
60static inline struct gotoline_counter *gotoline_counter_new(void);
62
63#define SPECHASH_TAG gotoline
64#define SPECHASH_IKEY_TYPE struct tile *
65#define SPECHASH_IDATA_TYPE struct gotoline_counter *
66#define SPECHASH_IDATA_FREE gotoline_counter_destroy
67#include "spechash.h"
68#define gotoline_hash_iterate(hash, ptile, pglc) \
69 TYPED_HASH_ITERATE(struct tile *, struct gotoline_counter *, \
70 hash, ptile, pglc)
71#define gotoline_hash_iterate_end HASH_ITERATE_END
72
74
77
79
80const struct tile *center_tile = nullptr;
81
82struct tile *infratile = nullptr;
83
84static void base_canvas_to_map_pos(float zoom, int *map_x, int *map_y,
85 float canvas_x, float canvas_y);
86
87static void show_full_citybar(struct canvas *pcanvas,
88 const int canvas_x0, const int canvas_y0,
89 struct city *pcity, int *width, int *height)
90 fc__attribute((nonnull(5, 6)));
91static void show_city_desc(struct canvas *pcanvas,
92 int canvas_x, int canvas_y,
93 struct city *pcity, int *width, int *height)
94 fc__attribute((nonnull(5, 6)));
95
103
104/* A tile update has a tile associated with it as well as an area type.
105 * See unqueue_mapview_updates() for a thorough explanation. */
115
116static void queue_mapview_update(enum update_type update);
117static void queue_mapview_tile_update(struct tile *ptile,
119
120/* Helper struct for drawing trade routes. */
122 float x, y, width, height;
123};
124
125/* A trade route line might need to be drawn in two parts. */
126static const int MAX_TRADE_ROUTE_DRAW_LINES = 2;
127
128static struct timer *anim_timer = nullptr;
129
131
133{
135 int id;
137 int old_x;
138 int old_y;
139 int width;
141 union {
142 struct {
143 struct unit *mover;
144 struct tile *src;
145 struct tile *dest;
149 struct {
158 int steps;
160 struct {
161 struct tile *tile;
162 const struct sprite_vector *sprites;
165 struct {
166 bool shown;
169 };
170};
171
172#define SPECLIST_TAG animation
173#define SPECLIST_TYPE struct animation
174#include "speclist.h"
175
176struct animation_list *animations = nullptr;
177
178/************************************************************************/
182{
184}
185
186/************************************************************************/
190{
191 if (animations != nullptr) {
192 int i;
193 size_t last = animation_list_size(animations);
194
195 for (i = 0; i < last; i++) {
197
198 switch (anim->type) {
199 case ANIM_MOVEMENT:
200 unit_virtual_destroy(anim->movement.mover);
201 break;
202 case ANIM_BATTLE:
203 unit_virtual_destroy(anim->battle.virt_winner);
204 unit_virtual_destroy(anim->battle.virt_loser);
205 break;
206 case ANIM_EXPL:
207 case ANIM_NUKE:
208 /* Nothing to free */
209 break;
210 }
211
212 free(anim);
213 }
214
216 animations = nullptr;
217 }
218}
219
220/************************************************************************/
223static void anim_timer_renew(void)
224{
226 anim_timer != nullptr ? nullptr : "anim");
228}
229
230/************************************************************************/
233static void animation_add(struct animation *anim)
234{
237 }
238
239 anim->finished = FALSE;
240 anim->old_x = -1; /* Initial frame */
242}
243
244/************************************************************************/
247static bool movement_animation(struct animation *anim, double time_gone)
248{
249 float start_x, start_y;
250 int new_x, new_y;
252 double mytime = MIN(time_gone, timing_sec);
253 struct unit *punit = anim->movement.mover;
254
255 if (punit != nullptr) {
256 tile_to_canvas_pos(&start_x, &start_y, map_zoom, anim->movement.src);
260 }
261 new_x = start_x + anim->movement.canvas_dx * (mytime / timing_sec);
262 new_y = start_y + anim->movement.canvas_dy * (mytime / timing_sec);
263
264 if (anim->old_x >= 0) {
265 update_map_canvas(anim->old_x, anim->old_y,
266 anim->width, anim->height);
267 }
269 dirty_rect(new_x, new_y, anim->width, anim->height);
270 anim->old_x = new_x;
271 anim->old_y = new_y;
272
273 if (time_gone >= timing_sec) {
274 /* Animation over */
275 unit_virtual_destroy(anim->movement.mover);
276
277 return TRUE;
278 }
279 } else {
280 return TRUE;
281 }
282
283 return FALSE;
284}
285
286/************************************************************************/
289static bool battle_animation(struct animation *anim, double time_gone)
290{
291 double time_per_step;
292 int step;
293 float canvas_x, canvas_y;
295 * anim->battle.steps / 1000.0;
296
297 if (time_gone >= timing_sec) {
298 /* Animation over */
299
300 unit_virtual_destroy(anim->battle.virt_winner);
301 unit_virtual_destroy(anim->battle.virt_loser);
302
303 return TRUE;
304 }
305
306 time_per_step = timing_sec / anim->battle.steps;
308
309 if (tile_to_canvas_pos(&canvas_x, &canvas_y, map_zoom, anim->battle.loser_tile)) {
310 anim->battle.virt_loser->hp
311 = anim->battle.loser_hp_start - ((anim->battle.loser_hp_start
312 - anim->battle.loser_hp_end)
313 * step / anim->battle.steps);
314
318 }
319
320 put_unit(anim->battle.virt_loser, mapview.store, map_zoom,
325 }
326
327 if (tile_to_canvas_pos(&canvas_x, &canvas_y, map_zoom, anim->battle.winner_tile)) {
328 anim->battle.virt_winner->hp
329 = anim->battle.winner_hp_start - ((anim->battle.winner_hp_start
330 - anim->battle.winner_hp_end)
331 * step / anim->battle.steps);
332
336 }
337 put_unit(anim->battle.virt_winner, mapview.store, map_zoom,
342 }
343
344 return FALSE;
345}
346
347/************************************************************************/
350static bool explosion_animation(struct animation *anim, double time_gone)
351{
352 float canvas_x, canvas_y;
353 double timing_sec;
354
355 if (anim->expl.sprite_count <= 0) {
356 return TRUE;
357 }
358
360 * anim->expl.sprite_count / 1000.0;
361
362 if (tile_to_canvas_pos(&canvas_x, &canvas_y, map_zoom, anim->expl.tile)) {
363 double time_per_frame = timing_sec / anim->expl.sprite_count;
364 int frame = time_gone / time_per_frame;
365 struct sprite *spr;
366 int w, h;
367
368 frame = MIN(frame, anim->expl.sprite_count - 1);
369
370 if (anim->old_x >= 0) {
371 update_map_canvas(anim->old_x, anim->old_y,
372 anim->width, anim->height);
373 }
374
375 spr = *sprite_vector_get(anim->expl.sprites, frame);
376 get_sprite_dimensions(spr, &w, &h);
377
380 - w / 2,
382 - h / 2,
383 spr);
386
387 anim->old_x = canvas_x;
388 anim->old_y = canvas_y;
389 }
390
391 if (time_gone >= timing_sec) {
392 /* Animation over */
393 return TRUE;
394 }
395
396 return FALSE;
397}
398
399/************************************************************************/
402static bool nuke_animation(struct animation *anim, double time_gone)
403{
404 if (!anim->nuke.shown) {
405 float canvas_x, canvas_y;
407 int w, h;
408
409 (void) tile_to_canvas_pos(&canvas_x, &canvas_y, map_zoom, anim->nuke.nuke_tile);
411
414 - w / 2,
416 - h / 2,
417 nuke_spr);
420
421 anim->old_x = canvas_x;
422 anim->old_y = canvas_y;
423
424 anim->nuke.shown = TRUE;
425
426 return FALSE;
427 }
428
429 if (time_gone > 1.0) {
431
432 return TRUE;
433 }
434
435 return FALSE;
436}
437
438/************************************************************************/
442{
443 if (animations != nullptr && animation_list_size(animations) > 0) {
445
446 if (anim->finished) {
447 /* Animation over */
448
449 anim->id = -1;
450 if (anim->old_x >= 0) {
451 update_map_canvas(anim->old_x, anim->old_y, anim->width, anim->height);
452 }
454 free(anim);
455
457 /* Start next */
459 }
460 } else {
462 bool finished = FALSE;
463
464 switch (anim->type) {
465 case ANIM_MOVEMENT:
467 break;
468 case ANIM_BATTLE:
470 break;
471 case ANIM_EXPL:
473 break;
474 case ANIM_NUKE:
476 }
477
478 if (finished) {
479 anim->finished = TRUE;
480 }
481 }
482 }
483}
484
485/************************************************************************/
488static inline struct gotoline_counter *gotoline_counter_new(void)
489{
490 struct gotoline_counter *pglc = fc_calloc(1, sizeof(*pglc));
491 return pglc;
492}
493
494/************************************************************************/
498{
499 fc_assert_ret(pglc != nullptr);
500
501 free(pglc);
502}
503
504/************************************************************************/
507void refresh_tile_mapcanvas(struct tile *ptile,
508 bool full_refresh, bool write_to_screen)
509{
510 if (full_refresh) {
512 } else {
514 }
515 if (write_to_screen) {
517 }
518}
519
520/************************************************************************/
523void refresh_unit_mapcanvas(struct unit *punit, struct tile *ptile,
524 bool full_refresh, bool write_to_screen)
525{
526 if (full_refresh && gui_options.draw_native) {
528 } else if (full_refresh && unit_drawn_with_city_outline(punit, TRUE)) {
530 } else {
532 }
533 if (write_to_screen) {
535 }
536}
537
538/************************************************************************/
544void refresh_city_mapcanvas(struct city *pcity, struct tile *ptile,
545 bool full_refresh, bool write_to_screen)
546{
547 if (full_refresh && (gui_options.draw_map_grid || gui_options.draw_borders)) {
549 } else {
551 }
552 if (write_to_screen) {
554 }
555}
556
557/************************************************************************/
566void map_to_gui_vector(const struct tileset *t, float zoom,
567 float *gui_dx, float *gui_dy, int map_dx, int map_dy)
568{
569 if (tileset_is_isometric(t)) {
570 /*
571 * Convert the map coordinates to isometric GUI
572 * coordinates. We'll make tile map(0,0) be the origin, and
573 * transform like this:
574 *
575 * 3
576 * 123 2 6
577 * 456 -> becomes -> 1 5 9
578 * 789 4 8
579 * 7
580 */
581 *gui_dx = (map_dx - map_dy) * tileset_tile_width(t) / 2 * zoom;
582 *gui_dy = (map_dx + map_dy) * tileset_tile_height(t) / 2 * zoom;
583 } else {
584 *gui_dx = map_dx * tileset_tile_height(t) * zoom;
585 *gui_dy = map_dy * tileset_tile_width(t) * zoom;
586 }
587}
588
589/************************************************************************/
595static void map_to_gui_pos(const struct tileset *t, float zoom,
596 float *gui_x, float *gui_y, int map_x, int map_y)
597{
598 /* Since the GUI origin is the same as the map origin we can just do a
599 * vector conversion. */
601}
602
603/************************************************************************/
610static void gui_to_map_pos(const struct tileset *t, float zoom,
611 int *map_x, int *map_y, float gui_x, float gui_y)
612{
613 const float W = tileset_tile_width(t) * zoom, H = tileset_tile_height(t) * zoom;
614 const float HH = tileset_hex_height(t) * zoom, HW = tileset_hex_width(t) * zoom;
615
616 if (HH > 0 || HW > 0) {
617 /* To handle hexagonal cases we have to revert to a less elegant method
618 * of calculation. */
619 float x, y;
620 int dx, dy;
621 int xmult, ymult, mod, compar;
622
624
625 x = DIVIDE((int)gui_x, (int)W);
626 y = DIVIDE((int)gui_y, (int)H);
627 dx = gui_x - x * W;
628 dy = gui_y - y * H;
629 fc_assert(dx >= 0 && dx < W);
630 fc_assert(dy >= 0 && dy < H);
631
632 /* Now fold so we consider only one-quarter tile. */
633 xmult = (dx >= W / 2) ? -1 : 1;
634 ymult = (dy >= H / 2) ? -1 : 1;
635 dx = (dx >= W / 2) ? (W - 1 - dx) : dx;
636 dy = (dy >= H / 2) ? (H - 1 - dy) : dy;
637
638 /* Next compare to see if we're across onto the next tile. */
639 if (HW > 0) {
640 compar = (dx - HW / 2) * (H / 2) - (H / 2 - 1 - dy) * (W / 2 - HW);
641 } else {
642 compar = (dy - HH / 2) * (W / 2) - (W / 2 - 1 - dx) * (H / 2 - HH);
643 }
644 mod = (compar < 0) ? -1 : 0;
645
646 *map_x = (x + y) + mod * (xmult + ymult) / 2;
647 *map_y = (y - x) + mod * (ymult - xmult) / 2;
648 } else if (tileset_is_isometric(t)) {
649 /* The basic operation here is a simple pi/4 rotation; however, we
650 * have to first scale because the tiles have different width and
651 * height. Mathematically, this looks like
652 * | 1/W 1/H | |x| |x`|
653 * | | | | -> | |
654 * |-1/W 1/H | |y| |y`|
655 *
656 * Where W is the tile width and H the height.
657 *
658 * In simple terms, this is
659 * map_x = [ x / W + y / H ]
660 * map_y = [ - x / W + y / H ]
661 * where [q] stands for integer part of q.
662 *
663 * Here the division is proper mathematical floating point division.
664 *
665 * We have to subtract off a half-tile in the X direction before doing
666 * the transformation. This is because, although the origin of the tile
667 * is the top-left corner of the bounding box, after the transformation
668 * the top corner of the diamond-shaped tile moves into this position.
669 *
670 * The calculation is complicated somewhat because of two things: we
671 * only use integer math, and C integer division rounds toward zero
672 * instead of rounding down.
673 *
674 * For another example of this math, see canvas_to_city_pos().
675 */
676 gui_x -= W / 2;
677 *map_x = DIVIDE((int)(gui_x * H + gui_y * W), (int)(W * H));
678 *map_y = DIVIDE((int)(gui_y * W - gui_x * H), (int)(W * H));
679 } else { /* tileset_is_isometric(t) */
680 /* We use DIVIDE so that we will get the correct result even
681 * for negative coordinates. */
682 *map_x = DIVIDE((int)gui_x, (int)W);
683 *map_y = DIVIDE((int)gui_y, (int)H);
684 }
685}
686
687/************************************************************************/
709bool tile_to_canvas_pos(float *canvas_x, float *canvas_y, float zoom,
710 const struct tile *ptile)
711{
712 int center_map_x, center_map_y, dx, dy, tile_x, tile_y;
713
714 /*
715 * First we wrap the coordinates to hopefully be within the mapview
716 * window. We do this by finding the position closest to the center
717 * of the window.
718 */
719 /* TODO: Cache the value of this position */
721 mapview.width / 2,
722 mapview.height / 2);
725 tile_y);
726
731
732 /*
733 * Finally we clip.
734 *
735 * This check is tailored to work for both iso-view and classic view.
736 * Note that (canvas_x, canvas_y) need not be aligned to a tile boundary, and
737 * that the position is at the top-left of the NORMAL (not UNIT) tile.
738 * This checks to see if _any part_ of the tile is present on the backing
739 * store. Even if it's not visible on the canvas, if it's present on the
740 * backing store we need to draw it in case the canvas is resized.
741 */
748}
749
750/************************************************************************/
754static void base_canvas_to_map_pos(float zoom, int *map_x, int *map_y,
755 float canvas_x, float canvas_y)
756{
758 canvas_x + mapview.gui_x0 / map_zoom * zoom,
759 canvas_y + mapview.gui_y0 / map_zoom * zoom);
760}
761
762/************************************************************************/
767 float zoom)
768{
769 int map_x, map_y;
770
772 if (normalize_map_pos(&(wld.map), &map_x, &map_y)) {
773 return map_pos_to_tile(&(wld.map), map_x, map_y);
774 } else {
775 return nullptr;
776 }
777}
778
779/************************************************************************/
785 float zoom)
786{
787 int map_x, map_y;
788
790
791 return nearest_real_tile(&(wld.map), map_x, map_y);
792}
793
794/************************************************************************/
798static void normalize_gui_pos(const struct tileset *t, float zoom,
799 float *gui_x, float *gui_y)
800{
802 float gui_x0, gui_y0;
803
804 /* Convert the (gui_x, gui_y) into a (map_x, map_y) plus a GUI offset
805 * from this tile. */
806 gui_to_map_pos(t, zoom, &map_x, &map_y, *gui_x, *gui_y);
807 map_to_gui_pos(t, zoom, &gui_x0, &gui_y0, map_x, map_y);
808 diff_x = *gui_x - gui_x0;
809 diff_y = *gui_y - gui_y0;
810
811 /* Perform wrapping without any realness check. It's important that
812 * we wrap even if the map position is unreal, which normalize_map_pos()
813 * doesn't necessarily do. */
817 }
820 }
822
823 /* Now convert the wrapped map position back to a GUI position and add the
824 * offset back on. */
825 map_to_gui_pos(t, zoom, gui_x, gui_y, map_x, map_y);
826 *gui_x += diff_x;
827 *gui_y += diff_y;
828}
829
830/************************************************************************/
834static void gui_distance_vector(const struct tileset *t, float zoom,
835 float *gui_dx, float *gui_dy,
836 float gui_x0, float gui_y0,
837 float gui_x1, float gui_y1)
838{
839 int map_x0, map_y0, map_x1, map_y1;
842 int map_dx, map_dy;
843
844 /* Make sure positions are canonical. Yes, this is the only way. */
845 normalize_gui_pos(t, zoom, &gui_x0, &gui_y0);
846 normalize_gui_pos(t, zoom, &gui_x1, &gui_y1);
847
848 /* Now we have to find the offset of each GUI position from its tile
849 * origin. This is complicated: it means converting to a map position and
850 * then back to the GUI position to find the tile origin, then subtracting
851 * to get the offset. */
852 gui_to_map_pos(t, zoom, &map_x0, &map_y0, gui_x0, gui_y0);
854
855 map_to_gui_pos(t, zoom, &gui_x0_base, &gui_y0_base, map_x0, map_y0);
857
858 gui_x0_diff = gui_x0 - gui_x0_base;
859 gui_y0_diff = gui_y0 - gui_y0_base;
862
863 /* Next we find the map distance vector and convert this into a GUI
864 * vector. */
865 base_map_distance_vector(&map_dx, &map_dy, map_x0, map_y0, map_x1, map_y1);
867
868 /* Finally we add on the difference in offsets to retain pixel
869 * resolution. */
872}
873
874/************************************************************************/
878static void base_set_mapview_origin(float gui_x0, float gui_y0, float zoom)
879{
880 float old_gui_x0, old_gui_y0;
881 float dx, dy;
882 const int width = mapview.width, height = mapview.height;
885
886 /* Then update everything. This does some tricky math to avoid having
887 * to do unnecessary redraws in update_map_canvas(). This makes for ugly
888 * code but speeds up the mapview by a large factor. */
889
890 /* We need to calculate the vector of movement of the mapview. So
891 * we find the GUI distance vector and then use this to calculate
892 * the original mapview origin relative to the current position. Thus
893 * if we move one tile to the left, even if this causes GUI positions
894 * to wrap the distance vector is only one tile. */
895 normalize_gui_pos(tileset, zoom, &gui_x0, &gui_y0);
896 gui_distance_vector(tileset, zoom, &dx, &dy,
898 gui_x0, gui_y0);
899 old_gui_x0 = gui_x0 - dx;
900 old_gui_y0 = gui_y0 - dy;
901
902 mapview.gui_x0 = gui_x0;
903 mapview.gui_y0 = gui_y0;
904
905 /* Find the overlapping area of the new and old mapview. This is
906 * done in GUI coordinates. Note that if the GUI coordinates wrap
907 * no overlap will be found. */
908 common_x0 = MAX(old_gui_x0, gui_x0);
909 common_x1 = MIN(old_gui_x0, gui_x0) + width;
910 common_y0 = MAX(old_gui_y0, gui_y0);
911 common_y1 = MIN(old_gui_y0, gui_y0) + height;
912
915 /* Do a partial redraw only. This means the area of overlap (a
916 * rectangle) is copied. Then the remaining areas (two rectangles)
917 * are updated through update_map_canvas(). */
918 struct canvas *target = mapview.tmp_store;
919
920 if (old_gui_x0 < gui_x0) {
921 update_x0 = MAX(old_gui_x0 + width, gui_x0);
922 update_x1 = gui_x0 + width;
923 } else {
924 update_x0 = gui_x0;
925 update_x1 = MIN(old_gui_x0, gui_x0 + width);
926 }
927 if (old_gui_y0 < gui_y0) {
928 update_y0 = MAX(old_gui_y0 + height, gui_y0);
929 update_y1 = gui_y0 + height;
930 } else {
931 update_y0 = gui_y0;
932 update_y1 = MIN(old_gui_y0, gui_y0 + height);
933 }
934
935 dirty_all();
936 canvas_copy(target, mapview.store,
939 common_x0 - gui_x0, common_y0 - gui_y0,
942 mapview.store = target;
943
944 if (update_y1 > update_y0) {
945 update_map_canvas(0, update_y0 - gui_y0,
947 }
948 if (update_x1 > update_x0) {
949 update_map_canvas(update_x0 - gui_x0, common_y0 - gui_y0,
951 }
952 } else {
953 dirty_all();
955 }
956
958
959 switch (hover_state) {
960 case HOVER_GOTO:
961 case HOVER_PATROL:
962 case HOVER_CONNECT:
964 break;
966 case HOVER_NONE:
967 case HOVER_TELEPORT:
968 case HOVER_PARADROP:
970 break;
971 }
972
973 if (rectangle_active) {
975 }
976}
977
978/************************************************************************/
982static bool calc_mapview_origin(float *gui_x0, float *gui_y0, float zoom)
983{
984 float xmin, ymin, xmax, ymax;
985 int xsize, ysize;
986
987 /* Normalize (wrap) the mapview origin. */
988 normalize_gui_pos(tileset, zoom, gui_x0, gui_y0);
989
990 /* First wrap/clip the position. Wrapping is done in native positions
991 * while clipping is done in scroll (native) positions. */
992 get_mapview_scroll_window(&xmin, &ymin, &xmax, &ymax, &xsize, &ysize);
993
995 *gui_x0 = CLIP(xmin, *gui_x0, xmax - xsize);
996 }
997
999 *gui_y0 = CLIP(ymin, *gui_y0, ymax - ysize);
1000 }
1001
1002 if (mapview.gui_x0 == *gui_x0 && mapview.gui_y0 == *gui_y0) {
1003 return FALSE;
1004 }
1005
1006 return TRUE;
1007}
1008
1009/************************************************************************/
1012void set_mapview_origin(float gui_x0, float gui_y0, float zoom)
1013{
1014 if (!calc_mapview_origin(&gui_x0, &gui_y0, zoom)) {
1015 return;
1016 }
1017
1020 /* TODO: Implement animation */
1021 base_set_mapview_origin(gui_x0, gui_y0, zoom);
1022 } else {
1023 int start_x = mapview.gui_x0, start_y = mapview.gui_y0;
1024 float diff_x, diff_y;
1026 double currtime;
1027 int frames = 0;
1028
1029 /* We track the average FPS, which is used to predict how long the
1030 * next draw will take. We start with a 100 FPS estimate - this
1031 * value will quickly become irrelevant as the correct value is
1032 * calculated, but it's needed to give an estimate of the FPS for
1033 * the first draw.
1034 *
1035 * Note that the initial value shouldn't be larger than the sliding
1036 * time, or we'll jump straight to the last frame. The FPS should
1037 * therefore be a "high" estimate. */
1038 static double total_frames = 0.01;
1039 static double total_time = 0.0001;
1040
1042 &diff_x, &diff_y, start_x, start_y, gui_x0, gui_y0);
1044
1046
1047 do {
1048 double mytime;
1049
1050 /* Get the current time, and add on the average 1/FPS, which is the
1051 * expected time this frame will take. This is done so that the
1052 * frame's position is calculated from the expected time when the
1053 * frame will complete, rather than the time when the frame drawing
1054 * is started. */
1057
1061 zoom);
1062 flush_dirty();
1063 gui_flush();
1064 frames++;
1065 } while (currtime < timing_sec);
1066
1068 total_frames += frames;
1070 log_debug("Got %d frames in %f seconds: %f FPS (avg %f).",
1071 frames, currtime, (double)frames / currtime,
1073
1074 /* A very small decay factor to make things more accurate when something
1075 * changes (mapview size, tileset change, etc.). This gives a
1076 * half-life of 68 slides. */
1077 total_frames *= 0.99;
1078 total_time *= 0.99;
1079 }
1080 } else {
1081 base_set_mapview_origin(gui_x0, gui_y0, zoom);
1082 }
1083
1085}
1086
1087/************************************************************************/
1113 float *xmax, float *ymax,
1114 int *xsize, int *ysize)
1115{
1116 int diff;
1117
1118 *xsize = mapview.width;
1119 *ysize = mapview.height;
1120
1122 /* If the map and view line up, it's easy. */
1123 NATIVE_TO_MAP_POS(xmin, ymin, 0, 0);
1125
1130
1131 /* To be able to center on positions near the edges, we have to be
1132 * allowed to scroll all the way to those edges. To allow wrapping the
1133 * clipping boundary needs to extend past the edge - a half-tile in
1134 * iso-view or a full tile in non-iso view. The above math already has
1135 * taken care of some of this so all that's left is to fix the corner
1136 * cases. */
1138 *xmax += *xsize;
1139
1140 /* We need to be able to scroll a little further to the left. */
1142 }
1144 *ymax += *ysize;
1145
1146 /* We need to be able to scroll a little further up. */
1148 }
1149 } else {
1150 /* Otherwise it's hard. Very hard. Impossible, in fact. This is just
1151 * an approximation - a huge bounding box. */
1153 int map_x, map_y;
1154
1155 NATIVE_TO_MAP_POS(&map_x, &map_y, 0, 0);
1157
1160
1163
1166
1167 *xmin = MIN(gui_x1, MIN(gui_x2, gui_x3)) - mapview.width / 2;
1169
1170 *xmax = MAX(gui_x4, MAX(gui_x2, gui_x3)) + mapview.width / 2;
1172 }
1173
1174 /* Make sure the scroll window is big enough to hold the mapview. If
1175 * not scrolling will be very ugly and the GUI may become confused. */
1176 diff = *xsize - (*xmax - *xmin);
1177 if (diff > 0) {
1178 *xmin -= diff / 2;
1179 *xmax += (diff + 1) / 2;
1180 }
1181
1182 diff = *ysize - (*ymax - *ymin);
1183 if (diff > 0) {
1184 *ymin -= diff / 2;
1185 *ymax += (diff + 1) / 2;
1186 }
1187
1188 log_debug("x: %f<-%d->%f; y: %f<-%f->%d",
1189 *xmin, *xsize, *xmax, *ymin, *ymax, *ysize);
1190}
1191
1192/************************************************************************/
1197{
1200
1202 *xstep /= 2;
1203 *ystep /= 2;
1204 }
1205}
1206
1207/************************************************************************/
1215
1216/************************************************************************/
1220{
1221 int gui_x0 = scroll_x, gui_y0 = scroll_y;
1222
1223 can_slide = FALSE;
1224 set_mapview_origin(gui_x0, gui_y0, zoom);
1225 can_slide = TRUE;
1226}
1227
1228/************************************************************************/
1232{
1234 mapview.height / 2,
1235 map_zoom);
1236}
1237
1238/************************************************************************/
1241void center_tile_mapcanvas(const struct tile *ptile)
1242{
1243 float gui_x, gui_y;
1244 int tile_x, tile_y;
1245 static bool first = TRUE;
1246
1247 if (first && can_slide) {
1248 return;
1249 }
1250 first = FALSE;
1251
1254
1255 /* Put the center pixel of the tile at the exact center of the mapview. */
1258
1260
1261 center_tile = ptile;
1262}
1263
1264/************************************************************************/
1268bool tile_visible_mapcanvas(struct tile *ptile)
1269{
1270 float dummy_x, dummy_y; /* Well, it needs two pointers... */
1271
1272 return tile_to_canvas_pos(&dummy_x, &dummy_y, map_zoom, ptile);
1273}
1274
1275/************************************************************************/
1287{
1288 float canvas_x, canvas_y;
1289 float xmin, ymin, xmax, ymax;
1290 int xsize, ysize, scroll_x, scroll_y;
1296
1297 get_mapview_scroll_window(&xmin, &ymin, &xmax, &ymax, &xsize, &ysize);
1299
1300 if (!tile_to_canvas_pos(&canvas_x, &canvas_y, map_zoom, ptile)) {
1301 /* The tile isn't visible at all. */
1302 return FALSE;
1303 }
1304
1305 /* For each direction: if the tile is too close to the mapview border
1306 * in that direction, and scrolling can get us any closer to the
1307 * border, then it's a border tile. We can only really check the
1308 * scrolling when the mapview window lines up with the map. */
1309 if (canvas_x < border_x
1311 return FALSE;
1312 }
1313 if (canvas_y < border_y
1315 return FALSE;
1316 }
1318 && (!same || scroll_x + xsize < xmax || current_wrap_has_flag(WRAP_X))) {
1319 return FALSE;
1320 }
1322 && (!same || scroll_y + ysize < ymax || current_wrap_has_flag(WRAP_Y))) {
1323 return FALSE;
1324 }
1325
1326 return TRUE;
1327}
1328
1329/************************************************************************/
1332void put_drawn_sprites(struct canvas *pcanvas, float zoom,
1333 int canvas_x, int canvas_y,
1334 int count, struct drawn_sprite *pdrawn,
1335 bool fog)
1336{
1337 int i;
1338
1339 for (i = 0; i < count; i++) {
1340 if (!pdrawn[i].sprite) {
1341 /* This can happen, although it should probably be avoided. */
1342 continue;
1343 }
1344
1345 if (pdrawn[i].w > 0) {
1346 /* TODO: Fog support */
1348 canvas_x / zoom + pdrawn[i].offset_x,
1349 canvas_y / zoom + pdrawn[i].offset_y,
1350 pdrawn[i].w / zoom,
1351 pdrawn[i].h / zoom,
1352 pdrawn[i].sprite);
1353 } else if (fog && pdrawn[i].foggable) {
1355 canvas_x / zoom + pdrawn[i].offset_x,
1356 canvas_y / zoom + pdrawn[i].offset_y,
1357 pdrawn[i].sprite,
1358 TRUE,
1360 } else {
1361 /* We avoid calling canvas_put_sprite_fogged(), even though it
1362 * should be a valid thing to do, because gui-gtk-2.0 doesn't have
1363 * a full implementation. */
1365 canvas_x / zoom + pdrawn[i].offset_x,
1366 canvas_y / zoom + pdrawn[i].offset_y,
1367 pdrawn[i].sprite);
1368 }
1369 }
1370}
1371
1372/************************************************************************/
1376void put_one_element(struct canvas *pcanvas, float zoom,
1377 enum mapview_layer layer,
1378 const struct tile *ptile,
1379 const struct tile_edge *pedge,
1380 const struct tile_corner *pcorner,
1381 const struct unit *punit, const struct city *pcity,
1382 int canvas_x, int canvas_y,
1383 const struct city *citymode,
1384 const struct unit_type *putype)
1385{
1386 struct drawn_sprite tile_sprs[80];
1387 int count = fill_sprite_array(tileset, tile_sprs, layer,
1388 ptile, pedge, pcorner,
1390 bool fog = (ptile && gui_options.draw_fog_of_war
1392
1393 /*** Draw terrain and specials ***/
1394 put_drawn_sprites(pcanvas, zoom, canvas_x, canvas_y, count, tile_sprs, fog);
1395}
1396
1397/************************************************************************/
1401void put_unit(const struct unit *punit, struct canvas *pcanvas, float zoom,
1402 int canvas_x, int canvas_y)
1403{
1404 canvas_y
1406
1407 mapview_layer_iterate(layer) {
1408 put_one_element(pcanvas, zoom, layer, nullptr, nullptr, nullptr,
1409 punit, nullptr, canvas_x, canvas_y, nullptr, nullptr);
1411}
1412
1413/************************************************************************/
1417void put_unittype(const struct unit_type *putype, struct canvas *pcanvas,
1418 float zoom, int canvas_x, int canvas_y)
1419{
1420 canvas_y
1422
1423 mapview_layer_iterate(layer) {
1424 put_one_element(pcanvas, zoom, layer, nullptr, nullptr, nullptr,
1425 nullptr, nullptr, canvas_x, canvas_y, nullptr, putype);
1427}
1428
1429/************************************************************************/
1434void put_city(struct city *pcity, struct canvas *pcanvas, float zoom,
1435 int canvas_x, int canvas_y)
1436{
1437 canvas_y
1439
1440 mapview_layer_iterate(layer) {
1441 put_one_element(pcanvas, zoom, layer,
1442 nullptr, nullptr, nullptr, nullptr, pcity,
1443 canvas_x, canvas_y, nullptr, nullptr);
1445}
1446
1447/************************************************************************/
1453void put_terrain(struct tile *ptile, struct canvas *pcanvas, float zoom,
1454 int canvas_x, int canvas_y)
1455{
1456 /* Use full tile height, even for terrains. */
1457 canvas_y
1459
1460 mapview_layer_iterate(layer) {
1461 put_one_element(pcanvas, zoom, layer, ptile, nullptr, nullptr, nullptr,
1462 nullptr, canvas_x, canvas_y, nullptr, nullptr);
1464}
1465
1466/************************************************************************/
1474 struct canvas *pcanvas,
1475 int canvas_x, int canvas_y, int *upkeep_cost,
1476 int happy_cost)
1477{
1478 struct sprite *sprite;
1479
1481 if (sprite) {
1483 }
1484
1487 if (sprite) {
1489 }
1491}
1492
1493/*
1494 * pcity->client.color_index is an index into the city_colors array.
1495 * When toggle_city_color is called the city's coloration is toggled. When
1496 * a city is newly colored its color is taken from color_index and
1497 * color_index is moved forward one position. Each color in the array
1498 * tells what color the citymap will be drawn on the mapview.
1499 *
1500 * This array can be added to without breaking anything elsewhere.
1501 */
1502static int color_index = 0;
1503#define NUM_CITY_COLORS tileset_num_city_colors(tileset)
1504
1505
1506/************************************************************************/
1512{
1513 if (pcity->client.colored) {
1515 } else {
1519 }
1520
1522}
1523
1524/************************************************************************/
1530{
1531 if (punit->client.colored) {
1533 } else {
1537 }
1538
1540}
1541
1542/************************************************************************/
1546{
1547 float canvas_x, canvas_y;
1549 int width, height;
1550
1552
1554 struct animation *anim = fc_malloc(sizeof(struct animation));
1555
1556 anim->type = ANIM_NUKE;
1557 anim->id = -1;
1558 anim->nuke.shown = FALSE;
1559 anim->nuke.nuke_tile = ptile;
1560
1563
1564 anim->width = width * map_zoom;
1565 anim->height = height * map_zoom;
1567 } else {
1568 /* We can't count on the return value of tile_to_canvas_pos() since the
1569 * sprite may span multiple tiles. */
1571
1574
1575 /* Make sure everything is flushed and synced before proceeding. First
1576 * we update everything to the store, but don't write this to screen.
1577 * Then add the nuke graphic to the store. Finally flush everything to
1578 * the screen and wait 1 second. */
1580
1583
1584 flush_dirty();
1585 gui_flush();
1586
1587 fc_usleep(1000000);
1588
1590 }
1591}
1592
1593/************************************************************************/
1596static void put_one_tile(struct canvas *pcanvas, enum mapview_layer layer,
1597 struct tile *ptile, int canvas_x, int canvas_y,
1598 const struct city *citymode)
1599{
1601 || (editor_is_active() && editor_tile_is_selected(ptile))) {
1602 struct unit *punit = get_drawable_unit(tileset, ptile, citymode);
1603 struct animation *anim = nullptr;
1604
1607 }
1608
1609 if (anim != nullptr && punit != nullptr
1610 && punit->id == anim->id) {
1611 punit = nullptr;
1612 }
1613
1614 put_one_element(pcanvas, map_zoom, layer, ptile, nullptr, nullptr, punit,
1615 tile_city(ptile), canvas_x, canvas_y, citymode, nullptr);
1616 }
1617}
1618
1619/************************************************************************/
1630static int trade_route_to_canvas_lines(const struct tile *ptile1,
1631 const struct tile *ptile2,
1632 struct trade_route_line *lines)
1633{
1634 int dx, dy;
1635
1636 if (!ptile1 || !ptile2 || !lines) {
1637 return 0;
1638 }
1639
1642
1643 /* FIXME: Remove these casts. */
1644 tile_to_canvas_pos(&lines[0].x, &lines[0].y, map_zoom, (struct tile *)ptile1);
1645 tile_to_canvas_pos(&lines[1].x, &lines[1].y, map_zoom, (struct tile *)ptile2);
1646
1647 if (lines[1].x - lines[0].x == lines[0].width
1648 && lines[1].y - lines[0].y == lines[0].height) {
1649 return 1;
1650 }
1651
1652 lines[1].width = -lines[0].width;
1653 lines[1].height = -lines[0].height;
1654 return 2;
1655}
1656
1657/************************************************************************/
1660static void draw_trade_route_line(const struct tile *ptile1,
1661 const struct tile *ptile2,
1662 enum color_std color)
1663{
1665 int line_count, i;
1666 struct color *pcolor;
1667
1668 if (!ptile1 || !ptile2) {
1669 return;
1670 }
1671
1673 if (!pcolor) {
1674 return;
1675 }
1676
1677 /* Order the source and destination tiles consistently
1678 * so that if a line is drawn twice it does not produce
1679 * ugly effects due to dashes not lining up. */
1681 const struct tile *tmp;
1682 tmp = ptile1;
1683 ptile1 = ptile2;
1684 ptile2 = tmp;
1685 }
1686
1688 for (i = 0; i < line_count; i++) {
1689 /* XXX: canvas_put_line doesn't currently take map_zoom into account
1690 * itself, but it probably should? */
1694 lines[i].width, lines[i].height);
1695 }
1696}
1697
1698/************************************************************************/
1702{
1703 if (!pcity_src) {
1704 return;
1705 }
1706
1708 if (pcity_dest != nullptr) {
1711 }
1713}
1714
1715/************************************************************************/
1718static void draw_trade_routes(void)
1719{
1721 return;
1722 }
1723
1728 } else {
1729 struct player *pplayer = client_player();
1730
1731 if (!pplayer) {
1732 return;
1733 }
1734
1735 city_list_iterate(pplayer->cities, pcity) {
1738 }
1739}
1740
1741/************************************************************************/
1757{
1758 int gui_x0, gui_y0;
1759 bool full;
1760 struct canvas *tmp;
1761
1762 if (canvas_x < 0) {
1763 width += canvas_x;
1764 canvas_x = 0;
1765 } else if (canvas_x > mapview.store_width) {
1768 }
1769
1770 if (canvas_y < 0) {
1771 height += canvas_y;
1772 canvas_y = 0;
1773 } else if (canvas_y > mapview.store_height) {
1776 }
1777
1778 if (width <= 0 || height <= 0) {
1779 /* Area outside mapview */
1780 return;
1781 }
1782
1783 gui_x0 = mapview.gui_x0 + canvas_x;
1784 gui_y0 = mapview.gui_y0 + canvas_y;
1785 full = (canvas_x == 0 && canvas_y == 0
1788
1789 log_debug("update_map_canvas(pos=(%d,%d), size=(%d,%d))",
1791
1792 /* If a full redraw is done, we just draw everything onto the canvas.
1793 * However if a partial redraw is done we draw everything onto the
1794 * tmp_canvas then copy *just* the area of update onto the canvas. */
1795 if (!full) {
1796 /* Swap store and tmp_store. */
1797 tmp = mapview.store;
1800 }
1801
1802 /* Clear the area. This is necessary since some parts of the rectangle
1803 * may not actually have any tiles drawn on them. This will happen when
1804 * the mapview is large enough so that the tile is visible in multiple
1805 * locations. In this case it will only be drawn in one place.
1806 *
1807 * Of course it's necessary to draw to the whole area to cover up any old
1808 * drawing that was done there. */
1812
1813 mapview_layer_iterate(layer) {
1814 if (layer == LAYER_TILELABEL) {
1816 }
1817 if (layer == LAYER_CITYBAR) {
1819 continue;
1820 }
1821 gui_rect_iterate_coord(gui_x0, gui_y0, width,
1823 ? (tileset_tile_height(tileset) / 2 * map_zoom) : 0),
1824 ptile, pedge, pcorner, gui_x, gui_y, map_zoom) {
1825 const int cx = gui_x - mapview.gui_x0, cy = gui_y - mapview.gui_y0;
1826
1827 if (ptile) {
1828 put_one_tile(mapview.store, layer, ptile, cx, cy, nullptr);
1829 } else if (pedge) {
1830 put_one_element(mapview.store, map_zoom, layer, nullptr, pedge,
1831 nullptr, nullptr, nullptr, cx, cy, nullptr, nullptr);
1832 } else if (pcorner) {
1833 put_one_element(mapview.store, map_zoom, layer, nullptr, nullptr,
1834 pcorner, nullptr, nullptr, cx, cy, nullptr, nullptr);
1835 } else {
1836 /* This can happen, for instance for unreal tiles. */
1837 }
1840
1843
1844 /* Draw the goto lines on top of the whole thing. This is done last as
1845 * we want it completely on top.
1846 *
1847 * Note that a pixel right on the border of a tile may actually contain a
1848 * goto line from an adjacent tile. Thus we draw any extra goto lines
1849 * from adjacent tiles (if they're close enough). */
1850 gui_rect_iterate(gui_x0 - GOTO_WIDTH, gui_y0 - GOTO_WIDTH,
1851 width + 2 * GOTO_WIDTH, height + 2 * GOTO_WIDTH,
1852 ptile, pedge, pcorner, map_zoom) {
1853 if (!ptile) {
1854 continue;
1855 }
1856 adjc_dir_base_iterate(&(wld.map), ptile, dir) {
1857 if (mapdeco_is_gotoline_set(ptile, dir)) {
1858 draw_segment(ptile, dir);
1859 }
1862
1863 if (!full) {
1864 /* Swap store and tmp_store back. */
1865 tmp = mapview.store;
1868
1869 /* And copy store to tmp_store. */
1872 }
1873
1875}
1876
1877/************************************************************************/
1884
1885/* The maximum city description width and height. This gives the dimensions
1886 * of a rectangle centered directly beneath the tile a city is on, that
1887 * contains the city description.
1888 *
1889 * These values are increased when drawing is done. This may mean that
1890 * the change (from increasing the value) won't take place until the
1891 * next redraw. */
1893
1894/* Same for tile labels */
1896
1897/************************************************************************/
1904
1905/************************************************************************/
1908void update_tile_label(struct tile *ptile)
1909{
1911}
1912
1913/************************************************************************/
1918 const int canvas_x0, const int canvas_y0,
1919 struct city *pcity, int *width, int *height)
1920{
1921 const struct citybar_sprites *citybar = get_citybar_sprites(tileset);
1922 static char name[512], growth[32], prod[512], size[32], trade_routes[32];
1925 /* trade_routes_color initialized just to get rid of gcc warning
1926 * on optimization level 3 when it misdiagnoses that it would be used
1927 * uninitialized otherwise. Funny thing here is that warning would
1928 * go away also by *not* setting it to values other than
1929 * COLOR_MAPVIEW_CITYTEXT in get_city_mapview_trade_routes() */
1931 struct color *owner_color;
1932 struct area_rect
1933 name_rect = {0, 0, 0, 0}, growth_rect = {0, 0, 0, 0},
1934 prod_rect = {0, 0, 0, 0}, size_rect = {0, 0, 0, 0},
1935 flag_rect = {0, 0, 0, 0}, occupy_rect = {0, 0, 0, 0},
1936 food_rect = {0, 0, 0, 0}, shield_rect = {0, 0, 0, 0},
1937 trade_routes_rect = {0,}, trade_rect = {0,};
1938 int width1 = 0, width2 = 0, height1 = 0, height2 = 0;
1939 struct sprite *bg = citybar->background;
1940 struct sprite *flag = get_city_flag_sprite(tileset, pcity);
1941 struct sprite *occupy = nullptr;
1942 int bg_w, bg_h, x, y;
1945 const int border = 6;
1946 const enum client_font FONT_CITY_SIZE = FONT_CITY_NAME; /* TODO: new font */
1947
1948 /* We can see the city's production or growth values if
1949 * we are observing or playing as the owner of the city. */
1950 const bool can_see_inside
1952 const bool should_draw_productions
1957 const bool should_draw_lower_bar
1960
1961
1962 *width = 0;
1963 *height = 0;
1964
1966 return;
1967 }
1968
1969
1970 /* First: calculate rect dimensions (but not positioning). */
1971
1973 bg_w *= map_zoom;
1974 bg_h *= map_zoom;
1976 growth, sizeof(growth),
1978
1980 fc_snprintf(size, sizeof(size), "%d", city_size_get(pcity));
1981
1984
1986 int count = unit_list_size(pcity->tile->units);
1987
1988 count = CLIP(0, count, citybar->occupancy.size - 1);
1989 occupy = citybar->occupancy.p[count];
1990 } else {
1991 if (pcity->client.occupied) {
1992 occupy = citybar->occupied;
1993 } else {
1994 occupy = citybar->occupancy.p[0];
1995 }
1996 }
1997
2002
2004 + 2 * border + size_rect.w);
2005 height1 = MAX(flag_rect.h,
2006 MAX(occupy_rect.h,
2007 MAX(name_rect.h + border,
2008 size_rect.h + border)));
2009 }
2010
2012 width2 = 0;
2013 height2 = 0;
2014
2016 get_city_mapview_production(pcity, prod, sizeof(prod));
2018
2024 }
2025
2026 if (should_draw_growth) {
2033 }
2034
2037 sizeof(trade_routes),
2046 }
2047 }
2048
2049 *width = MAX(width1, width2);
2050 *height = height1 + height2;
2051
2052 /* Next fill in X and Y locations. */
2053
2055 flag_rect.x = canvas_x - *width / 2;
2056 flag_rect.y = canvas_y + (height1 - flag_rect.h) / 2;
2057
2059 occupy_rect.y = canvas_y + (height1 - occupy_rect.h) / 2;
2060
2062 - name_rect.w - size_rect.w - border) / 2;
2063 name_rect.y = canvas_y + (height1 - name_rect.h) / 2;
2064
2065 size_rect.x = canvas_x + (*width + 1) / 2 - size_rect.w - border / 2;
2066 size_rect.y = canvas_y + (height1 - size_rect.h) / 2;
2067 }
2068
2071 shield_rect.x = canvas_x - *width / 2;
2073
2074 prod_rect.x = shield_rect.x + shield_rect.w + border / 2;
2075 prod_rect.y = canvas_y + height1 + (height2 - prod_rect.h) / 2;
2076 }
2077
2079 trade_routes_rect.x = canvas_x + (*width + 1) / 2
2080 - trade_routes_rect.w - border / 2;
2082 + (height2 - trade_routes_rect.h) / 2;
2083
2085 trade_rect.y = canvas_y + height1 + (height2 - trade_rect.h) / 2;
2086 }
2087
2088 if (should_draw_growth) {
2089 growth_rect.x = canvas_x + (*width + 1) / 2
2090 - growth_rect.w - border / 2;
2091 if (trade_routes_rect.w > 0) {
2093 - trade_routes_rect.w - border / 2 - trade_rect.w - border / 2;
2094 }
2096
2097 food_rect.x = growth_rect.x - border / 2 - food_rect.w;
2098 food_rect.y = canvas_y + height1 + (height2 - food_rect.h) / 2;
2099 }
2100 }
2101
2102
2103 /* Now draw. */
2104
2105 /* Draw the city bar's background. */
2106 for (x = 0; x < *width; x += bg_w) {
2107 for (y = 0; y < *height; y += bg_h) {
2109 (canvas_y + y) / map_zoom,
2110 bg, 0, 0,
2111 (*width - x) / map_zoom, (*height - y) / map_zoom);
2112 }
2113 }
2114
2116
2121 flag);
2122 /* XXX: canvas_put_line() doesn't currently take map_zoom into account.
2123 * Should it?
2124 * In the meantime, don't compensate with '/ map_zoom' here, unlike
2125 * for canvas_put_sprite/text/rectangle */
2127 (flag_rect.x + flag_rect.w) /* / map_zoom */ - 1,
2128 canvas_y /* / map_zoom */,
2129 0, height1 /* / map_zoom */);
2132 occupy);
2137 (size_rect.x - border / 2) / map_zoom,
2139 (size_rect.w + border) / map_zoom,
2140 height1 / map_zoom);
2141 {
2142 /* Try to pick a color for city size text that contrasts with
2143 * player color */
2144 struct color *textcolors[2] = {
2147 };
2148
2153 }
2154 }
2155
2157
2161 citybar->shields);
2165 }
2166
2170 citybar->trade);
2175 }
2176
2177 if (should_draw_growth) {
2180 citybar->food);
2184 }
2185 }
2186
2187 /* Draw the city bar's outline. */
2188 /* XXX not scaling by map_zoom, see above */
2190 (canvas_x - *width / 2) /* / map_zoom */,
2191 canvas_y /* / map_zoom */,
2192 *width /* / map_zoom */, 0);
2194 (canvas_x - *width / 2) /* / map_zoom */,
2195 canvas_y /* / map_zoom */,
2196 0, *height /* / map_zoom */);
2198 (canvas_x - *width / 2) /* / map_zoom */,
2199 (canvas_y + *height) /* / map_zoom */ - 1,
2200 *width /* / map_zoom */, 0);
2202 (canvas_x - *width / 2 + *width) /* / map_zoom */,
2203 canvas_y /* / map_zoom */,
2204 0, *height /* / map_zoom */);
2205
2206 /* Draw the dividing line if we drew both the
2207 * upper and lower parts. */
2210 (canvas_x - *width / 2) /* / map_zoom */,
2211 (canvas_y + height1) /* / map_zoom */ - 1,
2212 *width /* / map_zoom */, 0);
2213 }
2214}
2215
2216/************************************************************************/
2222 int canvas_x, int canvas_y,
2223 struct city *pcity, int *width, int *height)
2224{
2225 static char name[512], growth[32], prod[512], trade_routes[32];
2228 /* trade_routes_color initialized just to get rid off gcc warning
2229 * on optimization level 3 when it misdiagnoses that it would be used
2230 * uninitialized otherwise. Funny thing here is that warning would
2231 * go away also by *not* setting it to values other than
2232 * COLOR_MAPVIEW_CITYTEXT in get_city_mapview_trade_routes() */
2234 struct {
2235 int x, y, w, h;
2236 } name_rect = {0, 0, 0, 0}, growth_rect = {0, 0, 0, 0},
2237 prod_rect = {0, 0, 0, 0}, trade_routes_rect = {0,};
2239 int spacer_width = 0;
2241 || city_owner(pcity) == client_player());
2242
2243 *width = *height = 0;
2244
2247
2249 growth, sizeof(growth), &growth_color,
2251
2253 int drawposx;
2254
2255 /* HACK: Put a character's worth of space between the two
2256 * strings if needed. */
2257 get_text_size(&spacer_width, nullptr, FONT_CITY_NAME, "M");
2258
2259 total_width = 0;
2260 total_height = 0;
2261
2265
2270 }
2271
2274 sizeof(trade_routes),
2280 }
2281
2283 drawposx -= total_width / 2;
2287 drawposx += name_rect.w;
2288
2295 drawposx += growth_rect.w;
2296 }
2297
2304 }
2305
2306 canvas_y += total_height + 3;
2307
2308 *width = MAX(*width, total_width);
2309 *height += total_height + 3;
2310 }
2311
2313 get_city_mapview_production(pcity, prod, sizeof(prod));
2315
2318
2323
2324 *width = MAX(*width, total_width);
2325 *height += total_height;
2326 }
2327}
2328
2329/************************************************************************/
2341static void show_city_desc(struct canvas *pcanvas,
2342 int canvas_x, int canvas_y,
2343 struct city *pcity, int *width, int *height)
2344{
2347 } else {
2349 }
2350}
2351
2352/************************************************************************/
2362static void show_tile_label(struct canvas *pcanvas,
2363 int canvas_x, int canvas_y,
2364 struct tile *ptile, int *width, int *height)
2365{
2366 const enum client_font FONT_TILE_LABEL = FONT_CITY_NAME; /* TODO: new font */
2367#define COLOR_MAPVIEW_TILELABEL COLOR_MAPVIEW_CITYTEXT
2368
2371
2373
2377#undef COLOR_MAPVIEW_TILELABEL
2378}
2379
2380/************************************************************************/
2384 int width_base, int height_base)
2385{
2387 const int dy = max_desc_height;
2390
2394 return;
2395 }
2396
2399 return;
2400 }
2401
2402 /* A city description is shown below the city. It has a specified
2403 * maximum width and height (although these are only estimates). Thus
2404 * we need to update some tiles above the mapview and some to the left
2405 * and right.
2406 *
2407 * /--W1--\ (W1 = tileset_tile_width(tileset))
2408 * -------- \
2409 * | CITY | H1 (H1 = tileset_tile_height(tileset))
2410 * | | /
2411 * ------------------ \
2412 * | DESCRIPTION | H2 (H2 = MAX_CITY_DESC_HEIGHT)
2413 * | | /
2414 * ------------------
2415 * \-------W2-------/ (W2 = MAX_CITY_DESC_WIDTH)
2416 *
2417 * We must draw H2 extra pixels above and (W2 - W1) / 2 extra pixels
2418 * to each side of the mapview.
2419 */
2422 width_base + dx, height_base + dy - offset_y,
2423 ptile, pedge, pcorner, gui_x, gui_y, map_zoom) {
2424 const int canvas_x = gui_x - mapview.gui_x0;
2425 const int canvas_y = gui_y - mapview.gui_y0;
2426
2427 if (ptile && tile_city(ptile)) {
2428 int width = 0, height = 0;
2429 struct city *pcity = tile_city(ptile);
2430
2432 pcity, &width, &height);
2433 log_debug("Drawing %s.", city_name_get(pcity));
2434
2436 /* The update was incomplete! We queue a new update. Note that
2437 * this is recursively queueing an update within a dequeuing of an
2438 * update. This is allowed specifically because of the code in
2439 * unqueue_mapview_updates. See that function for more. */
2440 log_debug("Re-queuing %s.", city_name_get(pcity));
2442 }
2445 }
2447
2448 /* We don't update the new max values until the end, so that the
2449 * check above to see what cities need redrawing will be complete. */
2452}
2453
2454/************************************************************************/
2458 int width_base, int height_base)
2459{
2461 const int dy = max_label_height;
2463
2466 width_base + dx, height_base + dy,
2467 ptile, pedge, pcorner, gui_x, gui_y, map_zoom) {
2468 const int canvas_x = gui_x - mapview.gui_x0;
2469 const int canvas_y = gui_y - mapview.gui_y0;
2470
2471 if (ptile && ptile->label != nullptr) {
2472 int width = 0, height = 0;
2473
2475 ptile, &width, &height);
2476 log_debug("Drawing label %s.", ptile->label);
2477
2479 /* The update was incomplete! We queue a new update. Note that
2480 * this is recursively queueing an update within a dequeuing of an
2481 * update. This is allowed specifically because of the code in
2482 * unqueue_mapview_updates(). See that function for more. */
2483 log_debug("Re-queuing tile label %s drawing.", ptile->label);
2484 update_tile_label(ptile);
2485 }
2488 }
2490
2491 /* We don't update the new max values until the end, so that the
2492 * check above to see what cities need redrawing will be complete. */
2495}
2496
2497/************************************************************************/
2501void draw_segment(struct tile *src_tile, enum direction8 dir)
2502{
2503 float canvas_x, canvas_y, canvas_dx, canvas_dy;
2504
2505 /* Determine the source position of the segment. */
2509
2510 /* Determine the vector of the segment. */
2511 map_to_gui_vector(tileset, map_zoom, &canvas_dx, &canvas_dy,
2512 DIR_DX[dir], DIR_DY[dir]);
2513
2514 /* Draw the segment. */
2515 /* XXX: canvas_put_line doesn't currently take map_zoom into account
2516 * itself, but it probably should? If so this will need adjusting */
2519 canvas_x, canvas_y, canvas_dx, canvas_dy);
2520
2521 /* The actual area drawn will extend beyond the base rectangle, since
2522 * the goto lines have width. */
2523 dirty_rect(MIN(canvas_x, canvas_x + canvas_dx) - GOTO_WIDTH,
2524 MIN(canvas_y, canvas_y + canvas_dy) - GOTO_WIDTH,
2525 ABS(canvas_dx) + 2 * GOTO_WIDTH,
2526 ABS(canvas_dy) + 2 * GOTO_WIDTH);
2527
2528 /* It is possible that the mapview wraps between the source and dest
2529 * tiles. In this case they will not be next to each other; they'll be
2530 * on the opposite sides of the screen. If this happens then the dest
2531 * tile will not be updated. This is consistent with the mapview design
2532 * which fails when the size of the mapview approaches that of the map. */
2533}
2534
2535/************************************************************************/
2540 struct unit *punit1, int hp1)
2541{
2542 struct unit *losing_unit = (hp0 == 0 ? punit0 : punit1);
2543 float canvas_x, canvas_y;
2544 int i;
2545
2547
2548 /* Make sure we don't start out with fewer HP than we're supposed to
2549 * end up with (which would cause the following loop to break). */
2550 punit0->hp = MAX(punit0->hp, hp0);
2551 punit1->hp = MAX(punit1->hp, hp1);
2552
2554
2556 struct animation *anim = fc_malloc(sizeof(struct animation));
2557 struct unit *winning_unit;
2558 int winner_end_hp;
2559 int loser_end_hp;
2562
2563 if (losing_unit == punit1) {
2566 loser_end_hp = hp1;
2567 } else {
2570 loser_end_hp = hp0;
2571 }
2572
2573 anim->type = ANIM_BATTLE;
2574 anim->id = -1;
2575 anim->battle.virt_loser
2577 nullptr, unit_type_get(losing_unit),
2579 anim->battle.loser_tile = unit_tile(losing_unit);
2580 anim->battle.virt_loser->facing = losing_unit->facing;
2581 anim->battle.loser_hp_start = losing_unit->hp;
2582 anim->battle.loser_hp_end = loser_end_hp;
2583 anim->battle.virt_winner
2585 nullptr, unit_type_get(winning_unit),
2587 anim->battle.winner_tile = unit_tile(winning_unit);
2588 anim->battle.virt_winner->facing = winning_unit->facing;
2589 anim->battle.winner_hp_start = MAX(winning_unit->hp, winner_end_hp);
2590 anim->battle.winner_hp_end = winner_end_hp;
2591 anim->battle.steps = MAX(losing_unit->hp,
2592 anim->battle.winner_hp_start - winner_end_hp);
2593 anim->width = aw;
2594 anim->height = ah;
2596
2597 if (loser_end_hp <= 0) {
2598 anim = fc_malloc(sizeof(struct animation));
2599 anim->type = ANIM_EXPL;
2600 anim->id = winning_unit->id;
2601 anim->expl.tile = losing_unit->tile;
2603 anim->expl.sprite_count = sprite_vector_size(anim->expl.sprites);
2604 anim->width = aw;
2605 anim->height = ah;
2607 }
2608 } else {
2611
2612 while (punit0->hp > hp0 || punit1->hp > hp1) {
2613 const int diff0 = punit0->hp - hp0, diff1 = punit1->hp - hp1;
2614
2616
2617 if (fc_rand(diff0 + diff1) < diff0) {
2618 punit0->hp--;
2620 } else {
2621 punit1->hp--;
2623 }
2624
2626 gui_flush();
2627
2630 }
2631
2641
2642 for (i = 0; i < num_tiles_explode_unit; i++) {
2643 int w, h;
2644 struct sprite *sprite = *sprite_vector_get(anim, i);
2645
2648
2649 /* We first draw the explosion onto the unit and draw draw the
2650 * complete thing onto the map canvas window. This avoids
2651 * flickering. */
2658 - w / 2,
2660 - h / 2,
2661 sprite);
2664
2665 flush_dirty();
2666 gui_flush();
2667
2670 }
2671 }
2672 }
2673
2674 set_units_in_combat(nullptr, nullptr);
2677}
2678
2679/************************************************************************/
2684 struct tile *src_tile, int dx, int dy)
2685{
2686 struct tile *dest_tile;
2687 int dest_x, dest_y, src_x, src_y;
2688 int prev_x = -1;
2689 int prev_y = -1;
2690 int tuw;
2691 int tuh;
2692
2693 /* Only works for adjacent-square moves */
2694 if (dx < -1 || dx > 1 || dy < -1 || dy > 1 || (dx == 0 && dy == 0)) {
2695 return;
2696 }
2697
2698 index_to_map_pos(&src_x, &src_y, tile_index(src_tile));
2699 dest_x = src_x + dx;
2700 dest_y = src_y + dy;
2701 dest_tile = map_pos_to_tile(&(wld.map), dest_x, dest_y);
2702 if (!dest_tile) {
2703 return;
2704 }
2705
2706 if (tile_visible_mapcanvas(src_tile)
2707 || tile_visible_mapcanvas(dest_tile)) {
2708 float start_x, start_y;
2709 float canvas_dx, canvas_dy;
2711 double mytime;
2712
2714
2715 map_to_gui_vector(tileset, map_zoom, &canvas_dx, &canvas_dy, dx, dy);
2716
2717 tile_to_canvas_pos(&start_x, &start_y, map_zoom, src_tile);
2721 }
2722
2723 /* Bring the backing store up to date, but don't flush. */
2725
2728
2730 struct animation *anim = fc_malloc(sizeof(struct animation));
2731
2732 anim->type = ANIM_MOVEMENT;
2733 anim->id = punit->id;
2734 anim->movement.mover
2737 anim->movement.mover->hp = punit->hp;
2738 anim->movement.mover->facing = punit->facing;
2739 anim->movement.src = src_tile;
2740 anim->movement.dest = dest_tile;
2741 anim->movement.canvas_dx = canvas_dx;
2742 anim->movement.canvas_dy = canvas_dy;
2743 anim->width = tuw;
2744 anim->height = tuh;
2746 } else {
2747
2748 /* Start the timer (AFTER the unqueue above). */
2750
2751 do {
2752 int new_x, new_y;
2754
2756
2757 new_x = start_x + canvas_dx * (mytime / timing_sec);
2759
2760 if (new_x != prev_x || new_y != prev_y) {
2761 /* Backup the canvas store to the temp store. */
2764 tuw, tuh);
2765
2766 /* Draw */
2769
2770 /* Flush. */
2771 flush_dirty();
2772 gui_flush();
2773
2774 /* Restore the backup. It won't take effect until the next flush. */
2777 tuw, tuh);
2779
2780 prev_x = new_x;
2781 prev_y = new_y;
2782 } else {
2783 fc_usleep(500);
2784 }
2785 } while (mytime < timing_sec);
2786 }
2787 }
2788}
2789
2790/************************************************************************/
2802struct city *find_city_or_settler_near_tile(const struct tile *ptile,
2803 struct unit **punit)
2804{
2805 struct city *closest_city;
2806 struct city *pcity;
2807 struct unit *closest_settler = nullptr, *best_settler = nullptr;
2809
2810 if (punit) {
2811 *punit = nullptr;
2812 }
2813
2814 /* Check if there is visible city working that tile */
2815 pcity = tile_worked(ptile);
2816 if (pcity && pcity->tile) {
2817 if (client.conn.playing == nullptr
2819 /* Rule a */
2820 return pcity;
2821 } else {
2822 /* Rule b */
2823 return nullptr;
2824 }
2825 }
2826
2827 /* Rule e */
2828 closest_city = nullptr;
2829
2830 /* Check within maximum (squared) city radius */
2831 city_tile_iterate(&(wld.map), max_rad, ptile, tile1) {
2833 if (pcity
2834 && (client.conn.playing == nullptr
2837 /*
2838 * Note, we must explicitly check if the tile is workable (with
2839 * city_can_work_tile() above), since it is possible that another
2840 * city (perhaps an UNSEEN city) may be working it!
2841 */
2842
2844 /* Rule c */
2845 return pcity;
2846 }
2847 if (!closest_city) {
2849 }
2850 }
2852
2853 /* Rule d */
2854 if (closest_city || !punit) {
2855 return closest_city;
2856 }
2857
2859 /* Check within maximum (squared) city radius */
2860 city_tile_iterate(&(wld.map), max_rad, ptile, tile1) {
2862 if ((client.conn.playing == nullptr
2866 psettler, FALSE)) {
2867 if (closest_settler == nullptr) {
2869 }
2870 if (best_settler == nullptr && psettler->client.colored) {
2872 }
2873 }
2876
2877 if (best_settler != nullptr) {
2878 /* Rule e */
2880 } else if (closest_settler != nullptr) {
2881 /* Rule f */
2883 }
2884 }
2885
2886 /* Rule g */
2887 return nullptr;
2888}
2889
2890/************************************************************************/
2893struct city *find_city_near_tile(const struct tile *ptile)
2894{
2895 return find_city_or_settler_near_tile(ptile, nullptr);
2896}
2897
2898/************************************************************************/
2904static void append_city_buycost_string(const struct city *pcity,
2905 char *buffer, int buffer_len)
2906{
2907 if (!pcity || !buffer || buffer_len < 1) {
2908 return;
2909 }
2910
2912 return;
2913 }
2914
2915 cat_snprintf(buffer, buffer_len, "/%d", pcity->client.buy_cost);
2916}
2917
2918/************************************************************************/
2923 char *buffer, size_t buffer_len)
2924{
2925 int turns;
2926
2927 universal_name_translation(&pcity->production, buffer, buffer_len);
2928
2930 return;
2931 }
2933
2934 if (999 < turns) {
2935 cat_snprintf(buffer, buffer_len, " -");
2936 } else {
2937 cat_snprintf(buffer, buffer_len, " %d", turns);
2938 }
2939
2941}
2942
2943/************************************************************************/
2949 char *trade_routes_buffer,
2951 enum color_std *pcolor)
2952{
2953 int num_trade_routes;
2954 int max_routes;
2955
2957 return;
2958 }
2959
2960 if (!pcity) {
2961 trade_routes_buffer[0] = '\0';
2962 if (pcolor) {
2964 }
2965 return;
2966 }
2967
2970
2972 "%d/%d", num_trade_routes, max_routes);
2973
2974 if (pcolor) {
2977 } else if (num_trade_routes == 0) {
2979 } else {
2981 }
2982 }
2983}
2984
2985/***************************************************************************/
2988
2989/* These values hold the tiles that need city, unit, or tile updates.
2990 * These different types of updates just tell what area need to be updated,
2991 * not necessarily what's sitting on the tile. A city update covers the
2992 * whole citymap area. A unit update covers just the "full" unit tile
2993 * area. A tile update covers the base tile plus half a tile in each
2994 * direction. */
2996
2997/************************************************************************/
3001static void queue_callback(void *data)
3002{
3005}
3006
3007/************************************************************************/
3011static void queue_add_callback(void)
3012{
3013 if (!callback_queued) {
3016 }
3017}
3018
3019/************************************************************************/
3037{
3038 if (can_client_change_view()) {
3039 needed_updates |= update;
3041 }
3042}
3043
3044/************************************************************************/
3053{
3054 if (can_client_change_view()) {
3055 if (!tile_updates[type]) {
3057 }
3060 }
3061}
3062
3063/************************************************************************/
3067{
3068 /* Calculate the area covered by each update type. The area array gives
3069 * the offset from the tile origin as well as the width and height of the
3070 * area to be updated. This is initialized each time when entering the
3071 * function from the existing tileset variables.
3072 *
3073 * A TILE update covers the base tile (W x H) plus a half-tile in each
3074 * direction (for edge/corner graphics), making its area 2W x 2H.
3075 *
3076 * A UNIT update covers a UW x UH area. This is centered horizontally
3077 * over the tile but extends up above the tile (e.g., units in iso-view).
3078 *
3079 * A CITYMAP update covers the whole citymap of a tile. This includes
3080 * the citymap area itself plus an extra half-tile in each direction (for
3081 * edge/corner graphics).
3082 */
3083 const float W = tileset_tile_width(tileset) * map_zoom;
3084 const float H = tileset_tile_height(tileset) * map_zoom;
3085 const float UW = tileset_unit_width(tileset) * map_zoom;
3086 const float UH = tileset_unit_height(tileset) * map_zoom;
3087 const float city_width = get_citydlg_canvas_width() * map_zoom + W;
3088 const float city_height = get_citydlg_canvas_height() * map_zoom + H;
3089 const struct {
3090 float dx, dy, w, h;
3091 } area[TILE_UPDATE_COUNT] = {
3092 {0, 0, W, H},
3093 {-W / 2, -H / 2, 2 * W, 2 * H},
3094 {(W - UW) / 2, H - UH, UW, UH},
3096 {-(city_width - W) / 2, -(city_height - H) / 2, city_width, city_height},
3098 };
3100
3101 int i;
3102
3103 if (!can_client_change_view()) {
3104 /* Double sanity check: make sure we don't unqueue an invalid update
3105 * after we've already detached. */
3106 return;
3107 }
3108
3109 log_debug("unqueue_mapview_update: needed_updates=%d",
3111
3112 /* This code "pops" the lists of tile updates off of the static array and
3113 * stores them locally. This allows further updates to be queued within
3114 * the function itself (namely, within update_map_canvas() ). */
3115 for (i = 0; i < TILE_UPDATE_COUNT; i++) {
3117 tile_updates[i] = nullptr;
3118 }
3119
3120 if (!map_is_empty()) {
3124 dirty_all();
3127 /* Have to update the overview too, since some tiles may have changed. */
3129 } else {
3131 int max_x = 0, max_y = 0;
3132
3133 for (i = 0; i < TILE_UPDATE_COUNT; i++) {
3134 if (my_tile_updates[i]) {
3136 float xl, yt;
3137 int xr, yb;
3138
3139 (void) tile_to_canvas_pos(&xl, &yt, map_zoom, ptile);
3140
3141 xl += area[i].dx;
3142 yt += area[i].dy;
3143 xr = xl + area[i].w;
3144 yb = yt + area[i].h;
3145
3146 if (xr > 0 && xl < mapview.width
3147 && yb > 0 && yt < mapview.height) {
3148 min_x = MIN(min_x, xl);
3149 min_y = MIN(min_y, yt);
3150 max_x = MAX(max_x, xr);
3151 max_y = MAX(max_y, yb);
3152 }
3153
3154 /* FIXME: These overview updates should be batched as well.
3155 * Right now they account for as much as 90% of the runtime of
3156 * the unqueue. */
3157 overview_update_tile(ptile);
3159 }
3160 }
3161
3162 if (min_x < max_x && min_y < max_y) {
3164 }
3165 }
3166 }
3167
3168 for (i = 0; i < TILE_UPDATE_COUNT; i++) {
3169 if (my_tile_updates[i]) {
3171 }
3172 }
3174
3175 if (write_to_screen) {
3176 flush_dirty();
3178 }
3179}
3180
3181/************************************************************************/
3186 char *name_buffer,
3187 size_t name_buffer_len,
3188 char *growth_buffer,
3189 size_t growth_buffer_len,
3190 enum color_std *growth_color,
3192{
3194
3196 if (client.conn.playing == nullptr
3198 int turns = city_turns_to_grow(pcity);
3199
3200 if (turns == 0) {
3202 } else if (turns == FC_INFINITY) {
3204 } else {
3205 /* Negative turns means we're shrinking, but that's handled
3206 down below. */
3208 }
3209
3210 if (turns <= 0) {
3211 /* A blocked or shrinking city has its growth status shown in red. */
3213 } else {
3215 }
3216
3217 if (pcity->surplus[O_SHIELD] < 0) {
3219 }
3220 } else {
3221 growth_buffer[0] = '\0';
3223 }
3224}
3225
3226/************************************************************************/
3230static bool can_do_cached_drawing(void)
3231{
3232 const int W = tileset_tile_width(tileset) * map_zoom;
3233 const int H = tileset_tile_height(tileset) * map_zoom;
3235
3236 /* If the mapview window is too large, cached drawing is not possible.
3237 *
3238 * BACKGROUND: cached drawing occurs when the mapview is scrolled just
3239 * a short distance. The majority of the mapview window can simply be
3240 * copied while the newly visible areas must be drawn from scratch. This
3241 * speeds up drawing significantly, especially when using the scrollbars
3242 * or mapview sliding.
3243 *
3244 * When the mapview is larger than the map, however, some tiles may become
3245 * visible twice. In this case one instance of the tile will be drawn
3246 * while all others are drawn black. When this happens the cached drawing
3247 * system breaks since it assumes the mapview canvas is an "ideal" window
3248 * over the map. So black tiles may be scrolled from the edges of the
3249 * mapview into the center, while drawn tiles may be scrolled from the
3250 * center of the mapview out to the edges. The result is very bad.
3251 *
3252 * There are a few different ways this could be solved. One way is simply
3253 * to turn off cached drawing, which is what we do now. If the mapview
3254 * window gets to be too large, the caching is disabled. Another would
3255 * be to prevent the window from getting too large in the first place -
3256 * but because the window boundaries aren't at an even tile this would
3257 * mean the entire map could never be shown. Yet another way would be
3258 * to draw tiles more than once if they are visible in multiple locations
3259 * on the mapview.
3260 *
3261 * The logic below is complicated and determined in part by
3262 * trial-and-error. */
3264 /* An unwrapping map: no limitation. On an unwrapping map no tile can
3265 * be visible twice so there's no problem. */
3266 return TRUE;
3267 }
3270 /* Non-matching. In this case the mapview does not line up with the
3271 * map's axis of wrapping. This will give very bad results for the
3272 * player!
3273 * We can never show more than half of the map.
3274 *
3275 * We divide by 4 below because we have to divide by 2 twice. The
3276 * first division by 2 is because the square must be half the size
3277 * of the (width+height). The second division by two is because for
3278 * an iso-map, MAP_NATURAL_XXX has a scale of 2, whereas for iso-view
3279 * NORMAL_TILE_XXX has a scale of 2. */
3280 return (w <= (MAP_NATURAL_WIDTH + MAP_NATURAL_HEIGHT) * W / 4
3281 && h <= (MAP_NATURAL_WIDTH + MAP_NATURAL_HEIGHT) * H / 4);
3282 } else {
3283 /* Matching. */
3284 const int isofactor = (tileset_is_isometric(tileset) ? 2 : 1);
3285 const int isodiff = (tileset_is_isometric(tileset) ? 6 : 2);
3286
3287 /* Now we can use the full width and height, with the exception of a small
3288 * area on each side. */
3290 && w > (MAP_NATURAL_WIDTH - isodiff) * W / isofactor) {
3291 return FALSE;
3292 }
3294 && h > (MAP_NATURAL_HEIGHT - isodiff) * H / isofactor) {
3295 return FALSE;
3296 }
3297
3298 return TRUE;
3299 }
3300}
3301
3302/************************************************************************/
3307{
3308 /* HACK: this must be called on a map_info packet. */
3310
3311 mapdeco_free();
3315}
3316
3317/************************************************************************/
3335
3336/************************************************************************/
3340void mapdeco_set_highlight(const struct tile *ptile, bool highlight)
3341{
3342 bool changed = FALSE;
3343
3344 if (!ptile || !mapdeco_highlight_table) {
3345 return;
3346 }
3347
3348 if (highlight) {
3349 changed = tile_hash_insert(mapdeco_highlight_table, ptile, nullptr);
3350 } else {
3351 changed = tile_hash_remove(mapdeco_highlight_table, ptile);
3352 }
3353
3354 if (changed) {
3355 /* FIXME: Remove the cast. */
3356 refresh_tile_mapcanvas((struct tile *) ptile, TRUE, FALSE);
3357 }
3358}
3359
3360/************************************************************************/
3363bool mapdeco_is_highlight_set(const struct tile *ptile)
3364{
3365 if (!ptile || !mapdeco_highlight_table) {
3366 return FALSE;
3367 }
3368
3369 return tile_hash_lookup(mapdeco_highlight_table, ptile, nullptr);
3370}
3371
3372/************************************************************************/
3388
3389/************************************************************************/
3392void mapdeco_set_crosshair(const struct tile *ptile, bool crosshair)
3393{
3394 bool changed;
3395
3396 if (!mapdeco_crosshair_table || !ptile) {
3397 return;
3398 }
3399
3400 if (crosshair) {
3401 changed = tile_hash_insert(mapdeco_crosshair_table, ptile, nullptr);
3402 } else {
3403 changed = tile_hash_remove(mapdeco_crosshair_table, ptile);
3404 }
3405
3406 if (changed) {
3407 /* FIXME: Remove the cast. */
3408 refresh_tile_mapcanvas((struct tile *) ptile, FALSE, FALSE);
3409 }
3410}
3411
3412/************************************************************************/
3415bool mapdeco_is_crosshair_set(const struct tile *ptile)
3416{
3417 if (!mapdeco_crosshair_table || !ptile) {
3418 return FALSE;
3419 }
3420
3421 return tile_hash_lookup(mapdeco_crosshair_table, ptile, nullptr);
3422}
3423
3424/************************************************************************/
3440
3441/************************************************************************/
3446void mapdeco_add_gotoline(const struct tile *ptile, enum direction8 dir)
3447{
3448 struct gotoline_counter *pglc;
3449 const struct tile *ptile_dest;
3450 bool changed;
3451
3452 if (!mapdeco_gotoline_table || !ptile
3453 || !(dir <= direction8_max())) {
3454 return;
3455 }
3456 ptile_dest = mapstep(&(wld.map), ptile, dir);
3457 if (!ptile_dest) {
3458 return;
3459 }
3460
3464 }
3465 changed = (pglc->line_count[dir] < 1);
3466 pglc->line_count[dir]++;
3467
3468 if (changed) {
3469 /* FIXME: Remove cast. */
3470 refresh_tile_mapcanvas((struct tile *) ptile, FALSE, FALSE);
3472 }
3473}
3474
3475/************************************************************************/
3480void mapdeco_remove_gotoline(const struct tile *ptile,
3481 enum direction8 dir)
3482{
3483 struct gotoline_counter *pglc;
3484 bool changed = FALSE;
3485
3486 if (!mapdeco_gotoline_table || !ptile
3487 || !(dir <= direction8_max())) {
3488 return;
3489 }
3490
3492 return;
3493 }
3494
3495 pglc->line_count[dir]--;
3496 if (pglc->line_count[dir] <= 0) {
3497 pglc->line_count[dir] = 0;
3498 changed = TRUE;
3499 }
3500
3501 if (changed) {
3502 /* FIXME: Remove the casts. */
3503 refresh_tile_mapcanvas((struct tile *) ptile, FALSE, FALSE);
3504 ptile = mapstep(&(wld.map), ptile, dir);
3505 if (ptile != nullptr) {
3506 refresh_tile_mapcanvas((struct tile *) ptile, FALSE, FALSE);
3507 }
3508 }
3509}
3510
3511/************************************************************************/
3517{
3518 const struct unit_order *porder;
3519 const struct tile *ptile;
3520 int i, ind;
3521
3523 || punit->orders.length < 1) {
3524 return;
3525 }
3526
3527 ptile = unit_tile(punit);
3528
3529 for (i = 0; ptile != nullptr && i < punit->orders.length; i++) {
3530 if (punit->orders.index + i >= punit->orders.length
3531 && !punit->orders.repeat) {
3532 break;
3533 }
3534
3536 porder = &punit->orders.list[ind];
3537 if (porder->order != ORDER_MOVE) {
3538 /* FIXME: Should display some indication of non-move orders here. */
3539 continue;
3540 }
3541
3542 mapdeco_add_gotoline(ptile, porder->dir);
3543 ptile = mapstep(&(wld.map), ptile, porder->dir);
3544 }
3545}
3546
3547/************************************************************************/
3551bool mapdeco_is_gotoline_set(const struct tile *ptile,
3552 enum direction8 dir)
3553{
3554 struct gotoline_counter *pglc;
3555
3556 if (!ptile || !(dir <= direction8_max())
3558 return FALSE;
3559 }
3560
3562 return FALSE;
3563 }
3564
3565 return pglc->line_count[dir] > 0;
3566}
3567
3568/************************************************************************/
3573{
3575 return;
3576 }
3577
3580 adjc_dir_iterate(&(wld.map), ptile, ptile_dest, dir) {
3581 if (pglc->line_count[dir] > 0) {
3583 }
3587}
3588
3589/************************************************************************/
3595{
3599 int tile_width = (width + tileset_tile_width(tileset) * map_zoom - 1) /
3601 int tile_height = (height + tileset_tile_height(tileset) * map_zoom - 1) /
3603 int full_width = tile_width * tileset_tile_width(tileset) * map_zoom;
3604 int full_height = tile_height * tileset_tile_height(tileset) * map_zoom;
3606
3607 /* Resized */
3608
3609 /* Since a resize is only triggered when the tile_*** changes, the canvas
3610 * width and height must include the entire backing store - otherwise
3611 * small resizings may lead to undrawn tiles. */
3612 mapview.tile_width = tile_width;
3613 mapview.tile_height = tile_height;
3618
3619 /* Check for what's changed. */
3620 tile_size_changed = (tile_width != old_tile_width
3621 || tile_height != old_tile_height);
3623
3624 /* If the tile size has changed, resize the canvas. */
3625 if (tile_size_changed) {
3626 if (mapview.store) {
3629 }
3636
3640 }
3641
3643 if (tile_size_changed) {
3644 if (center_tile != nullptr) {
3645 int x_left, y_top;
3646 float gui_x, gui_y;
3647
3650
3651 /* Put the center pixel of the tile at the exact center of the mapview. */
3654
3658 }
3661
3662 /* Do not draw to the screen here as that could cause problems
3663 * when we are only initially setting up the view and some widgets
3664 * are not yet ready. */
3666 redrawn = TRUE;
3667 }
3668
3669 /* If the width/height has changed, update the scrollbars even if
3670 * the backing store is not resized. */
3671 if (size_changed) {
3674 }
3675 }
3676
3678
3679 return redrawn;
3680}
3681
3682/************************************************************************/
3686{
3687 /* Create a dummy map to make sure mapview.store is never nullptr. */
3688 map_canvas_resized(1, 1);
3689}
3690
3691/************************************************************************/
3699
3700/************************************************************************/
3704{
3705 struct sprite *sprite
3707
3709 *width *= 7;
3710 *height *= 7;
3711}
3712
3713/************************************************************************/
3717 const struct player *pplayer)
3718{
3719 int i, x, y;
3720 const struct player_spaceship *ship = &pplayer->spaceship;
3721 int w, h;
3722 struct sprite *spr;
3723 struct tileset *t = tileset;
3724
3726 get_sprite_dimensions(spr, &w, &h);
3727
3730 0, 0, w * 7, h * 7);
3731
3732 for (i = 0; i < NUM_SS_MODULES; i++) {
3733 const int j = i / 3;
3734 const int k = i % 3;
3735
3736 if ((k == 0 && j >= ship->habitation)
3737 || (k == 1 && j >= ship->life_support)
3738 || (k == 2 && j >= ship->solar_panels)) {
3739 continue;
3740 }
3741 x = modules_info[i].x * w / 4 - w / 2;
3742 y = modules_info[i].y * h / 4 - h / 2;
3743
3748 }
3749
3750 for (i = 0; i < NUM_SS_COMPONENTS; i++) {
3751 const int j = i / 2;
3752 const int k = i % 2;
3753
3754 if ((k == 0 && j >= ship->fuel)
3755 || (k == 1 && j >= ship->propulsion)) {
3756 continue;
3757 }
3758 x = components_info[i].x * w / 4 - w / 2;
3759 y = components_info[i].y * h / 4 - h / 2;
3760
3761 spr = ((k == 0) ? get_spaceship_sprite(t, SPACESHIP_FUEL)
3763
3765
3766 if (k && ship->state == SSHIP_LAUNCHED) {
3769 }
3770 }
3771
3772 for (i = 0; i < NUM_SS_STRUCTURALS; i++) {
3773 if (!BV_ISSET(ship->structure, i)) {
3774 continue;
3775 }
3776 x = structurals_info[i].x * w / 4 - w / 2;
3777 y = structurals_info[i].y * h / 4 - h / 2;
3778
3781 }
3782}
3783
3784/****************************************************************************
3785 Map link mark module: it makes link marks when a link is sent by chating,
3786 or restore a mark with clicking a link on the chatline.
3787****************************************************************************/
3789 enum text_link_type type; /* The target type. */
3790 int id; /* The city or unit id, or tile index. */
3791 int turn_counter; /* The turn counter before it disappears. */
3792};
3793
3794#define SPECLIST_TAG link_mark
3795#define SPECLIST_TYPE struct link_mark
3796#include "speclist.h"
3797#define link_marks_iterate(pmark) \
3798 TYPED_LIST_ITERATE(struct link_mark, link_marks, pmark)
3799#define link_marks_iterate_end LIST_ITERATE_END
3800
3801static struct link_mark_list *link_marks = nullptr;
3802
3803/************************************************************************/
3806static struct link_mark *link_mark_find(enum text_link_type type, int id)
3807{
3809 if (pmark->type == type && pmark->id == id) {
3810 return pmark;
3811 }
3813
3814 return nullptr;
3815}
3816
3817/************************************************************************/
3821 int id, int turns)
3822{
3823 struct link_mark *pmark = fc_malloc(sizeof(struct link_mark));
3824
3825 pmark->type = type;
3826 pmark->id = id;
3827 pmark->turn_counter = turns;
3828
3829 return pmark;
3830}
3831
3832/************************************************************************/
3836{
3837 free(pmark);
3838}
3839
3840/************************************************************************/
3843static struct tile *link_mark_tile(const struct link_mark *pmark)
3844{
3845 switch (pmark->type) {
3846 case TLT_CITY:
3847 {
3848 struct city *pcity = game_city_by_number(pmark->id);
3849
3850 return pcity ? pcity->tile : nullptr;
3851 }
3852 case TLT_TILE:
3853 return index_to_tile(&(wld.map), pmark->id);
3854 case TLT_UNIT:
3855 {
3856 struct unit *punit = game_unit_by_number(pmark->id);
3857
3858 return punit ? unit_tile(punit) : nullptr;
3859 }
3860 }
3861
3862 return nullptr;
3863}
3864
3865/************************************************************************/
3868static struct color *link_mark_color(const struct link_mark *pmark)
3869{
3870 switch (pmark->type) {
3871 case TLT_CITY:
3873 case TLT_TILE:
3875 case TLT_UNIT:
3877 }
3878
3879 return nullptr;
3880}
3881
3882/************************************************************************/
3885static void link_mark_draw(const struct link_mark *pmark)
3886{
3889 int xd = width / 20, yd = height / 20;
3890 int xlen = width / 3, ylen = height / 3;
3891 float canvas_x, canvas_y;
3893 struct tile *ptile = link_mark_tile(pmark);
3894 struct color *pcolor = link_mark_color(pmark);
3895
3896 if (!ptile || !tile_to_canvas_pos(&canvas_x, &canvas_y, map_zoom, ptile)) {
3897 return;
3898 }
3899
3900 x_left = canvas_x + xd;
3901 x_right = canvas_x + width - xd;
3902 y_top = canvas_y + yd;
3903 y_bottom = canvas_y + height - yd;
3904
3905 /* XXX: canvas_put_line() doesn't currently take map_zoom into account
3906 * itself, but it probably should? If so these will need adjusting */
3908 xlen, 0);
3910 0, ylen);
3911
3913 -xlen, 0);
3915 0, ylen);
3916
3918 xlen, 0);
3920 0, -ylen);
3921
3923 -xlen, 0);
3925 0, -ylen);
3926}
3927
3928/************************************************************************/
3932{
3933 if (link_marks) {
3935 }
3936
3938}
3939
3940/************************************************************************/
3944{
3945 if (!link_marks) {
3946 return;
3947 }
3948
3950 link_marks = nullptr;
3951}
3952
3953/************************************************************************/
3962
3963/************************************************************************/
3971
3972/************************************************************************/
3976{
3978 if (--pmark->turn_counter <= 0) {
3980 }
3982
3983 /* update_map_canvas_visible(); not needed here. */
3984}
3985
3986/************************************************************************/
3990{
3991 struct link_mark *pmark = link_mark_find(type, id);
3992 struct tile *ptile;
3993
3994 if (pmark) {
3995 /* Already displayed, but maybe increase the turn counter. */
3996 pmark->turn_counter = MAX(pmark->turn_counter, 2);
3997 return;
3998 }
3999
4000 pmark = link_mark_new(type, id, 2);
4002 ptile = link_mark_tile(pmark);
4003 if (ptile && tile_visible_mapcanvas(ptile)) {
4005 }
4006}
4007
4008/************************************************************************/
4012{
4013 struct link_mark *pmark;
4014 struct tile *ptile;
4015
4016 if (link_mark_find(type, id)) {
4017 return;
4018 }
4019
4020 pmark = link_mark_new(type, id, 1);
4022 ptile = link_mark_tile(pmark);
4023 if (ptile && tile_visible_mapcanvas(ptile)) {
4025 }
4026}
4027
4028/************************************************************************/
4032 struct tileset *tset,
4033 int *tset_topo)
4034{
4035 int tileset_topology;
4036
4037 if (tileset_hex_width(tset) > 0) {
4040 } else if (tileset_hex_height(tset) > 0) {
4043 } else if (tileset_is_isometric(tset)) {
4045 } else {
4046 tileset_topology = 0;
4047 }
4048
4049 if (tset_topo != nullptr) {
4051 }
4052
4053 if (tileset_topology & TF_HEX) {
4054 if ((topology_id & (TF_HEX | TF_ISO)) == tileset_topology) {
4055 return TOPO_COMPATIBLE;
4056 }
4057
4058 /* Hex topology must match for both hexness and iso/non-iso */
4059 return TOPO_INCOMP_HARD;
4060 }
4061
4062 if (topology_id & TF_HEX) {
4063 return TOPO_INCOMP_HARD;
4064 }
4065
4066 if ((topology_id & TF_ISO) != (tileset_topology & TF_ISO)) {
4067 /* Non-hex iso/non-iso incompatibility is a soft one */
4068 return TOPO_INCOMP_SOFT;
4069 }
4070
4071 return TOPO_COMPATIBLE;
4072}
4073
4074/************************************************************************/
4077const char *describe_topology(int topo)
4078{
4079 if (topo & TF_ISO) {
4080 if (topo & TF_HEX) {
4081 return _("ISO|Hex");
4082 }
4083 return _("ISO");
4084 }
4085 if (topo & TF_HEX) {
4086 return _("Hex");
4087 }
4088
4089 return _("Overhead");
4090}
4091
4092/************************************************************************/
4099
4100/************************************************************************/
4104{
4105 return infratile;
4106}
4107
4108/************************************************************************/
4111void client_infratile_set(struct tile *ptile)
4112{
4113 struct tile *old_tile = infratile;
4114
4115 infratile = ptile;
4116
4117 if (old_tile != nullptr) {
4119 }
4120 if (ptile != nullptr) {
4122 }
4123}
#define BV_ISSET(bv, bit)
Definition bitvector.h:86
struct canvas int int struct sprite int int int int height
Definition canvas_g.h:44
struct canvas int int int int struct sprite *sprite struct canvas struct color * pcolor
Definition canvas_g.h:56
struct canvas int int int int struct sprite *sprite canvas_put_rectangle
Definition canvas_g.h:55
struct canvas int int canvas_y
Definition canvas_g.h:43
struct canvas int canvas_x
Definition canvas_g.h:43
struct canvas int int int int struct sprite *sprite struct canvas struct color int int int int height canvas_put_line
Definition canvas_g.h:62
canvas_put_sprite
Definition canvas_g.h:42
struct canvas * pcanvas
Definition canvas_g.h:42
canvas_put_text
Definition canvas_g.h:80
struct canvas int int struct sprite int int offset_y
Definition canvas_g.h:44
struct canvas int int struct sprite int offset_x
Definition canvas_g.h:44
struct canvas int int struct sprite int int int width
Definition canvas_g.h:44
@ LINE_GOTO
Definition canvas_g.h:26
@ LINE_TILE_FRAME
Definition canvas_g.h:26
@ LINE_BORDER
Definition canvas_g.h:26
@ LINE_NORMAL
Definition canvas_g.h:26
const char * city_name_getx(const struct city *pcity)
Definition city.c:1166
const char * city_name_get(const struct city *pcity)
Definition city.c:1157
int city_production_turns_to_build(const struct city *pcity, bool include_shield_stock)
Definition city.c:824
bool city_can_be_built_here(const struct civ_map *nmap, const struct tile *ptile, const struct unit *punit, bool hut_test)
Definition city.c:1540
int rs_max_city_radius_sq(void)
Definition city.c:159
bool city_production_is_genus(const struct city *pcity, enum impr_genus_id genus)
Definition city.c:720
int city_turns_to_grow(const struct city *pcity)
Definition city.c:2049
#define cities_iterate_end
Definition city.h:517
#define city_list_iterate(citylist, pcity)
Definition city.h:508
#define city_tile(_pcity_)
Definition city.h:565
#define cities_iterate(pcity)
Definition city.h:512
static citizens city_size_get(const struct city *pcity)
Definition city.h:570
#define output_type_iterate(output)
Definition city.h:853
#define city_owner(_pcity_)
Definition city.h:564
#define city_list_iterate_end
Definition city.h:510
#define city_tile_iterate(_nmap, _radius_sq, _city_tile, _tile)
Definition city.h:228
#define city_tile_iterate_end
Definition city.h:236
#define output_type_iterate_end
Definition city.h:859
int get_citydlg_canvas_width(void)
bool city_can_buy(const struct city *pcity)
int get_citydlg_canvas_height(void)
bool client_is_global_observer(void)
struct civclient client
bool can_client_change_view(void)
#define client_player()
enum known_type client_tile_get_known(const struct tile *ptile)
Definition climap.c:36
bool client_city_can_work_tile(const struct city *pcity, const struct tile *ptile)
Definition climap.c:133
struct color * color_best_contrast(struct color *subject, struct color **candidates, int ncandidates)
struct color * get_player_color(const struct tileset *t, const struct player *pplayer)
struct color * get_color(const struct tileset *t, enum color_std stdcolor)
char * incite_cost
Definition comments.c:77
void set_units_in_combat(struct unit *pattacker, struct unit *pdefender)
Definition control.c:1044
enum cursor_hover_state hover_state
Definition control.c:89
@ HOVER_GOTO
Definition control.h:27
@ HOVER_PARADROP
Definition control.h:29
@ HOVER_TELEPORT
Definition control.h:28
@ HOVER_ACT_SEL_TGT
Definition control.h:32
@ HOVER_NONE
Definition control.h:26
@ HOVER_CONNECT
Definition control.h:30
@ HOVER_PATROL
Definition control.h:31
@ HOVER_GOTO_SEL_TGT
Definition control.h:33
struct unit struct city struct unit struct tile struct extra_type const struct act_prob *act_probs int actor_unit_id struct unit struct unit * punit
Definition dialogs_g.h:73
struct unit struct city struct unit struct tile struct extra_type const struct act_prob *act_probs int actor_unit_id struct unit struct unit int const struct action *paction struct unit struct city * pcity
Definition dialogs_g.h:77
int int id
Definition editgui_g.h:28
bool editor_is_active(void)
Definition editor.c:346
bool editor_tile_is_selected(const struct tile *ptile)
Definition editor.c:1117
#define DIR8_MAGIC_MAX
Definition fc_types.h:330
@ O_SHIELD
Definition fc_types.h:103
#define _(String)
Definition fcintl.h:67
text_link_type
@ TLT_TILE
@ TLT_UNIT
@ TLT_CITY
struct civ_game game
Definition game.c:62
struct world wld
Definition game.c:63
struct unit * game_unit_by_number(int id)
Definition game.c:115
struct city * game_city_by_number(int id)
Definition game.c:106
void canvas_put_sprite_full(struct canvas *pcanvas, int canvas_x, int canvas_y, struct sprite *sprite)
Definition canvas.c:144
void canvas_set_zoom(struct canvas *store, float zoom)
Definition canvas.c:53
void canvas_mapview_init(struct canvas *store)
Definition canvas.c:69
void get_text_size(int *width, int *height, enum client_font font, const char *text)
Definition canvas.c:341
void canvas_free(struct canvas *store)
Definition canvas.c:44
void canvas_copy(struct canvas *dest, struct canvas *src, int src_x, int src_y, int dest_x, int dest_y, int width, int height)
Definition canvas.c:76
struct canvas * canvas_create(int width, int height)
Definition canvas.c:28
void canvas_put_sprite_fogged(struct canvas *pcanvas, int canvas_x, int canvas_y, struct sprite *psprite, bool fog, int fog_x, int fog_y)
Definition canvas.c:170
void add_idle_callback(void(callback)(void *), void *data)
Definition gui_main.c:2374
void create_line_at_mouse_pos(void)
Definition mapctrl.c:355
void update_rect_at_mouse_pos(void)
Definition mapctrl.c:380
void flush_dirty(void)
Definition mapview.c:468
void dirty_all(void)
Definition mapview.c:456
void update_map_canvas_scrollbars_size(void)
Definition mapview.c:696
void gui_flush(void)
Definition mapview.c:480
void update_map_canvas_scrollbars(void)
Definition mapview.c:681
GType type
Definition repodlgs.c:1313
const char * name
Definition inputfile.c:127
#define fc_assert_ret(condition)
Definition log.h:192
#define fc_assert(condition)
Definition log.h:177
#define log_debug(message,...)
Definition log.h:116
const int DIR_DY[8]
Definition map.c:86
#define nat_x
#define nat_y
const int DIR_DX[8]
Definition map.c:85
struct tile * index_to_tile(const struct civ_map *imap, int mindex)
Definition map.c:471
struct tile * map_pos_to_tile(const struct civ_map *nmap, int map_x, int map_y)
Definition map.c:434
void base_map_distance_vector(int *dx, int *dy, int x0dv, int y0dv, int x1dv, int y1dv)
Definition map.c:1171
struct tile * mapstep(const struct civ_map *nmap, const struct tile *ptile, enum direction8 dir)
Definition map.c:384
struct tile * nearest_real_tile(const struct civ_map *nmap, int x, int y)
Definition map.c:1142
bool map_is_empty(void)
Definition map.c:148
bool normalize_map_pos(const struct civ_map *nmap, int *x, int *y)
Definition map.c:1126
#define current_topo_has_flag(flag)
Definition map.h:43
#define adjc_dir_iterate(nmap, center_tile, itr_tile, dir_itr)
Definition map.h:444
#define MAP_TO_NATIVE_POS(pnat_x, pnat_y, map_x, map_y)
Definition map.h:176
#define NATIVE_TO_MAP_POS(pmap_x, pmap_y, nat_x, nat_y)
Definition map.h:170
#define adjc_dir_iterate_end
Definition map.h:448
#define current_wrap_has_flag(flag)
Definition map.h:46
#define adjc_dir_base_iterate(nmap, center_tile, dir_itr)
Definition map.h:451
#define adjc_dir_base_iterate_end
Definition map.h:455
#define index_to_map_pos(pmap_x, pmap_y, mindex)
Definition map.h:229
bool rectangle_active
void mapdeco_set_highlight(const struct tile *ptile, bool highlight)
void link_mark_restore(enum text_link_type type, int id)
void put_nuke_mushroom_pixmaps(struct tile *ptile)
void init_mapcanvas_and_overview(void)
static struct gotoline_counter * gotoline_counter_new(void)
struct city * find_city_or_settler_near_tile(const struct tile *ptile, struct unit **punit)
void put_drawn_sprites(struct canvas *pcanvas, float zoom, int canvas_x, int canvas_y, int count, struct drawn_sprite *pdrawn, bool fog)
void update_map_canvas_visible(void)
static void anim_timer_renew(void)
bool tile_visible_mapcanvas(struct tile *ptile)
void set_mapview_origin(float gui_x0, float gui_y0, float zoom)
void put_terrain(struct tile *ptile, struct canvas *pcanvas, float zoom, int canvas_x, int canvas_y)
static const int MAX_TRADE_ROUTE_DRAW_LINES
void map_to_gui_vector(const struct tileset *t, float zoom, float *gui_dx, float *gui_dy, int map_dx, int map_dy)
void update_tile_label(struct tile *ptile)
tile_update_type
@ TILE_UPDATE_COUNT
@ TILE_UPDATE_TILE_SINGLE
@ TILE_UPDATE_TILE_LABEL
@ TILE_UPDATE_UNIT
@ TILE_UPDATE_CITY_DESC
@ TILE_UPDATE_CITYMAP
@ TILE_UPDATE_TILE_FULL
#define COLOR_MAPVIEW_TILELABEL
#define gotoline_hash_iterate(hash, ptile, pglc)
void mapdeco_set_crosshair(const struct tile *ptile, bool crosshair)
enum topo_comp_lvl tileset_map_topo_compatible(int topology_id, struct tileset *tset, int *tset_topo)
static int color_index
static int max_desc_width
static void link_mark_draw(const struct link_mark *pmark)
static void base_set_mapview_origin(float gui_x0, float gui_y0, float zoom)
static void queue_callback(void *data)
struct view mapview
void refresh_city_mapcanvas(struct city *pcity, struct tile *ptile, bool full_refresh, bool write_to_screen)
static void draw_trade_routes(void)
static void draw_trade_routes_for_city(const struct city *pcity_src)
void put_city(struct city *pcity, struct canvas *pcanvas, float zoom, int canvas_x, int canvas_y)
struct tile * get_center_tile_mapcanvas(void)
static struct link_mark * link_mark_new(enum text_link_type type, int id, int turns)
struct tile * canvas_pos_to_tile(float canvas_x, float canvas_y, float zoom)
update_type
@ UPDATE_TILE_LABELS
@ UPDATE_NONE
@ UPDATE_CITY_DESCRIPTIONS
@ UPDATE_MAP_CANVAS_VISIBLE
void put_unit(const struct unit *punit, struct canvas *pcanvas, float zoom, int canvas_x, int canvas_y)
struct city * find_city_near_tile(const struct tile *ptile)
void mapdeco_free(void)
void free_mapcanvas_and_overview(void)
struct gotoline_hash * mapdeco_gotoline_table
static bool movement_animation(struct animation *anim, double time_gone)
void get_mapview_scroll_pos(int *scroll_x, int *scroll_y)
bool mapdeco_is_highlight_set(const struct tile *ptile)
static void put_one_tile(struct canvas *pcanvas, enum mapview_layer layer, struct tile *ptile, int canvas_x, int canvas_y, const struct city *citymode)
void get_mapview_scroll_window(float *xmin, float *ymin, float *xmax, float *ymax, int *xsize, int *ysize)
static void show_small_citybar(struct canvas *pcanvas, int canvas_x, int canvas_y, struct city *pcity, int *width, int *height)
static bool frame_by_frame_animation
void move_unit_map_canvas(struct unit *punit, struct tile *src_tile, int dx, int dy)
animation_type
@ ANIM_NUKE
@ ANIM_MOVEMENT
@ ANIM_BATTLE
@ ANIM_EXPL
static void link_mark_destroy(struct link_mark *pmark)
void mapdeco_set_gotoroute(const struct unit *punit)
void unqueue_mapview_updates(bool write_to_screen)
void get_mapview_scroll_step(int *xstep, int *ystep)
void draw_segment(struct tile *src_tile, enum direction8 dir)
void show_city_descriptions(int canvas_base_x, int canvas_base_y, int width_base, int height_base)
void set_mapview_scroll_pos(int scroll_x, int scroll_y, float zoom)
static void gui_distance_vector(const struct tileset *t, float zoom, float *gui_dx, float *gui_dy, float gui_x0, float gui_y0, float gui_x1, float gui_y1)
void mapdeco_init(void)
void client_infratile_set(struct tile *ptile)
static void queue_add_callback(void)
static void static void show_city_desc(struct canvas *pcanvas, int canvas_x, int canvas_y, struct city *pcity, int *width, int *height) fc__attribute((nonnull(5
void get_city_mapview_trade_routes(struct city *pcity, char *trade_routes_buffer, size_t trade_routes_buffer_len, enum color_std *pcolor)
#define NUM_CITY_COLORS
void put_unittype(const struct unit_type *putype, struct canvas *pcanvas, float zoom, int canvas_x, int canvas_y)
void mapdeco_clear_gotoroutes(void)
static enum update_type needed_updates
void put_unit_city_overlays(struct unit *punit, struct canvas *pcanvas, int canvas_x, int canvas_y, int *upkeep_cost, int happy_cost)
void put_one_element(struct canvas *pcanvas, float zoom, enum mapview_layer layer, const struct tile *ptile, const struct tile_edge *pedge, const struct tile_corner *pcorner, const struct unit *punit, const struct city *pcity, int canvas_x, int canvas_y, const struct city *citymode, const struct unit_type *putype)
bool mapdeco_is_gotoline_set(const struct tile *ptile, enum direction8 dir)
#define link_marks_iterate(pmark)
struct tile * infratile
static bool nuke_animation(struct animation *anim, double time_gone)
void center_tile_mapcanvas(const struct tile *ptile)
void animations_init(void)
static int max_label_height
static bool battle_animation(struct animation *anim, double time_gone)
void update_animation(void)
void toggle_city_color(struct city *pcity)
struct tile * canvas_pos_to_nearest_tile(float canvas_x, float canvas_y, float zoom)
void mapdeco_add_gotoline(const struct tile *ptile, enum direction8 dir)
void update_map_canvas(int canvas_x, int canvas_y, int width, int height)
static struct color * link_mark_color(const struct link_mark *pmark)
const struct tile * center_tile
void link_marks_decrease_turn_counters(void)
static void normalize_gui_pos(const struct tileset *t, float zoom, float *gui_x, float *gui_y)
#define link_marks_iterate_end
void mapdeco_clear_crosshairs(void)
void get_city_mapview_production(struct city *pcity, char *buffer, size_t buffer_len)
static void base_canvas_to_map_pos(float zoom, int *map_x, int *map_y, float canvas_x, float canvas_y)
void link_marks_draw_all(void)
void update_city_description(struct city *pcity)
void link_marks_init(void)
struct tile * client_infratile(void)
void set_frame_by_frame_animation(void)
void mapdeco_clear_highlights(void)
void link_marks_clear_all(void)
const char * describe_topology(int topo)
static int trade_route_to_canvas_lines(const struct tile *ptile1, const struct tile *ptile2, struct trade_route_line *lines)
static void queue_mapview_update(enum update_type update)
static int max_label_width
struct animation_list * animations
void toggle_unit_color(struct unit *punit)
static struct link_mark * link_mark_find(enum text_link_type type, int id)
void animations_free(void)
struct tile_hash * mapdeco_highlight_table
static struct timer * anim_timer
static void queue_mapview_tile_update(struct tile *ptile, enum tile_update_type type)
static bool callback_queued
static void show_full_citybar(struct canvas *pcanvas, const int canvas_x0, const int canvas_y0, struct city *pcity, int *width, int *height) fc__attribute((nonnull(5
static void map_to_gui_pos(const struct tileset *t, float zoom, float *gui_x, float *gui_y, int map_x, int map_y)
static int max_desc_height
bool map_canvas_resized(int width, int height)
void link_mark_add_new(enum text_link_type type, int id)
bool can_slide
void refresh_tile_mapcanvas(struct tile *ptile, bool full_refresh, bool write_to_screen)
static void draw_trade_route_line(const struct tile *ptile1, const struct tile *ptile2, enum color_std color)
void refresh_unit_mapcanvas(struct unit *punit, struct tile *ptile, bool full_refresh, bool write_to_screen)
static bool explosion_animation(struct animation *anim, double time_gone)
struct tile_list * tile_updates[TILE_UPDATE_COUNT]
bool tile_to_canvas_pos(float *canvas_x, float *canvas_y, float zoom, const struct tile *ptile)
static struct link_mark_list * link_marks
void get_spaceship_dimensions(int *width, int *height)
void decrease_unit_hp_smooth(struct unit *punit0, int hp0, struct unit *punit1, int hp1)
static void show_tile_label(struct canvas *pcanvas, int canvas_x, int canvas_y, struct tile *ptile, int *width, int *height)
static void animation_add(struct animation *anim)
void get_city_mapview_name_and_growth(struct city *pcity, char *name_buffer, size_t name_buffer_len, char *growth_buffer, size_t growth_buffer_len, enum color_std *growth_color, enum color_std *production_color)
bool tile_visible_and_not_on_border_mapcanvas(struct tile *ptile)
struct tile_hash * mapdeco_crosshair_table
void put_spaceship(struct canvas *pcanvas, int canvas_x, int canvas_y, const struct player *pplayer)
static struct tile * link_mark_tile(const struct link_mark *pmark)
static void append_city_buycost_string(const struct city *pcity, char *buffer, int buffer_len)
void mapdeco_remove_gotoline(const struct tile *ptile, enum direction8 dir)
#define gotoline_hash_iterate_end
static bool can_do_cached_drawing(void)
bool mapdeco_is_crosshair_set(const struct tile *ptile)
void link_marks_free(void)
void show_tile_labels(int canvas_base_x, int canvas_base_y, int width_base, int height_base)
static bool calc_mapview_origin(float *gui_x0, float *gui_y0, float zoom)
static void gotoline_counter_destroy(struct gotoline_counter *pglc)
static void gui_to_map_pos(const struct tileset *t, float zoom, int *map_x, int *map_y, float gui_x, float gui_y)
#define gui_rect_iterate_coord(GRI_x0, GRI_y0, GRI_width, GRI_height, _t, _e, _c, _x, _y, _zoom)
#define gui_rect_iterate_coord_end
#define gui_rect_iterate_end
#define GOTO_WIDTH
topo_comp_lvl
@ TOPO_COMPATIBLE
@ TOPO_INCOMP_HARD
@ TOPO_INCOMP_SOFT
#define gui_rect_iterate(GRI_x0, GRI_y0, GRI_width, GRI_height, _t, _e, _c, _zoom)
dirty_rect
Definition mapview_g.h:47
#define H(x, y, z)
Definition md5.c:92
#define fc_calloc(n, esz)
Definition mem.h:38
#define fc_malloc(sz)
Definition mem.h:34
struct client_options gui_options
Definition options.c:71
void flush_dirty_overview(void)
void refresh_overview_canvas(void)
void center_tile_overviewcanvas(void)
void overview_update_tile(struct tile *ptile)
char * lines
Definition packhand.c:133
bool can_player_see_units_in_city(const struct player *pplayer, const struct city *pcity)
Definition player.c:1124
#define fc_rand(_size)
Definition rand.h:56
const char * universal_name_translation(const struct universal *psource, char *buf, size_t bufsz)
#define CLIP(lower, current, upper)
Definition shared.h:57
#define DIVIDE(n, d)
Definition shared.h:78
#define ARRAY_SIZE(x)
Definition shared.h:85
#define FC_WRAP(value, range)
Definition shared.h:65
#define MIN(x, y)
Definition shared.h:55
#define FC_INFINITY
Definition shared.h:36
#define ABS(x)
Definition shared.h:61
#define MAX(x, y)
Definition shared.h:54
#define XOR(p, q)
Definition shared.h:71
const struct sship_part_info structurals_info[NUM_SS_STRUCTURALS]
Definition spaceship.c:23
const struct sship_part_info modules_info[NUM_SS_MODULES]
Definition spaceship.c:77
const struct sship_part_info components_info[NUM_SS_COMPONENTS]
Definition spaceship.c:58
#define NUM_SS_MODULES
Definition spaceship.h:89
#define NUM_SS_COMPONENTS
Definition spaceship.h:88
#define NUM_SS_STRUCTURALS
Definition spaceship.h:87
@ SSHIP_LAUNCHED
Definition spaceship.h:85
int step
Definition specpq.h:92
size_t size
Definition specvec.h:72
struct sprite int int y
Definition sprite_g.h:31
struct sprite int x
Definition sprite_g.h:31
struct sprite int int int int struct sprite int int float bool smooth get_sprite_dimensions
Definition sprite_g.h:36
struct sprite ** sprites
Definition tilespec.c:172
const struct sprite_vector * sprites
struct tile * loser_tile
struct tile * dest
struct unit * virt_loser
struct tile * winner_tile
struct tile * nuke_tile
struct tile * tile
struct tile * src
struct animation::@228::@230 movement
struct unit * mover
struct animation::@228::@232 expl
enum animation_type type
struct animation::@228::@233 nuke
struct unit * virt_winner
struct animation::@228::@231 battle
float zoom
Definition canvas.h:25
Definition city.h:318
struct sprite * food
Definition tilespec.h:335
struct sprite * trade
Definition tilespec.h:336
struct sprite * shields
Definition tilespec.h:333
struct sprite * background
Definition tilespec.h:338
struct sprite * occupied
Definition tilespec.h:337
struct sprite_vector occupancy
Definition tilespec.h:339
struct packet_scenario_info scenario
Definition game.h:87
struct connection conn
Definition client_main.h:96
bool draw_native
Definition options.h:216
int smooth_center_slide_msec
Definition options.h:153
bool draw_city_names
Definition options.h:197
bool draw_city_productions
Definition options.h:199
int smooth_move_unit_msec
Definition options.h:152
bool draw_borders
Definition options.h:215
bool draw_city_buycost
Definition options.h:200
bool draw_fog_of_war
Definition options.h:214
bool draw_city_trade_routes
Definition options.h:201
bool draw_map_grid
Definition options.h:196
bool draw_city_growth
Definition options.h:198
int smooth_combat_step_msec
Definition options.h:154
bool draw_full_citybar
Definition options.h:217
Definition colors.h:21
struct player * playing
Definition connection.h:151
int line_count[DIR8_MAGIC_MAX]
struct city_list * cities
Definition player.h:281
struct player_spaceship spaceship
Definition player.h:286
Definition tile.h:50
char * label
Definition tile.h:66
struct unit_list * units
Definition tile.h:58
Definition timing.c:81
struct veteran_system * veteran
Definition unittype.h:551
Definition unit.h:139
int length
Definition unit.h:197
bool occupied
Definition unit.h:221
int id
Definition unit.h:146
struct unit::@83 orders
int index
Definition unit.h:197
int hp
Definition unit.h:152
bool colored
Definition unit.h:224
enum direction8 facing
Definition unit.h:143
struct tile * tile
Definition unit.h:141
struct unit::@84::@86 client
struct unit_order * list
Definition unit.h:200
bool repeat
Definition unit.h:198
int color_index
Definition unit.h:225
int veteran
Definition unit.h:153
int height
int store_width
float gui_x0
int tile_height
bool can_do_cached_drawing
int store_height
struct canvas * store
struct canvas * tmp_store
float gui_y0
int width
int tile_width
struct civ_map map
int fc_snprintf(char *str, size_t n, const char *format,...)
Definition support.c:925
size_t fc_strlcpy(char *dest, const char *src, size_t n)
Definition support.c:742
void fc_usleep(unsigned long usec)
Definition support.c:604
int cat_snprintf(char *str, size_t n, const char *format,...)
Definition support.c:951
#define fc__attribute(x)
Definition support.h:99
#define TRUE
Definition support.h:46
#define FALSE
Definition support.h:47
void canvas_put_flag_sprite(struct canvas *pcanvas, int canvas_x, int canvas_y, int canvas_w, int canvas_h, struct sprite *flag)
Definition svgflag.c:137
void get_flag_dimensions(struct sprite *flag, struct area_rect *rect, int svg_height)
Definition svgflag.c:123
struct city * tile_city(const struct tile *ptile)
Definition tile.c:83
#define tile_index(_pt_)
Definition tile.h:89
#define tile_hash_iterate(hash, ptile)
Definition tile.h:83
#define tile_worked(_tile)
Definition tile.h:119
@ TILE_KNOWN_UNSEEN
Definition tile.h:37
@ TILE_UNKNOWN
Definition tile.h:36
#define tile_list_iterate(tile_list, ptile)
Definition tile.h:74
#define tile_hash_iterate_end
Definition tile.h:85
#define TILE_XY(ptile)
Definition tile.h:43
#define tile_list_iterate_end
Definition tile.h:76
struct sprite * get_nuke_explode_sprite(const struct tileset *t)
Definition tilespec.c:7208
int tileset_hex_width(const struct tileset *t)
Definition tilespec.c:747
int tileset_unit_width(const struct tileset *t)
Definition tilespec.c:823
struct unit * get_drawable_unit(const struct tileset *t, struct tile *ptile, const struct city *citymode)
Definition tilespec.c:6805
int tileset_unit_height(const struct tileset *t)
Definition tilespec.c:831
int tileset_svg_flag_height(struct tileset *t)
Definition tilespec.c:7854
struct sprite * get_city_flag_sprite(const struct tileset *t, const struct city *pcity)
Definition tilespec.c:4670
int tileset_full_tile_height(const struct tileset *t)
Definition tilespec.c:815
struct sprite * get_unit_upkeep_sprite(const struct tileset *t, Output_type_id otype, const struct unit *punit, const int *upkeep_cost)
Definition tilespec.c:7314
int tileset_citybar_offset_y(const struct tileset *t)
Definition tilespec.c:936
bool unit_drawn_with_city_outline(const struct unit *punit, bool check_focus)
Definition tilespec.c:5731
const struct sprite_vector * get_unit_explode_animation(const struct tileset *t)
Definition tilespec.c:7197
bool tileset_is_isometric(const struct tileset *t)
Definition tilespec.c:738
int tileset_tile_height(const struct tileset *t)
Definition tilespec.c:791
struct sprite * get_spaceship_sprite(const struct tileset *t, enum spaceship_part part)
Definition tilespec.c:6956
int tileset_hex_height(const struct tileset *t)
Definition tilespec.c:756
const struct citybar_sprites * get_citybar_sprites(const struct tileset *t)
Definition tilespec.c:7216
struct sprite * get_unit_unhappy_sprite(const struct tileset *t, const struct unit *punit, int happy_cost)
Definition tilespec.c:7295
int fill_sprite_array(struct tileset *t, struct drawn_sprite *sprs, enum mapview_layer layer, const struct tile *ptile, const struct tile_edge *pedge, const struct tile_corner *pcorner, const struct unit *punit, const struct city *pcity, const struct city *citymode, const struct unit_type *putype)
Definition tilespec.c:6033
int tileset_tile_width(const struct tileset *t)
Definition tilespec.c:779
int tileset_tilelabel_offset_y(const struct tileset *t)
Definition tilespec.c:945
#define mapview_layer_iterate(layer)
Definition tilespec.h:178
#define mapview_layer_iterate_end
Definition tilespec.h:186
@ SPACESHIP_STRUCTURAL
Definition tilespec.h:325
@ SPACESHIP_HABITATION
Definition tilespec.h:324
@ SPACESHIP_EXHAUST
Definition tilespec.h:328
@ SPACESHIP_FUEL
Definition tilespec.h:326
@ SPACESHIP_PROPULSION
Definition tilespec.h:327
@ SPACESHIP_SOLAR_PANEL
Definition tilespec.h:322
@ SPACESHIP_LIFE_SUPPORT
Definition tilespec.h:323
void timer_usleep_since_start(struct timer *t, long usec)
Definition timing.c:398
void timer_start(struct timer *t)
Definition timing.c:263
double timer_read_seconds(struct timer *t)
Definition timing.c:379
struct timer * timer_renew(struct timer *t, enum timer_timetype type, enum timer_use use, const char *name)
Definition timing.c:180
@ TIMER_ACTIVE
Definition timing.h:46
@ TIMER_USER
Definition timing.h:42
unsigned max_trade_routes(const struct city *pcity)
Definition traderoutes.c:48
#define trade_partners_iterate_end
#define trade_partners_iterate(c, p)
bool unit_can_do_action(const struct unit *punit, const action_id act_id)
Definition unit.c:402
struct unit * unit_virtual_create(struct player *pplayer, struct city *pcity, const struct unit_type *punittype, int veteran_level)
Definition unit.c:1682
void unit_virtual_destroy(struct unit *punit)
Definition unit.c:1787
bool unit_has_orders(const struct unit *punit)
Definition unit.c:221
#define unit_tile(_pu)
Definition unit.h:408
@ ORDER_MOVE
Definition unit.h:39
#define unit_owner(_pu)
Definition unit.h:407
#define unit_list_iterate(unitlist, punit)
Definition unitlist.h:31
#define unit_list_iterate_end
Definition unitlist.h:33
const struct unit_type * unit_type_get(const struct unit *punit)
Definition unittype.c:126
#define MAP_NATIVE_WIDTH
#define MAP_IS_ISOMETRIC
#define MAP_NATURAL_HEIGHT
#define MAP_NATURAL_WIDTH
#define MAP_NATIVE_HEIGHT
float mouse_zoom
Definition zoom.c:28
float map_zoom
Definition zoom.c:25
#define zoom_is_enabled()
Definition zoom.h:26