Freeciv-3.2
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 = NULL;
81
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 = NULL;
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 {
157 int steps;
159 struct {
160 struct tile *tile;
161 const struct sprite_vector *sprites;
164 struct {
165 bool shown;
168 };
169};
170
171#define SPECLIST_TAG animation
172#define SPECLIST_TYPE struct animation
173#include "speclist.h"
174
176
177/************************************************************************/
181{
183}
184
185/************************************************************************/
189{
190 if (animations != NULL) {
191 int i;
192 size_t last = animation_list_size(animations);
193
194 for (i = 0; i < last; i++) {
196
197 switch (anim->type) {
198 case ANIM_MOVEMENT:
199 unit_virtual_destroy(anim->movement.mover);
200 break;
201 case ANIM_BATTLE:
202 unit_virtual_destroy(anim->battle.virt_winner);
203 unit_virtual_destroy(anim->battle.virt_loser);
204 break;
205 case ANIM_EXPL:
206 case ANIM_NUKE:
207 /* Nothing to free */
208 break;
209 }
210
211 free(anim);
212 }
213
216 }
217}
218
219/************************************************************************/
228
229/************************************************************************/
232static void animation_add(struct animation *anim)
233{
236 }
237
238 anim->finished = FALSE;
239 anim->old_x = -1; /* Initial frame */
241}
242
243/************************************************************************/
246static bool movement_animation(struct animation *anim, double time_gone)
247{
248 float start_x, start_y;
249 int new_x, new_y;
251 double mytime = MIN(time_gone, timing_sec);
252 struct unit *punit = anim->movement.mover;
253
254 if (punit != NULL) {
255 tile_to_canvas_pos(&start_x, &start_y, map_zoom, anim->movement.src);
259 }
260 new_x = start_x + anim->movement.canvas_dx * (mytime / timing_sec);
261 new_y = start_y + anim->movement.canvas_dy * (mytime / timing_sec);
262
263 if (anim->old_x >= 0) {
264 update_map_canvas(anim->old_x, anim->old_y,
265 anim->width, anim->height);
266 }
268 dirty_rect(new_x, new_y, anim->width, anim->height);
269 anim->old_x = new_x;
270 anim->old_y = new_y;
271
272 if (time_gone >= timing_sec) {
273 /* Animation over */
274 unit_virtual_destroy(anim->movement.mover);
275
276 return TRUE;
277 }
278 } else {
279 return TRUE;
280 }
281
282 return FALSE;
283}
284
285/************************************************************************/
288static bool battle_animation(struct animation *anim, double time_gone)
289{
290 double time_per_step;
291 int step;
292 float canvas_x, canvas_y;
294 * anim->battle.steps / 1000.0;
295
296 if (time_gone >= timing_sec) {
297 /* Animation over */
298
299 unit_virtual_destroy(anim->battle.virt_winner);
300 unit_virtual_destroy(anim->battle.virt_loser);
301
302 return TRUE;
303 }
304
305 time_per_step = timing_sec / anim->battle.steps;
307
308 if (tile_to_canvas_pos(&canvas_x, &canvas_y, map_zoom, anim->battle.loser_tile)) {
309 anim->battle.virt_loser->hp
310 = anim->battle.loser_hp_start - (anim->battle.loser_hp_start
311 * step / anim->battle.steps);
312
316 }
317
318 put_unit(anim->battle.virt_loser, mapview.store, map_zoom,
323 }
324
325 if (tile_to_canvas_pos(&canvas_x, &canvas_y, map_zoom, anim->battle.winner_tile)) {
326 anim->battle.virt_winner->hp
327 = anim->battle.winner_hp_start - ((anim->battle.winner_hp_start
328 - anim->battle.winner_hp_end)
329 * step / anim->battle.steps);
330
334 }
335 put_unit(anim->battle.virt_winner, mapview.store, map_zoom,
340 }
341
342 return FALSE;
343}
344
345/************************************************************************/
348static bool explosion_animation(struct animation *anim, double time_gone)
349{
350 float canvas_x, canvas_y;
351 double timing_sec;
352
353 if (anim->expl.sprite_count <= 0) {
354 return TRUE;
355 }
356
358 * anim->expl.sprite_count / 1000.0;
359
360 if (tile_to_canvas_pos(&canvas_x, &canvas_y, map_zoom, anim->expl.tile)) {
361 double time_per_frame = timing_sec / anim->expl.sprite_count;
362 int frame = time_gone / time_per_frame;
363 struct sprite *spr;
364 int w, h;
365
366 frame = MIN(frame, anim->expl.sprite_count - 1);
367
368 if (anim->old_x >= 0) {
369 update_map_canvas(anim->old_x, anim->old_y,
370 anim->width, anim->height);
371 }
372
373 spr = *sprite_vector_get(anim->expl.sprites, frame);
374 get_sprite_dimensions(spr, &w, &h);
375
378 - w / 2,
380 - h / 2,
381 spr);
384
385 anim->old_x = canvas_x;
386 anim->old_y = canvas_y;
387 }
388
389 if (time_gone >= timing_sec) {
390 /* Animation over */
391 return TRUE;
392 }
393
394 return FALSE;
395}
396
397/************************************************************************/
400static bool nuke_animation(struct animation *anim, double time_gone)
401{
402 if (!anim->nuke.shown) {
403 float canvas_x, canvas_y;
405 int w, h;
406
407 (void) tile_to_canvas_pos(&canvas_x, &canvas_y, map_zoom, anim->nuke.nuke_tile);
409
412 - w / 2,
414 - h / 2,
415 nuke_spr);
418
419 anim->old_x = canvas_x;
420 anim->old_y = canvas_y;
421
422 anim->nuke.shown = TRUE;
423
424 return FALSE;
425 }
426
427 if (time_gone > 1.0) {
429
430 return TRUE;
431 }
432
433 return FALSE;
434}
435
436/************************************************************************/
440{
443
444 if (anim->finished) {
445 /* Animation over */
446
447 anim->id = -1;
448 if (anim->old_x >= 0) {
449 update_map_canvas(anim->old_x, anim->old_y, anim->width, anim->height);
450 }
452 free(anim);
453
455 /* Start next */
457 }
458 } else {
460 bool finished = FALSE;
461
462 switch (anim->type) {
463 case ANIM_MOVEMENT:
465 break;
466 case ANIM_BATTLE:
468 break;
469 case ANIM_EXPL:
471 break;
472 case ANIM_NUKE:
474 }
475
476 if (finished) {
477 anim->finished = TRUE;
478 }
479 }
480 }
481}
482
483/************************************************************************/
486static inline struct gotoline_counter *gotoline_counter_new(void)
487{
488 struct gotoline_counter *pglc = fc_calloc(1, sizeof(*pglc));
489 return pglc;
490}
491
492/************************************************************************/
496{
498 free(pglc);
499}
500
501/************************************************************************/
504void refresh_tile_mapcanvas(struct tile *ptile,
505 bool full_refresh, bool write_to_screen)
506{
507 if (full_refresh) {
509 } else {
511 }
512 if (write_to_screen) {
514 }
515}
516
517/************************************************************************/
520void refresh_unit_mapcanvas(struct unit *punit, struct tile *ptile,
521 bool full_refresh, bool write_to_screen)
522{
523 if (full_refresh && gui_options.draw_native) {
525 } else if (full_refresh && unit_drawn_with_city_outline(punit, TRUE)) {
527 } else {
529 }
530 if (write_to_screen) {
532 }
533}
534
535/************************************************************************/
541void refresh_city_mapcanvas(struct city *pcity, struct tile *ptile,
542 bool full_refresh, bool write_to_screen)
543{
544 if (full_refresh && (gui_options.draw_map_grid || gui_options.draw_borders)) {
546 } else {
548 }
549 if (write_to_screen) {
551 }
552}
553
554/************************************************************************/
563void map_to_gui_vector(const struct tileset *t, float zoom,
564 float *gui_dx, float *gui_dy, int map_dx, int map_dy)
565{
566 if (tileset_is_isometric(t)) {
567 /*
568 * Convert the map coordinates to isometric GUI
569 * coordinates. We'll make tile map(0,0) be the origin, and
570 * transform like this:
571 *
572 * 3
573 * 123 2 6
574 * 456 -> becomes -> 1 5 9
575 * 789 4 8
576 * 7
577 */
578 *gui_dx = (map_dx - map_dy) * tileset_tile_width(t) / 2 * zoom;
579 *gui_dy = (map_dx + map_dy) * tileset_tile_height(t) / 2 * zoom;
580 } else {
581 *gui_dx = map_dx * tileset_tile_height(t) * zoom;
582 *gui_dy = map_dy * tileset_tile_width(t) * zoom;
583 }
584}
585
586/************************************************************************/
592static void map_to_gui_pos(const struct tileset *t, float zoom,
593 float *gui_x, float *gui_y, int map_x, int map_y)
594{
595 /* Since the GUI origin is the same as the map origin we can just do a
596 * vector conversion. */
598}
599
600/************************************************************************/
607static void gui_to_map_pos(const struct tileset *t, float zoom,
608 int *map_x, int *map_y, float gui_x, float gui_y)
609{
610 const float W = tileset_tile_width(t) * zoom, H = tileset_tile_height(t) * zoom;
611 const float HH = tileset_hex_height(t) * zoom, HW = tileset_hex_width(t) * zoom;
612
613 if (HH > 0 || HW > 0) {
614 /* To handle hexagonal cases we have to revert to a less elegant method
615 * of calculation. */
616 float x, y;
617 int dx, dy;
618 int xmult, ymult, mod, compar;
619
621
622 x = DIVIDE((int)gui_x, (int)W);
623 y = DIVIDE((int)gui_y, (int)H);
624 dx = gui_x - x * W;
625 dy = gui_y - y * H;
626 fc_assert(dx >= 0 && dx < W);
627 fc_assert(dy >= 0 && dy < H);
628
629 /* Now fold so we consider only one-quarter tile. */
630 xmult = (dx >= W / 2) ? -1 : 1;
631 ymult = (dy >= H / 2) ? -1 : 1;
632 dx = (dx >= W / 2) ? (W - 1 - dx) : dx;
633 dy = (dy >= H / 2) ? (H - 1 - dy) : dy;
634
635 /* Next compare to see if we're across onto the next tile. */
636 if (HW > 0) {
637 compar = (dx - HW / 2) * (H / 2) - (H / 2 - 1 - dy) * (W / 2 - HW);
638 } else {
639 compar = (dy - HH / 2) * (W / 2) - (W / 2 - 1 - dx) * (H / 2 - HH);
640 }
641 mod = (compar < 0) ? -1 : 0;
642
643 *map_x = (x + y) + mod * (xmult + ymult) / 2;
644 *map_y = (y - x) + mod * (ymult - xmult) / 2;
645 } else if (tileset_is_isometric(t)) {
646 /* The basic operation here is a simple pi/4 rotation; however, we
647 * have to first scale because the tiles have different width and
648 * height. Mathematically, this looks like
649 * | 1/W 1/H | |x| |x`|
650 * | | | | -> | |
651 * |-1/W 1/H | |y| |y`|
652 *
653 * Where W is the tile width and H the height.
654 *
655 * In simple terms, this is
656 * map_x = [ x / W + y / H ]
657 * map_y = [ - x / W + y / H ]
658 * where [q] stands for integer part of q.
659 *
660 * Here the division is proper mathematical floating point division.
661 *
662 * We have to subtract off a half-tile in the X direction before doing
663 * the transformation. This is because, although the origin of the tile
664 * is the top-left corner of the bounding box, after the transformation
665 * the top corner of the diamond-shaped tile moves into this position.
666 *
667 * The calculation is complicated somewhat because of two things: we
668 * only use integer math, and C integer division rounds toward zero
669 * instead of rounding down.
670 *
671 * For another example of this math, see canvas_to_city_pos().
672 */
673 gui_x -= W / 2;
674 *map_x = DIVIDE((int)(gui_x * H + gui_y * W), (int)(W * H));
675 *map_y = DIVIDE((int)(gui_y * W - gui_x * H), (int)(W * H));
676 } else { /* tileset_is_isometric(t) */
677 /* We use DIVIDE so that we will get the correct result even
678 * for negative coordinates. */
679 *map_x = DIVIDE((int)gui_x, (int)W);
680 *map_y = DIVIDE((int)gui_y, (int)H);
681 }
682}
683
684/************************************************************************/
706bool tile_to_canvas_pos(float *canvas_x, float *canvas_y, float zoom,
707 const struct tile *ptile)
708{
709 int center_map_x, center_map_y, dx, dy, tile_x, tile_y;
710
711 /*
712 * First we wrap the coordinates to hopefully be within the mapview
713 * window. We do this by finding the position closest to the center
714 * of the window.
715 */
716 /* TODO: Cache the value of this position */
718 mapview.width / 2,
719 mapview.height / 2);
722 tile_y);
723
728
729 /*
730 * Finally we clip.
731 *
732 * This check is tailored to work for both iso-view and classic view.
733 * Note that (canvas_x, canvas_y) need not be aligned to a tile boundary, and
734 * that the position is at the top-left of the NORMAL (not UNIT) tile.
735 * This checks to see if _any part_ of the tile is present on the backing
736 * store. Even if it's not visible on the canvas, if it's present on the
737 * backing store we need to draw it in case the canvas is resized.
738 */
745}
746
747/************************************************************************/
751static void base_canvas_to_map_pos(float zoom, int *map_x, int *map_y,
752 float canvas_x, float canvas_y)
753{
755 canvas_x + mapview.gui_x0 / map_zoom * zoom,
756 canvas_y + mapview.gui_y0 / map_zoom * zoom);
757}
758
759/************************************************************************/
764 float zoom)
765{
766 int map_x, map_y;
767
769 if (normalize_map_pos(&(wld.map), &map_x, &map_y)) {
770 return map_pos_to_tile(&(wld.map), map_x, map_y);
771 } else {
772 return NULL;
773 }
774}
775
776/************************************************************************/
781 float zoom)
782{
783 int map_x, map_y;
784
786
787 return nearest_real_tile(&(wld.map), map_x, map_y);
788}
789
790/************************************************************************/
794static void normalize_gui_pos(const struct tileset *t, float zoom,
795 float *gui_x, float *gui_y)
796{
798 float gui_x0, gui_y0;
799
800 /* Convert the (gui_x, gui_y) into a (map_x, map_y) plus a GUI offset
801 * from this tile. */
802 gui_to_map_pos(t, zoom, &map_x, &map_y, *gui_x, *gui_y);
803 map_to_gui_pos(t, zoom, &gui_x0, &gui_y0, map_x, map_y);
804 diff_x = *gui_x - gui_x0;
805 diff_y = *gui_y - gui_y0;
806
807 /* Perform wrapping without any realness check. It's important that
808 * we wrap even if the map position is unreal, which normalize_map_pos()
809 * doesn't necessarily do. */
813 }
816 }
818
819 /* Now convert the wrapped map position back to a GUI position and add the
820 * offset back on. */
821 map_to_gui_pos(t, zoom, gui_x, gui_y, map_x, map_y);
822 *gui_x += diff_x;
823 *gui_y += diff_y;
824}
825
826/************************************************************************/
830static void gui_distance_vector(const struct tileset *t, float zoom,
831 float *gui_dx, float *gui_dy,
832 float gui_x0, float gui_y0,
833 float gui_x1, float gui_y1)
834{
835 int map_x0, map_y0, map_x1, map_y1;
838 int map_dx, map_dy;
839
840 /* Make sure positions are canonical. Yes, this is the only way. */
841 normalize_gui_pos(t, zoom, &gui_x0, &gui_y0);
842 normalize_gui_pos(t, zoom, &gui_x1, &gui_y1);
843
844 /* Now we have to find the offset of each GUI position from its tile
845 * origin. This is complicated: it means converting to a map position and
846 * then back to the GUI position to find the tile origin, then subtracting
847 * to get the offset. */
848 gui_to_map_pos(t, zoom, &map_x0, &map_y0, gui_x0, gui_y0);
850
851 map_to_gui_pos(t, zoom, &gui_x0_base, &gui_y0_base, map_x0, map_y0);
853
854 gui_x0_diff = gui_x0 - gui_x0_base;
855 gui_y0_diff = gui_y0 - gui_y0_base;
858
859 /* Next we find the map distance vector and convert this into a GUI
860 * vector. */
861 base_map_distance_vector(&map_dx, &map_dy, map_x0, map_y0, map_x1, map_y1);
863
864 /* Finally we add on the difference in offsets to retain pixel
865 * resolution. */
868}
869
870/************************************************************************/
874static void base_set_mapview_origin(float gui_x0, float gui_y0, float zoom)
875{
876 float old_gui_x0, old_gui_y0;
877 float dx, dy;
878 const int width = mapview.width, height = mapview.height;
881
882 /* Then update everything. This does some tricky math to avoid having
883 * to do unnecessary redraws in update_map_canvas(). This makes for ugly
884 * code but speeds up the mapview by a large factor. */
885
886 /* We need to calculate the vector of movement of the mapview. So
887 * we find the GUI distance vector and then use this to calculate
888 * the original mapview origin relative to the current position. Thus
889 * if we move one tile to the left, even if this causes GUI positions
890 * to wrap the distance vector is only one tile. */
891 normalize_gui_pos(tileset, zoom, &gui_x0, &gui_y0);
892 gui_distance_vector(tileset, zoom, &dx, &dy,
894 gui_x0, gui_y0);
895 old_gui_x0 = gui_x0 - dx;
896 old_gui_y0 = gui_y0 - dy;
897
898 mapview.gui_x0 = gui_x0;
899 mapview.gui_y0 = gui_y0;
900
901 /* Find the overlapping area of the new and old mapview. This is
902 * done in GUI coordinates. Note that if the GUI coordinates wrap
903 * no overlap will be found. */
904 common_x0 = MAX(old_gui_x0, gui_x0);
905 common_x1 = MIN(old_gui_x0, gui_x0) + width;
906 common_y0 = MAX(old_gui_y0, gui_y0);
907 common_y1 = MIN(old_gui_y0, gui_y0) + height;
908
911 /* Do a partial redraw only. This means the area of overlap (a
912 * rectangle) is copied. Then the remaining areas (two rectangles)
913 * are updated through update_map_canvas(). */
914 struct canvas *target = mapview.tmp_store;
915
916 if (old_gui_x0 < gui_x0) {
917 update_x0 = MAX(old_gui_x0 + width, gui_x0);
918 update_x1 = gui_x0 + width;
919 } else {
920 update_x0 = gui_x0;
921 update_x1 = MIN(old_gui_x0, gui_x0 + width);
922 }
923 if (old_gui_y0 < gui_y0) {
924 update_y0 = MAX(old_gui_y0 + height, gui_y0);
925 update_y1 = gui_y0 + height;
926 } else {
927 update_y0 = gui_y0;
928 update_y1 = MIN(old_gui_y0, gui_y0 + height);
929 }
930
931 dirty_all();
932 canvas_copy(target, mapview.store,
935 common_x0 - gui_x0, common_y0 - gui_y0,
938 mapview.store = target;
939
940 if (update_y1 > update_y0) {
941 update_map_canvas(0, update_y0 - gui_y0,
943 }
944 if (update_x1 > update_x0) {
945 update_map_canvas(update_x0 - gui_x0, common_y0 - gui_y0,
947 }
948 } else {
949 dirty_all();
951 }
952
954
955 switch (hover_state) {
956 case HOVER_GOTO:
957 case HOVER_PATROL:
958 case HOVER_CONNECT:
960 break;
962 case HOVER_NONE:
963 case HOVER_TELEPORT:
964 case HOVER_PARADROP:
966 break;
967 }
968
969 if (rectangle_active) {
971 }
972}
973
974/************************************************************************/
978static bool calc_mapview_origin(float *gui_x0, float *gui_y0, float zoom)
979{
980 float xmin, ymin, xmax, ymax;
981 int xsize, ysize;
982
983 /* Normalize (wrap) the mapview origin. */
984 normalize_gui_pos(tileset, zoom, gui_x0, gui_y0);
985
986 /* First wrap/clip the position. Wrapping is done in native positions
987 * while clipping is done in scroll (native) positions. */
988 get_mapview_scroll_window(&xmin, &ymin, &xmax, &ymax, &xsize, &ysize);
989
991 *gui_x0 = CLIP(xmin, *gui_x0, xmax - xsize);
992 }
993
995 *gui_y0 = CLIP(ymin, *gui_y0, ymax - ysize);
996 }
997
998 if (mapview.gui_x0 == *gui_x0 && mapview.gui_y0 == *gui_y0) {
999 return FALSE;
1000 }
1001
1002 return TRUE;
1003}
1004
1005/************************************************************************/
1008void set_mapview_origin(float gui_x0, float gui_y0, float zoom)
1009{
1010 if (!calc_mapview_origin(&gui_x0, &gui_y0, zoom)) {
1011 return;
1012 }
1013
1016 /* TODO: Implement animation */
1017 base_set_mapview_origin(gui_x0, gui_y0, zoom);
1018 } else {
1019 int start_x = mapview.gui_x0, start_y = mapview.gui_y0;
1020 float diff_x, diff_y;
1022 double currtime;
1023 int frames = 0;
1024
1025 /* We track the average FPS, which is used to predict how long the
1026 * next draw will take. We start with a 100 FPS estimate - this
1027 * value will quickly become irrelevant as the correct value is
1028 * calculated, but it's needed to give an estimate of the FPS for
1029 * the first draw.
1030 *
1031 * Note that the initial value shouldn't be larger than the sliding
1032 * time, or we'll jump straight to the last frame. The FPS should
1033 * therefore be a "high" estimate. */
1034 static double total_frames = 0.01;
1035 static double total_time = 0.0001;
1036
1038 &diff_x, &diff_y, start_x, start_y, gui_x0, gui_y0);
1040
1042
1043 do {
1044 double mytime;
1045
1046 /* Get the current time, and add on the average 1/FPS, which is the
1047 * expected time this frame will take. This is done so that the
1048 * frame's position is calculated from the expected time when the
1049 * frame will complete, rather than the time when the frame drawing
1050 * is started. */
1053
1057 zoom);
1058 flush_dirty();
1059 gui_flush();
1060 frames++;
1061 } while (currtime < timing_sec);
1062
1066 log_debug("Got %d frames in %f seconds: %f FPS (avg %f).",
1067 frames, currtime, (double)frames / currtime,
1069
1070 /* A very small decay factor to make things more accurate when something
1071 * changes (mapview size, tileset change, etc.). This gives a
1072 * half-life of 68 slides. */
1073 total_frames *= 0.99;
1074 total_time *= 0.99;
1075 }
1076 } else {
1077 base_set_mapview_origin(gui_x0, gui_y0, zoom);
1078 }
1079
1081}
1082
1083/************************************************************************/
1109 float *xmax, float *ymax,
1110 int *xsize, int *ysize)
1111{
1112 int diff;
1113
1114 *xsize = mapview.width;
1115 *ysize = mapview.height;
1116
1118 /* If the map and view line up, it's easy. */
1119 NATIVE_TO_MAP_POS(xmin, ymin, 0, 0);
1121
1126
1127 /* To be able to center on positions near the edges, we have to be
1128 * allowed to scroll all the way to those edges. To allow wrapping the
1129 * clipping boundary needs to extend past the edge - a half-tile in
1130 * iso-view or a full tile in non-iso view. The above math already has
1131 * taken care of some of this so all that's left is to fix the corner
1132 * cases. */
1134 *xmax += *xsize;
1135
1136 /* We need to be able to scroll a little further to the left. */
1138 }
1140 *ymax += *ysize;
1141
1142 /* We need to be able to scroll a little further up. */
1144 }
1145 } else {
1146 /* Otherwise it's hard. Very hard. Impossible, in fact. This is just
1147 * an approximation - a huge bounding box. */
1149 int map_x, map_y;
1150
1151 NATIVE_TO_MAP_POS(&map_x, &map_y, 0, 0);
1153
1156
1159
1162
1163 *xmin = MIN(gui_x1, MIN(gui_x2, gui_x3)) - mapview.width / 2;
1165
1166 *xmax = MAX(gui_x4, MAX(gui_x2, gui_x3)) + mapview.width / 2;
1168 }
1169
1170 /* Make sure the scroll window is big enough to hold the mapview. If
1171 * not scrolling will be very ugly and the GUI may become confused. */
1172 diff = *xsize - (*xmax - *xmin);
1173 if (diff > 0) {
1174 *xmin -= diff / 2;
1175 *xmax += (diff + 1) / 2;
1176 }
1177
1178 diff = *ysize - (*ymax - *ymin);
1179 if (diff > 0) {
1180 *ymin -= diff / 2;
1181 *ymax += (diff + 1) / 2;
1182 }
1183
1184 log_debug("x: %f<-%d->%f; y: %f<-%f->%d",
1185 *xmin, *xsize, *xmax, *ymin, *ymax, *ysize);
1186}
1187
1188/************************************************************************/
1193{
1196
1198 *xstep /= 2;
1199 *ystep /= 2;
1200 }
1201}
1202
1203/************************************************************************/
1211
1212/************************************************************************/
1216{
1217 int gui_x0 = scroll_x, gui_y0 = scroll_y;
1218
1219 can_slide = FALSE;
1220 set_mapview_origin(gui_x0, gui_y0, zoom);
1221 can_slide = TRUE;
1222}
1223
1224/************************************************************************/
1228{
1230 mapview.height / 2,
1231 map_zoom);
1232}
1233
1234/************************************************************************/
1237void center_tile_mapcanvas(const struct tile *ptile)
1238{
1239 float gui_x, gui_y;
1240 int tile_x, tile_y;
1241 static bool first = TRUE;
1242
1243 if (first && can_slide) {
1244 return;
1245 }
1246 first = FALSE;
1247
1250
1251 /* Put the center pixel of the tile at the exact center of the mapview. */
1254
1256
1257 center_tile = ptile;
1258}
1259
1260/************************************************************************/
1264bool tile_visible_mapcanvas(struct tile *ptile)
1265{
1266 float dummy_x, dummy_y; /* Well, it needs two pointers... */
1267
1268 return tile_to_canvas_pos(&dummy_x, &dummy_y, map_zoom, ptile);
1269}
1270
1271/************************************************************************/
1283{
1284 float canvas_x, canvas_y;
1285 float xmin, ymin, xmax, ymax;
1286 int xsize, ysize, scroll_x, scroll_y;
1292
1293 get_mapview_scroll_window(&xmin, &ymin, &xmax, &ymax, &xsize, &ysize);
1295
1296 if (!tile_to_canvas_pos(&canvas_x, &canvas_y, map_zoom, ptile)) {
1297 /* The tile isn't visible at all. */
1298 return FALSE;
1299 }
1300
1301 /* For each direction: if the tile is too close to the mapview border
1302 * in that direction, and scrolling can get us any closer to the
1303 * border, then it's a border tile. We can only really check the
1304 * scrolling when the mapview window lines up with the map. */
1305 if (canvas_x < border_x
1307 return FALSE;
1308 }
1309 if (canvas_y < border_y
1311 return FALSE;
1312 }
1314 && (!same || scroll_x + xsize < xmax || current_wrap_has_flag(WRAP_X))) {
1315 return FALSE;
1316 }
1318 && (!same || scroll_y + ysize < ymax || current_wrap_has_flag(WRAP_Y))) {
1319 return FALSE;
1320 }
1321
1322 return TRUE;
1323}
1324
1325/************************************************************************/
1328void put_drawn_sprites(struct canvas *pcanvas, float zoom,
1329 int canvas_x, int canvas_y,
1330 int count, struct drawn_sprite *pdrawn,
1331 bool fog)
1332{
1333 int i;
1334
1335 for (i = 0; i < count; i++) {
1336 if (!pdrawn[i].sprite) {
1337 /* This can happen, although it should probably be avoided. */
1338 continue;
1339 }
1340
1341 if (pdrawn[i].w > 0) {
1342 /* TODO: Fog support */
1344 canvas_x / zoom + pdrawn[i].offset_x,
1345 canvas_y / zoom + pdrawn[i].offset_y,
1346 pdrawn[i].w / zoom,
1347 pdrawn[i].h / zoom,
1348 pdrawn[i].sprite);
1349 } else if (fog && pdrawn[i].foggable) {
1351 canvas_x / zoom + pdrawn[i].offset_x,
1352 canvas_y / zoom + pdrawn[i].offset_y,
1353 pdrawn[i].sprite,
1354 TRUE,
1356 } else {
1357 /* We avoid calling canvas_put_sprite_fogged(), even though it
1358 * should be a valid thing to do, because gui-gtk-2.0 doesn't have
1359 * a full implementation. */
1361 canvas_x / zoom + pdrawn[i].offset_x,
1362 canvas_y / zoom + pdrawn[i].offset_y,
1363 pdrawn[i].sprite);
1364 }
1365 }
1366}
1367
1368/************************************************************************/
1372void put_one_element(struct canvas *pcanvas, float zoom,
1373 enum mapview_layer layer,
1374 const struct tile *ptile,
1375 const struct tile_edge *pedge,
1376 const struct tile_corner *pcorner,
1377 const struct unit *punit, const struct city *pcity,
1378 int canvas_x, int canvas_y,
1379 const struct city *citymode,
1380 const struct unit_type *putype)
1381{
1382 struct drawn_sprite tile_sprs[80];
1383 int count = fill_sprite_array(tileset, tile_sprs, layer,
1384 ptile, pedge, pcorner,
1385 punit, pcity, citymode, putype);
1386 bool fog = (ptile && gui_options.draw_fog_of_war
1388
1389 /*** Draw terrain and specials ***/
1390 put_drawn_sprites(pcanvas, zoom, canvas_x, canvas_y, count, tile_sprs, fog);
1391}
1392
1393/************************************************************************/
1397void put_unit(const struct unit *punit, struct canvas *pcanvas, float zoom,
1398 int canvas_x, int canvas_y)
1399{
1401 mapview_layer_iterate(layer) {
1402 put_one_element(pcanvas, zoom, layer, NULL, NULL, NULL,
1405}
1406
1407/************************************************************************/
1411void put_unittype(const struct unit_type *putype, struct canvas *pcanvas, float zoom,
1412 int canvas_x, int canvas_y)
1413{
1415 mapview_layer_iterate(layer) {
1416 put_one_element(pcanvas, zoom, layer, NULL, NULL, NULL,
1419}
1420
1421/************************************************************************/
1426void put_city(struct city *pcity, struct canvas *pcanvas, float zoom,
1427 int canvas_x, int canvas_y)
1428{
1430 mapview_layer_iterate(layer) {
1431 put_one_element(pcanvas, zoom, layer,
1432 NULL, NULL, NULL, NULL, pcity,
1435}
1436
1437/************************************************************************/
1443void put_terrain(struct tile *ptile, struct canvas *pcanvas, float zoom,
1444 int canvas_x, int canvas_y)
1445{
1446 /* Use full tile height, even for terrains. */
1448 mapview_layer_iterate(layer) {
1449 put_one_element(pcanvas, zoom, layer, ptile, NULL, NULL, NULL, NULL,
1452}
1453
1454/************************************************************************/
1462 struct canvas *pcanvas,
1463 int canvas_x, int canvas_y, int *upkeep_cost,
1464 int happy_cost)
1465{
1466 struct sprite *sprite;
1467
1469 if (sprite) {
1471 }
1472
1475 if (sprite) {
1477 }
1479}
1480
1481/*
1482 * pcity->client.color_index is an index into the city_colors array.
1483 * When toggle_city_color is called the city's coloration is toggled. When
1484 * a city is newly colored its color is taken from color_index and
1485 * color_index is moved forward one position. Each color in the array
1486 * tells what color the citymap will be drawn on the mapview.
1487 *
1488 * This array can be added to without breaking anything elsewhere.
1489 */
1490static int color_index = 0;
1491#define NUM_CITY_COLORS tileset_num_city_colors(tileset)
1492
1493
1494/************************************************************************/
1499void toggle_city_color(struct city *pcity)
1500{
1501 if (pcity->client.colored) {
1502 pcity->client.colored = FALSE;
1503 } else {
1504 pcity->client.colored = TRUE;
1507 }
1508
1509 refresh_city_mapcanvas(pcity, pcity->tile, TRUE, FALSE);
1510}
1511
1512/************************************************************************/
1518{
1519 if (punit->client.colored) {
1521 } else {
1525 }
1526
1528}
1529
1530/************************************************************************/
1534{
1535 float canvas_x, canvas_y;
1537 int width, height;
1538
1540
1542 struct animation *anim = fc_malloc(sizeof(struct animation));
1543
1544 anim->type = ANIM_NUKE;
1545 anim->id = -1;
1546 anim->nuke.shown = FALSE;
1547 anim->nuke.nuke_tile = ptile;
1548
1551
1552 anim->width = width * map_zoom;
1553 anim->height = height * map_zoom;
1555 } else {
1556 /* We can't count on the return value of tile_to_canvas_pos() since the
1557 * sprite may span multiple tiles. */
1559
1562
1563 /* Make sure everything is flushed and synced before proceeding. First
1564 * we update everything to the store, but don't write this to screen.
1565 * Then add the nuke graphic to the store. Finally flush everything to
1566 * the screen and wait 1 second. */
1568
1571
1572 flush_dirty();
1573 gui_flush();
1574
1575 fc_usleep(1000000);
1576
1578 }
1579}
1580
1581/************************************************************************/
1584static void put_one_tile(struct canvas *pcanvas, enum mapview_layer layer,
1585 struct tile *ptile, int canvas_x, int canvas_y,
1586 const struct city *citymode)
1587{
1589 || (editor_is_active() && editor_tile_is_selected(ptile))) {
1590 struct unit *punit = get_drawable_unit(tileset, ptile, citymode);
1591 struct animation *anim = NULL;
1592
1595 }
1596
1597 if (anim != NULL && punit != NULL
1598 && punit->id == anim->id) {
1599 punit = NULL;
1600 }
1601
1602 put_one_element(pcanvas, map_zoom, layer, ptile, NULL, NULL, punit,
1604 }
1605}
1606
1607/************************************************************************/
1618static int trade_route_to_canvas_lines(const struct tile *ptile1,
1619 const struct tile *ptile2,
1620 struct trade_route_line *lines)
1621{
1622 int dx, dy;
1623
1624 if (!ptile1 || !ptile2 || !lines) {
1625 return 0;
1626 }
1627
1630
1631 /* FIXME: Remove these casts. */
1632 tile_to_canvas_pos(&lines[0].x, &lines[0].y, map_zoom, (struct tile *)ptile1);
1633 tile_to_canvas_pos(&lines[1].x, &lines[1].y, map_zoom, (struct tile *)ptile2);
1634
1635 if (lines[1].x - lines[0].x == lines[0].width
1636 && lines[1].y - lines[0].y == lines[0].height) {
1637 return 1;
1638 }
1639
1640 lines[1].width = -lines[0].width;
1641 lines[1].height = -lines[0].height;
1642 return 2;
1643}
1644
1645/************************************************************************/
1648static void draw_trade_route_line(const struct tile *ptile1,
1649 const struct tile *ptile2,
1650 enum color_std color)
1651{
1653 int line_count, i;
1654 struct color *pcolor;
1655
1656 if (!ptile1 || !ptile2) {
1657 return;
1658 }
1659
1661 if (!pcolor) {
1662 return;
1663 }
1664
1665 /* Order the source and destination tiles consistently
1666 * so that if a line is drawn twice it does not produce
1667 * ugly effects due to dashes not lining up. */
1669 const struct tile *tmp;
1670 tmp = ptile1;
1671 ptile1 = ptile2;
1672 ptile2 = tmp;
1673 }
1674
1676 for (i = 0; i < line_count; i++) {
1677 /* XXX: canvas_put_line doesn't currently take map_zoom into account
1678 * itself, but it probably should? */
1682 lines[i].width, lines[i].height);
1683 }
1684}
1685
1686/************************************************************************/
1690{
1691 if (!pcity_src) {
1692 return;
1693 }
1694
1696 if (pcity_dest != NULL) {
1699 }
1701}
1702
1703/************************************************************************/
1706static void draw_trade_routes(void)
1707{
1709 return;
1710 }
1711
1713 cities_iterate(pcity) {
1716 } else {
1717 struct player *pplayer = client_player();
1718
1719 if (!pplayer) {
1720 return;
1721 }
1722
1723 city_list_iterate(pplayer->cities, pcity) {
1726 }
1727}
1728
1729/************************************************************************/
1745{
1746 int gui_x0, gui_y0;
1747 bool full;
1748 struct canvas *tmp;
1749
1750 if (canvas_x < 0) {
1751 width += canvas_x;
1752 canvas_x = 0;
1753 } else if (canvas_x > mapview.store_width) {
1756 }
1757
1758 if (canvas_y < 0) {
1759 height += canvas_y;
1760 canvas_y = 0;
1761 } else if (canvas_y > mapview.store_height) {
1764 }
1765
1766 if (width <= 0 || height <= 0) {
1767 /* Area outside mapview */
1768 return;
1769 }
1770
1771 gui_x0 = mapview.gui_x0 + canvas_x;
1772 gui_y0 = mapview.gui_y0 + canvas_y;
1773 full = (canvas_x == 0 && canvas_y == 0
1776
1777 log_debug("update_map_canvas(pos=(%d,%d), size=(%d,%d))",
1779
1780 /* If a full redraw is done, we just draw everything onto the canvas.
1781 * However if a partial redraw is done we draw everything onto the
1782 * tmp_canvas then copy *just* the area of update onto the canvas. */
1783 if (!full) {
1784 /* Swap store and tmp_store. */
1785 tmp = mapview.store;
1788 }
1789
1790 /* Clear the area. This is necessary since some parts of the rectangle
1791 * may not actually have any tiles drawn on them. This will happen when
1792 * the mapview is large enough so that the tile is visible in multiple
1793 * locations. In this case it will only be drawn in one place.
1794 *
1795 * Of course it's necessary to draw to the whole area to cover up any old
1796 * drawing that was done there. */
1800
1801 mapview_layer_iterate(layer) {
1802 if (layer == LAYER_TILELABEL) {
1804 }
1805 if (layer == LAYER_CITYBAR) {
1807 continue;
1808 }
1809 gui_rect_iterate_coord(gui_x0, gui_y0, width,
1811 ? (tileset_tile_height(tileset) / 2 * map_zoom) : 0),
1812 ptile, pedge, pcorner, gui_x, gui_y, map_zoom) {
1813 const int cx = gui_x - mapview.gui_x0, cy = gui_y - mapview.gui_y0;
1814
1815 if (ptile) {
1816 put_one_tile(mapview.store, layer, ptile, cx, cy, NULL);
1817 } else if (pedge) {
1819 NULL, NULL, cx, cy, NULL, NULL);
1820 } else if (pcorner) {
1822 NULL, NULL, cx, cy, NULL, NULL);
1823 } else {
1824 /* This can happen, for instance for unreal tiles. */
1825 }
1828
1831
1832 /* Draw the goto lines on top of the whole thing. This is done last as
1833 * we want it completely on top.
1834 *
1835 * Note that a pixel right on the border of a tile may actually contain a
1836 * goto line from an adjacent tile. Thus we draw any extra goto lines
1837 * from adjacent tiles (if they're close enough). */
1838 gui_rect_iterate(gui_x0 - GOTO_WIDTH, gui_y0 - GOTO_WIDTH,
1839 width + 2 * GOTO_WIDTH, height + 2 * GOTO_WIDTH,
1840 ptile, pedge, pcorner, map_zoom) {
1841 if (!ptile) {
1842 continue;
1843 }
1844 adjc_dir_base_iterate(&(wld.map), ptile, dir) {
1845 if (mapdeco_is_gotoline_set(ptile, dir)) {
1846 draw_segment(ptile, dir);
1847 }
1850
1851 if (!full) {
1852 /* Swap store and tmp_store back. */
1853 tmp = mapview.store;
1856
1857 /* And copy store to tmp_store. */
1860 }
1861
1863}
1864
1865/************************************************************************/
1872
1873/* The maximum city description width and height. This gives the dimensions
1874 * of a rectangle centered directly beneath the tile a city is on, that
1875 * contains the city description.
1876 *
1877 * These values are increased when drawing is done. This may mean that
1878 * the change (from increasing the value) won't take place until the
1879 * next redraw. */
1881
1882/* Same for tile labels */
1884
1885/************************************************************************/
1892
1893/************************************************************************/
1896void update_tile_label(struct tile *ptile)
1897{
1899}
1900
1901/************************************************************************/
1906 const int canvas_x0, const int canvas_y0,
1907 struct city *pcity, int *width, int *height)
1908{
1909 const struct citybar_sprites *citybar = get_citybar_sprites(tileset);
1910 static char name[512], growth[32], prod[512], size[32], trade_routes[32];
1913 /* trade_routes_color initialized just to get rid of gcc warning
1914 * on optimization level 3 when it misdiagnoses that it would be used
1915 * uninitialized otherwise. Funny thing here is that warning would
1916 * go away also by *not* setting it to values other than
1917 * COLOR_MAPVIEW_CITYTEXT in get_city_mapview_trade_routes() */
1919 struct color *owner_color;
1920 struct area_rect
1921 name_rect = {0, 0, 0, 0}, growth_rect = {0, 0, 0, 0},
1922 prod_rect = {0, 0, 0, 0}, size_rect = {0, 0, 0, 0},
1923 flag_rect = {0, 0, 0, 0}, occupy_rect = {0, 0, 0, 0},
1924 food_rect = {0, 0, 0, 0}, shield_rect = {0, 0, 0, 0},
1925 trade_routes_rect = {0,}, trade_rect = {0,};
1926 int width1 = 0, width2 = 0, height1 = 0, height2 = 0;
1927 struct sprite *bg = citybar->background;
1928 struct sprite *flag = get_city_flag_sprite(tileset, pcity);
1929 struct sprite *occupy = NULL;
1930 int bg_w, bg_h, x, y;
1933 const int border = 6;
1934 const enum client_font FONT_CITY_SIZE = FONT_CITY_NAME; /* TODO: new font */
1935
1936 /* We can see the city's production or growth values if
1937 * we are observing or playing as the owner of the city. */
1938 const bool can_see_inside
1940 const bool should_draw_productions
1945 const bool should_draw_lower_bar
1948
1949
1950 *width = 0;
1951 *height = 0;
1952
1954 return;
1955 }
1956
1957
1958 /* First: calculate rect dimensions (but not positioning). */
1959
1961 bg_w *= map_zoom;
1962 bg_h *= map_zoom;
1964 growth, sizeof(growth),
1966
1968 fc_snprintf(size, sizeof(size), "%d", city_size_get(pcity));
1969
1972
1974 int count = unit_list_size(pcity->tile->units);
1975
1976 count = CLIP(0, count, citybar->occupancy.size - 1);
1977 occupy = citybar->occupancy.p[count];
1978 } else {
1979 if (pcity->client.occupied) {
1980 occupy = citybar->occupied;
1981 } else {
1982 occupy = citybar->occupancy.p[0];
1983 }
1984 }
1985
1990
1992 + 2 * border + size_rect.w);
1993 height1 = MAX(flag_rect.h,
1994 MAX(occupy_rect.h,
1995 MAX(name_rect.h + border,
1996 size_rect.h + border)));
1997 }
1998
2000 width2 = 0;
2001 height2 = 0;
2002
2004 get_city_mapview_production(pcity, prod, sizeof(prod));
2006
2012 }
2013
2014 if (should_draw_growth) {
2021 }
2022
2025 sizeof(trade_routes),
2034 }
2035 }
2036
2037 *width = MAX(width1, width2);
2038 *height = height1 + height2;
2039
2040 /* Next fill in X and Y locations. */
2041
2043 flag_rect.x = canvas_x - *width / 2;
2044 flag_rect.y = canvas_y + (height1 - flag_rect.h) / 2;
2045
2047 occupy_rect.y = canvas_y + (height1 - occupy_rect.h) / 2;
2048
2050 - name_rect.w - size_rect.w - border) / 2;
2051 name_rect.y = canvas_y + (height1 - name_rect.h) / 2;
2052
2053 size_rect.x = canvas_x + (*width + 1) / 2 - size_rect.w - border / 2;
2054 size_rect.y = canvas_y + (height1 - size_rect.h) / 2;
2055 }
2056
2059 shield_rect.x = canvas_x - *width / 2;
2061
2062 prod_rect.x = shield_rect.x + shield_rect.w + border / 2;
2063 prod_rect.y = canvas_y + height1 + (height2 - prod_rect.h) / 2;
2064 }
2065
2067 trade_routes_rect.x = canvas_x + (*width + 1) / 2
2068 - trade_routes_rect.w - border / 2;
2070 + (height2 - trade_routes_rect.h) / 2;
2071
2073 trade_rect.y = canvas_y + height1 + (height2 - trade_rect.h) / 2;
2074 }
2075
2076 if (should_draw_growth) {
2077 growth_rect.x = canvas_x + (*width + 1) / 2
2078 - growth_rect.w - border / 2;
2079 if (trade_routes_rect.w > 0) {
2081 - trade_routes_rect.w - border / 2 - trade_rect.w - border / 2;
2082 }
2084
2085 food_rect.x = growth_rect.x - border / 2 - food_rect.w;
2086 food_rect.y = canvas_y + height1 + (height2 - food_rect.h) / 2;
2087 }
2088 }
2089
2090
2091 /* Now draw. */
2092
2093 /* Draw the city bar's background. */
2094 for (x = 0; x < *width; x += bg_w) {
2095 for (y = 0; y < *height; y += bg_h) {
2097 (canvas_y + y) / map_zoom,
2098 bg, 0, 0,
2099 (*width - x) / map_zoom, (*height - y) / map_zoom);
2100 }
2101 }
2102
2104
2109 flag);
2110 /* XXX: canvas_put_line() doesn't currently take map_zoom into account.
2111 * Should it?
2112 * In the meantime, don't compensate with '/ map_zoom' here, unlike
2113 * for canvas_put_sprite/text/rectangle */
2115 (flag_rect.x + flag_rect.w) /* / map_zoom */ - 1,
2116 canvas_y /* / map_zoom */,
2117 0, height1 /* / map_zoom */);
2120 occupy);
2125 (size_rect.x - border / 2) / map_zoom,
2127 (size_rect.w + border) / map_zoom,
2128 height1 / map_zoom);
2129 {
2130 /* Try to pick a color for city size text that contrasts with
2131 * player color */
2132 struct color *textcolors[2] = {
2135 };
2136
2141 }
2142 }
2143
2145
2149 citybar->shields);
2153 }
2154
2158 citybar->trade);
2163 }
2164
2165 if (should_draw_growth) {
2168 citybar->food);
2172 }
2173 }
2174
2175 /* Draw the city bar's outline. */
2176 /* XXX not scaling by map_zoom, see above */
2178 (canvas_x - *width / 2) /* / map_zoom */,
2179 canvas_y /* / map_zoom */,
2180 *width /* / map_zoom */, 0);
2182 (canvas_x - *width / 2) /* / map_zoom */,
2183 canvas_y /* / map_zoom */,
2184 0, *height /* / map_zoom */);
2186 (canvas_x - *width / 2) /* / map_zoom */,
2187 (canvas_y + *height) /* / map_zoom */ - 1,
2188 *width /* / map_zoom */, 0);
2190 (canvas_x - *width / 2 + *width) /* / map_zoom */,
2191 canvas_y /* / map_zoom */,
2192 0, *height /* / map_zoom */);
2193
2194 /* Draw the dividing line if we drew both the
2195 * upper and lower parts. */
2198 (canvas_x - *width / 2) /* / map_zoom */,
2199 (canvas_y + height1) /* / map_zoom */ - 1,
2200 *width /* / map_zoom */, 0);
2201 }
2202}
2203
2204/************************************************************************/
2210 int canvas_x, int canvas_y,
2211 struct city *pcity, int *width, int *height)
2212{
2213 static char name[512], growth[32], prod[512], trade_routes[32];
2216 /* trade_routes_color initialized just to get rid off gcc warning
2217 * on optimization level 3 when it misdiagnoses that it would be used
2218 * uninitialized otherwise. Funny thing here is that warning would
2219 * go away also by *not* setting it to values other than
2220 * COLOR_MAPVIEW_CITYTEXT in get_city_mapview_trade_routes() */
2222 struct {
2223 int x, y, w, h;
2224 } name_rect = {0, 0, 0, 0}, growth_rect = {0, 0, 0, 0},
2225 prod_rect = {0, 0, 0, 0}, trade_routes_rect = {0,};
2227 int spacer_width = 0;
2229 || city_owner(pcity) == client_player());
2230
2231 *width = *height = 0;
2232
2235
2237 growth, sizeof(growth), &growth_color,
2239
2241 int drawposx;
2242
2243 /* HACK: put a character's worth of space between the two
2244 * strings if needed. */
2246
2247 total_width = 0;
2248 total_height = 0;
2249
2253
2258 }
2259
2262 sizeof(trade_routes),
2268 }
2269
2271 drawposx -= total_width / 2;
2275 drawposx += name_rect.w;
2276
2283 drawposx += growth_rect.w;
2284 }
2285
2292 }
2293
2294 canvas_y += total_height + 3;
2295
2296 *width = MAX(*width, total_width);
2297 *height += total_height + 3;
2298 }
2299
2301 get_city_mapview_production(pcity, prod, sizeof(prod));
2303
2306
2311
2312 *width = MAX(*width, total_width);
2313 *height += total_height;
2314 }
2315}
2316
2317/************************************************************************/
2329static void show_city_desc(struct canvas *pcanvas,
2330 int canvas_x, int canvas_y,
2331 struct city *pcity, int *width, int *height)
2332{
2335 } else {
2337 }
2338}
2339
2340/************************************************************************/
2350static void show_tile_label(struct canvas *pcanvas,
2351 int canvas_x, int canvas_y,
2352 struct tile *ptile, int *width, int *height)
2353{
2354 const enum client_font FONT_TILE_LABEL = FONT_CITY_NAME; /* TODO: new font */
2355#define COLOR_MAPVIEW_TILELABEL COLOR_MAPVIEW_CITYTEXT
2356
2359
2361
2365#undef COLOR_MAPVIEW_TILELABEL
2366}
2367
2368/************************************************************************/
2372 int width_base, int height_base)
2373{
2375 const int dy = max_desc_height;
2378
2382 return;
2383 }
2384
2387 return;
2388 }
2389
2390 /* A city description is shown below the city. It has a specified
2391 * maximum width and height (although these are only estimates). Thus
2392 * we need to update some tiles above the mapview and some to the left
2393 * and right.
2394 *
2395 * /--W1--\ (W1 = tileset_tile_width(tileset))
2396 * -------- \
2397 * | CITY | H1 (H1 = tileset_tile_height(tileset))
2398 * | | /
2399 * ------------------ \
2400 * | DESCRIPTION | H2 (H2 = MAX_CITY_DESC_HEIGHT)
2401 * | | /
2402 * ------------------
2403 * \-------W2-------/ (W2 = MAX_CITY_DESC_WIDTH)
2404 *
2405 * We must draw H2 extra pixels above and (W2 - W1) / 2 extra pixels
2406 * to each side of the mapview.
2407 */
2410 width_base + dx, height_base + dy - offset_y,
2411 ptile, pedge, pcorner, gui_x, gui_y, map_zoom) {
2412 const int canvas_x = gui_x - mapview.gui_x0;
2413 const int canvas_y = gui_y - mapview.gui_y0;
2414
2415 if (ptile && tile_city(ptile)) {
2416 int width = 0, height = 0;
2417 struct city *pcity = tile_city(ptile);
2418
2420 pcity, &width, &height);
2421 log_debug("Drawing %s.", city_name_get(pcity));
2422
2424 /* The update was incomplete! We queue a new update. Note that
2425 * this is recursively queueing an update within a dequeuing of an
2426 * update. This is allowed specifically because of the code in
2427 * unqueue_mapview_updates. See that function for more. */
2428 log_debug("Re-queuing %s.", city_name_get(pcity));
2430 }
2433 }
2435
2436 /* We don't update the new max values until the end, so that the
2437 * check above to see what cities need redrawing will be complete. */
2440}
2441
2442/************************************************************************/
2446 int width_base, int height_base)
2447{
2449 const int dy = max_label_height;
2451
2454 width_base + dx, height_base + dy,
2455 ptile, pedge, pcorner, gui_x, gui_y, map_zoom) {
2456 const int canvas_x = gui_x - mapview.gui_x0;
2457 const int canvas_y = gui_y - mapview.gui_y0;
2458
2459 if (ptile && ptile->label != NULL) {
2460 int width = 0, height = 0;
2461
2463 ptile, &width, &height);
2464 log_debug("Drawing label %s.", ptile->label);
2465
2467 /* The update was incomplete! We queue a new update. Note that
2468 * this is recursively queueing an update within a dequeuing of an
2469 * update. This is allowed specifically because of the code in
2470 * unqueue_mapview_updates(). See that function for more. */
2471 log_debug("Re-queuing tile label %s drawing.", ptile->label);
2472 update_tile_label(ptile);
2473 }
2476 }
2478
2479 /* We don't update the new max values until the end, so that the
2480 * check above to see what cities need redrawing will be complete. */
2483}
2484
2485/************************************************************************/
2491{
2492 if (punit && unit_has_orders(punit)) {
2493 struct tile *ptile = unit_tile(punit);
2494 int i;
2495
2496 for (i = 0; i < punit->orders.length; i++) {
2497 int idx = (punit->orders.index + i) % punit->orders.length;
2498 struct unit_order *order;
2499
2500 if (punit->orders.index + i >= punit->orders.length
2501 && !punit->orders.repeat) {
2502 break;
2503 }
2504
2505 if (ptile == NULL) {
2506 /* This shouldn't happen unless the server gives us invalid
2507 * data. */
2508 log_warn("Unit orders with illegal tile.");
2509 break;
2510 }
2511
2512 order = &punit->orders.list[idx];
2513
2514 switch (order->order) {
2515 case ORDER_MOVE:
2516 draw_segment(ptile, order->dir);
2517 ptile = mapstep(&(wld.map), ptile, order->dir);
2518 break;
2519 default:
2520 /* TODO: Graphics for other orders. */
2521 break;
2522 }
2523 }
2524 return TRUE;
2525 } else {
2526 return FALSE;
2527 }
2528}
2529
2530/************************************************************************/
2534void draw_segment(struct tile *src_tile, enum direction8 dir)
2535{
2536 float canvas_x, canvas_y, canvas_dx, canvas_dy;
2537
2538 /* Determine the source position of the segment. */
2542
2543 /* Determine the vector of the segment. */
2544 map_to_gui_vector(tileset, map_zoom, &canvas_dx, &canvas_dy,
2545 DIR_DX[dir], DIR_DY[dir]);
2546
2547 /* Draw the segment. */
2548 /* XXX: canvas_put_line doesn't currently take map_zoom into account
2549 * itself, but it probably should? If so this will need adjusting */
2552 canvas_x, canvas_y, canvas_dx, canvas_dy);
2553
2554 /* The actual area drawn will extend beyond the base rectangle, since
2555 * the goto lines have width. */
2556 dirty_rect(MIN(canvas_x, canvas_x + canvas_dx) - GOTO_WIDTH,
2557 MIN(canvas_y, canvas_y + canvas_dy) - GOTO_WIDTH,
2558 ABS(canvas_dx) + 2 * GOTO_WIDTH,
2559 ABS(canvas_dy) + 2 * GOTO_WIDTH);
2560
2561 /* It is possible that the mapview wraps between the source and dest
2562 * tiles. In this case they will not be next to each other; they'll be
2563 * on the opposite sides of the screen. If this happens then the dest
2564 * tile will not be updated. This is consistent with the mapview design
2565 * which fails when the size of the mapview approaches that of the map. */
2566}
2567
2568/************************************************************************/
2573 struct unit *punit1, int hp1)
2574{
2575 struct unit *losing_unit = (hp0 == 0 ? punit0 : punit1);
2576 float canvas_x, canvas_y;
2577 int i;
2578
2580
2581 /* Make sure we don't start out with fewer HP than we're supposed to
2582 * end up with (which would cause the following loop to break). */
2583 punit0->hp = MAX(punit0->hp, hp0);
2584 punit1->hp = MAX(punit1->hp, hp1);
2585
2587
2589 struct animation *anim = fc_malloc(sizeof(struct animation));
2590 struct unit *winning_unit;
2591 int winner_end_hp;
2594
2595 if (losing_unit == punit1) {
2598 } else {
2601 }
2602
2603 anim->type = ANIM_BATTLE;
2604 anim->id = -1;
2605 anim->battle.virt_loser = unit_virtual_create(unit_owner(losing_unit),
2608 anim->battle.loser_tile = unit_tile(losing_unit);
2609 anim->battle.virt_loser->facing = losing_unit->facing;
2610 anim->battle.loser_hp_start = losing_unit->hp;
2611 anim->battle.virt_winner = unit_virtual_create(unit_owner(winning_unit),
2614 anim->battle.winner_tile = unit_tile(winning_unit);
2615 anim->battle.virt_winner->facing = winning_unit->facing;
2616 anim->battle.winner_hp_start = MAX(winning_unit->hp, winner_end_hp);
2617 anim->battle.winner_hp_end = winner_end_hp;
2618 anim->battle.steps = MAX(losing_unit->hp,
2619 anim->battle.winner_hp_start - winner_end_hp);
2620 anim->width = aw;
2621 anim->height = ah;
2623
2624 anim = fc_malloc(sizeof(struct animation));
2625 anim->type = ANIM_EXPL;
2626 anim->id = winning_unit->id;
2627 anim->expl.tile = losing_unit->tile;
2628 anim->expl.sprites = get_unit_explode_animation(tileset);
2629 anim->expl.sprite_count = sprite_vector_size(anim->expl.sprites);
2630 anim->width = aw;
2631 anim->height = ah;
2633 } else {
2636
2637 while (punit0->hp > hp0 || punit1->hp > hp1) {
2638 const int diff0 = punit0->hp - hp0, diff1 = punit1->hp - hp1;
2639
2641
2642 if (fc_rand(diff0 + diff1) < diff0) {
2643 punit0->hp--;
2645 } else {
2646 punit1->hp--;
2648 }
2649
2651 gui_flush();
2652
2655 }
2656
2666
2667 for (i = 0; i < num_tiles_explode_unit; i++) {
2668 int w, h;
2669 struct sprite *sprite = *sprite_vector_get(anim, i);
2670
2673
2674 /* We first draw the explosion onto the unit and draw draw the
2675 * complete thing onto the map canvas window. This avoids
2676 * flickering. */
2683 - w / 2,
2685 - h / 2,
2686 sprite);
2689
2690 flush_dirty();
2691 gui_flush();
2692
2695 }
2696 }
2697 }
2698
2702}
2703
2704/************************************************************************/
2709 struct tile *src_tile, int dx, int dy)
2710{
2711 struct tile *dest_tile;
2712 int dest_x, dest_y, src_x, src_y;
2713 int prev_x = -1;
2714 int prev_y = -1;
2715 int tuw;
2716 int tuh;
2717
2718 /* Only works for adjacent-square moves */
2719 if (dx < -1 || dx > 1 || dy < -1 || dy > 1 || (dx == 0 && dy == 0)) {
2720 return;
2721 }
2722
2723 index_to_map_pos(&src_x, &src_y, tile_index(src_tile));
2724 dest_x = src_x + dx;
2725 dest_y = src_y + dy;
2726 dest_tile = map_pos_to_tile(&(wld.map), dest_x, dest_y);
2727 if (!dest_tile) {
2728 return;
2729 }
2730
2731 if (tile_visible_mapcanvas(src_tile)
2732 || tile_visible_mapcanvas(dest_tile)) {
2733 float start_x, start_y;
2734 float canvas_dx, canvas_dy;
2736 double mytime;
2737
2739
2740 map_to_gui_vector(tileset, map_zoom, &canvas_dx, &canvas_dy, dx, dy);
2741
2742 tile_to_canvas_pos(&start_x, &start_y, map_zoom, src_tile);
2746 }
2747
2748 /* Bring the backing store up to date, but don't flush. */
2750
2753
2755 struct animation *anim = fc_malloc(sizeof(struct animation));
2756
2757 anim->type = ANIM_MOVEMENT;
2758 anim->id = punit->id;
2759 anim->movement.mover = unit_virtual_create(unit_owner(punit),
2761 punit->veteran);
2762 anim->movement.mover->hp = punit->hp;
2763 anim->movement.mover->facing = punit->facing;
2764 anim->movement.src = src_tile;
2765 anim->movement.dest = dest_tile;
2766 anim->movement.canvas_dx = canvas_dx;
2767 anim->movement.canvas_dy = canvas_dy;
2768 anim->width = tuw;
2769 anim->height = tuh;
2771 } else {
2772
2773 /* Start the timer (AFTER the unqueue above). */
2775
2776 do {
2777 int new_x, new_y;
2779
2781
2782 new_x = start_x + canvas_dx * (mytime / timing_sec);
2784
2785 if (new_x != prev_x || new_y != prev_y) {
2786 /* Backup the canvas store to the temp store. */
2789 tuw, tuh);
2790
2791 /* Draw */
2794
2795 /* Flush. */
2796 flush_dirty();
2797 gui_flush();
2798
2799 /* Restore the backup. It won't take effect until the next flush. */
2802 tuw, tuh);
2804
2805 prev_x = new_x;
2806 prev_y = new_y;
2807 } else {
2808 fc_usleep(500);
2809 }
2810 } while (mytime < timing_sec);
2811 }
2812 }
2813}
2814
2815/************************************************************************/
2827struct city *find_city_or_settler_near_tile(const struct tile *ptile,
2828 struct unit **punit)
2829{
2830 struct city *closest_city;
2831 struct city *pcity;
2834
2835 if (punit) {
2836 *punit = NULL;
2837 }
2838
2839 /* Check if there is visible city working that tile */
2840 pcity = tile_worked(ptile);
2841 if (pcity && pcity->tile) {
2842 if (NULL == client.conn.playing
2843 || city_owner(pcity) == client.conn.playing) {
2844 /* Rule a */
2845 return pcity;
2846 } else {
2847 /* Rule b */
2848 return NULL;
2849 }
2850 }
2851
2852 /* Rule e */
2854
2855 /* Check within maximum (squared) city radius */
2856 city_tile_iterate(&(wld.map), max_rad, ptile, tile1) {
2857 pcity = tile_city(tile1);
2858 if (pcity
2859 && (NULL == client.conn.playing
2860 || city_owner(pcity) == client.conn.playing)
2861 && client_city_can_work_tile(pcity, tile1)) {
2862 /*
2863 * Note, we must explicitly check if the tile is workable (with
2864 * city_can_work_tile() above), since it is possible that another
2865 * city (perhaps an UNSEEN city) may be working it!
2866 */
2867
2868 if (mapdeco_is_highlight_set(city_tile(pcity))) {
2869 /* Rule c */
2870 return pcity;
2871 }
2872 if (!closest_city) {
2873 closest_city = pcity;
2874 }
2875 }
2877
2878 /* Rule d */
2879 if (closest_city || !punit) {
2880 return closest_city;
2881 }
2882
2884 /* Check within maximum (squared) city radius */
2885 city_tile_iterate(&(wld.map), max_rad, ptile, tile1) {
2887 if ((NULL == client.conn.playing
2891 psettler, FALSE)) {
2892 if (closest_settler == NULL) {
2894 }
2895 if (best_settler == NULL && psettler->client.colored) {
2897 }
2898 }
2901
2902 if (best_settler != NULL) {
2903 /* Rule e */
2905 } else if (closest_settler != NULL) {
2906 /* Rule f */
2908 }
2909 }
2910
2911 /* Rule g */
2912 return NULL;
2913}
2914
2915/************************************************************************/
2918struct city *find_city_near_tile(const struct tile *ptile)
2919{
2920 return find_city_or_settler_near_tile(ptile, NULL);
2921}
2922
2923/************************************************************************/
2929static void append_city_buycost_string(const struct city *pcity,
2930 char *buffer, int buffer_len)
2931{
2932 if (!pcity || !buffer || buffer_len < 1) {
2933 return;
2934 }
2935
2936 if (!gui_options.draw_city_buycost || !city_can_buy(pcity)) {
2937 return;
2938 }
2939
2940 cat_snprintf(buffer, buffer_len, "/%d", pcity->client.buy_cost);
2941}
2942
2943/************************************************************************/
2948 char *buffer, size_t buffer_len)
2949{
2950 int turns;
2951
2953
2955 return;
2956 }
2957 turns = city_production_turns_to_build(pcity, TRUE);
2958
2959 if (999 < turns) {
2960 cat_snprintf(buffer, buffer_len, " -");
2961 } else {
2962 cat_snprintf(buffer, buffer_len, " %d", turns);
2963 }
2964
2965 append_city_buycost_string(pcity, buffer, buffer_len);
2966}
2967
2968/************************************************************************/
2974 char *trade_routes_buffer,
2976 enum color_std *pcolor)
2977{
2978 int num_trade_routes;
2979 int max_routes;
2980
2982 return;
2983 }
2984
2985 if (!pcity) {
2986 trade_routes_buffer[0] = '\0';
2987 if (pcolor) {
2989 }
2990 return;
2991 }
2992
2995
2997 "%d/%d", num_trade_routes, max_routes);
2998
2999 if (pcolor) {
3002 } else if (num_trade_routes == 0) {
3004 } else {
3006 }
3007 }
3008}
3009
3010/***************************************************************************/
3013
3014/* These values hold the tiles that need city, unit, or tile updates.
3015 * These different types of updates just tell what area need to be updated,
3016 * not necessarily what's sitting on the tile. A city update covers the
3017 * whole citymap area. A unit update covers just the "full" unit tile
3018 * area. A tile update covers the base tile plus half a tile in each
3019 * direction. */
3021
3022/************************************************************************/
3026static void queue_callback(void *data)
3027{
3030}
3031
3032/************************************************************************/
3036static void queue_add_callback(void)
3037{
3038 if (!callback_queued) {
3041 }
3042}
3043
3044/************************************************************************/
3062{
3063 if (can_client_change_view()) {
3064 needed_updates |= update;
3066 }
3067}
3068
3069/************************************************************************/
3078{
3079 if (can_client_change_view()) {
3080 if (!tile_updates[type]) {
3082 }
3085 }
3086}
3087
3088/************************************************************************/
3092{
3093 /* Calculate the area covered by each update type. The area array gives
3094 * the offset from the tile origin as well as the width and height of the
3095 * area to be updated. This is initialized each time when entering the
3096 * function from the existing tileset variables.
3097 *
3098 * A TILE update covers the base tile (W x H) plus a half-tile in each
3099 * direction (for edge/corner graphics), making its area 2W x 2H.
3100 *
3101 * A UNIT update covers a UW x UH area. This is centered horizontally
3102 * over the tile but extends up above the tile (e.g., units in iso-view).
3103 *
3104 * A CITYMAP update covers the whole citymap of a tile. This includes
3105 * the citymap area itself plus an extra half-tile in each direction (for
3106 * edge/corner graphics).
3107 */
3108 const float W = tileset_tile_width(tileset) * map_zoom;
3109 const float H = tileset_tile_height(tileset) * map_zoom;
3110 const float UW = tileset_unit_width(tileset) * map_zoom;
3111 const float UH = tileset_unit_height(tileset) * map_zoom;
3112 const float city_width = get_citydlg_canvas_width() * map_zoom + W;
3113 const float city_height = get_citydlg_canvas_height() * map_zoom + H;
3114 const struct {
3115 float dx, dy, w, h;
3116 } area[TILE_UPDATE_COUNT] = {
3117 {0, 0, W, H},
3118 {-W / 2, -H / 2, 2 * W, 2 * H},
3119 {(W - UW) / 2, H - UH, UW, UH},
3121 {-(city_width - W) / 2, -(city_height - H) / 2, city_width, city_height},
3123 };
3125
3126 int i;
3127
3128 if (!can_client_change_view()) {
3129 /* Double sanity check: make sure we don't unqueue an invalid update
3130 * after we've already detached. */
3131 return;
3132 }
3133
3134 log_debug("unqueue_mapview_update: needed_updates=%d",
3136
3137 /* This code "pops" the lists of tile updates off of the static array and
3138 * stores them locally. This allows further updates to be queued within
3139 * the function itself (namely, within update_map_canvas() ). */
3140 for (i = 0; i < TILE_UPDATE_COUNT; i++) {
3142 tile_updates[i] = NULL;
3143 }
3144
3145 if (!map_is_empty()) {
3149 dirty_all();
3152 /* Have to update the overview too, since some tiles may have changed. */
3154 } else {
3156 int max_x = 0, max_y = 0;
3157
3158 for (i = 0; i < TILE_UPDATE_COUNT; i++) {
3159 if (my_tile_updates[i]) {
3161 float xl, yt;
3162 int xr, yb;
3163
3164 (void) tile_to_canvas_pos(&xl, &yt, map_zoom, ptile);
3165
3166 xl += area[i].dx;
3167 yt += area[i].dy;
3168 xr = xl + area[i].w;
3169 yb = yt + area[i].h;
3170
3171 if (xr > 0 && xl < mapview.width
3172 && yb > 0 && yt < mapview.height) {
3173 min_x = MIN(min_x, xl);
3174 min_y = MIN(min_y, yt);
3175 max_x = MAX(max_x, xr);
3176 max_y = MAX(max_y, yb);
3177 }
3178
3179 /* FIXME: These overview updates should be batched as well.
3180 * Right now they account for as much as 90% of the runtime of
3181 * the unqueue. */
3182 overview_update_tile(ptile);
3184 }
3185 }
3186
3187 if (min_x < max_x && min_y < max_y) {
3189 }
3190 }
3191 }
3192
3193 for (i = 0; i < TILE_UPDATE_COUNT; i++) {
3194 if (my_tile_updates[i]) {
3196 }
3197 }
3199
3200 if (write_to_screen) {
3201 flush_dirty();
3203 }
3204}
3205
3206/************************************************************************/
3211 char *name_buffer,
3212 size_t name_buffer_len,
3213 char *growth_buffer,
3214 size_t growth_buffer_len,
3215 enum color_std *growth_color,
3217{
3219
3221 if (NULL == client.conn.playing
3222 || city_owner(pcity) == client.conn.playing) {
3223 int turns = city_turns_to_grow(pcity);
3224
3225 if (turns == 0) {
3227 } else if (turns == FC_INFINITY) {
3229 } else {
3230 /* Negative turns means we're shrinking, but that's handled
3231 down below. */
3233 }
3234
3235 if (turns <= 0) {
3236 /* A blocked or shrinking city has its growth status shown in red. */
3238 } else {
3240 }
3241
3242 if (pcity->surplus[O_SHIELD] < 0) {
3244 }
3245 } else {
3246 growth_buffer[0] = '\0';
3248 }
3249}
3250
3251/************************************************************************/
3255static bool can_do_cached_drawing(void)
3256{
3257 const int W = tileset_tile_width(tileset) * map_zoom;
3258 const int H = tileset_tile_height(tileset) * map_zoom;
3260
3261 /* If the mapview window is too large, cached drawing is not possible.
3262 *
3263 * BACKGROUND: cached drawing occurs when the mapview is scrolled just
3264 * a short distance. The majority of the mapview window can simply be
3265 * copied while the newly visible areas must be drawn from scratch. This
3266 * speeds up drawing significantly, especially when using the scrollbars
3267 * or mapview sliding.
3268 *
3269 * When the mapview is larger than the map, however, some tiles may become
3270 * visible twice. In this case one instance of the tile will be drawn
3271 * while all others are drawn black. When this happens the cached drawing
3272 * system breaks since it assumes the mapview canvas is an "ideal" window
3273 * over the map. So black tiles may be scrolled from the edges of the
3274 * mapview into the center, while drawn tiles may be scrolled from the
3275 * center of the mapview out to the edges. The result is very bad.
3276 *
3277 * There are a few different ways this could be solved. One way is simply
3278 * to turn off cached drawing, which is what we do now. If the mapview
3279 * window gets to be too large, the caching is disabled. Another would
3280 * be to prevent the window from getting too large in the first place -
3281 * but because the window boundaries aren't at an even tile this would
3282 * mean the entire map could never be shown. Yet another way would be
3283 * to draw tiles more than once if they are visible in multiple locations
3284 * on the mapview.
3285 *
3286 * The logic below is complicated and determined in part by
3287 * trial-and-error. */
3289 /* An unwrapping map: no limitation. On an unwrapping map no tile can
3290 * be visible twice so there's no problem. */
3291 return TRUE;
3292 }
3295 /* Non-matching. In this case the mapview does not line up with the
3296 * map's axis of wrapping. This will give very bad results for the
3297 * player!
3298 * We can never show more than half of the map.
3299 *
3300 * We divide by 4 below because we have to divide by 2 twice. The
3301 * first division by 2 is because the square must be half the size
3302 * of the (width+height). The second division by two is because for
3303 * an iso-map, NATURAL_XXX has a scale of 2, whereas for iso-view
3304 * NORMAL_TILE_XXX has a scale of 2. */
3305 return (w <= (NATURAL_WIDTH + NATURAL_HEIGHT) * W / 4
3306 && h <= (NATURAL_WIDTH + NATURAL_HEIGHT) * H / 4);
3307 } else {
3308 /* Matching. */
3309 const int isofactor = (tileset_is_isometric(tileset) ? 2 : 1);
3310 const int isodiff = (tileset_is_isometric(tileset) ? 6 : 2);
3311
3312 /* Now we can use the full width and height, with the exception of a small
3313 * area on each side. */
3315 && w > (NATURAL_WIDTH - isodiff) * W / isofactor) {
3316 return FALSE;
3317 }
3319 && h > (NATURAL_HEIGHT - isodiff) * H / isofactor) {
3320 return FALSE;
3321 }
3322 return TRUE;
3323 }
3324}
3325
3326/************************************************************************/
3331{
3332 /* HACK: this must be called on a map_info packet. */
3334
3335 mapdeco_free();
3339}
3340
3341/************************************************************************/
3359
3360/************************************************************************/
3364void mapdeco_set_highlight(const struct tile *ptile, bool highlight)
3365{
3366 bool changed = FALSE;
3367
3368 if (!ptile || !mapdeco_highlight_table) {
3369 return;
3370 }
3371
3372 if (highlight) {
3374 } else {
3375 changed = tile_hash_remove(mapdeco_highlight_table, ptile);
3376 }
3377
3378 if (changed) {
3379 /* FIXME: Remove the cast. */
3380 refresh_tile_mapcanvas((struct tile *) ptile, TRUE, FALSE);
3381 }
3382}
3383
3384/************************************************************************/
3387bool mapdeco_is_highlight_set(const struct tile *ptile)
3388{
3389 if (!ptile || !mapdeco_highlight_table) {
3390 return FALSE;
3391 }
3393}
3394
3395/************************************************************************/
3411
3412/************************************************************************/
3415void mapdeco_set_crosshair(const struct tile *ptile, bool crosshair)
3416{
3417 bool changed;
3418
3419 if (!mapdeco_crosshair_table || !ptile) {
3420 return;
3421 }
3422
3423 if (crosshair) {
3425 } else {
3426 changed = tile_hash_remove(mapdeco_crosshair_table, ptile);
3427 }
3428
3429 if (changed) {
3430 /* FIXME: Remove the cast. */
3431 refresh_tile_mapcanvas((struct tile *) ptile, FALSE, FALSE);
3432 }
3433}
3434
3435/************************************************************************/
3438bool mapdeco_is_crosshair_set(const struct tile *ptile)
3439{
3440 if (!mapdeco_crosshair_table || !ptile) {
3441 return FALSE;
3442 }
3444}
3445
3446/************************************************************************/
3462
3463/************************************************************************/
3468void mapdeco_add_gotoline(const struct tile *ptile, enum direction8 dir)
3469{
3470 struct gotoline_counter *pglc;
3471 const struct tile *ptile_dest;
3472 bool changed;
3473
3474 if (!mapdeco_gotoline_table || !ptile
3475 || !(dir <= direction8_max())) {
3476 return;
3477 }
3478 ptile_dest = mapstep(&(wld.map), ptile, dir);
3479 if (!ptile_dest) {
3480 return;
3481 }
3482
3486 }
3487 changed = (pglc->line_count[dir] < 1);
3488 pglc->line_count[dir]++;
3489
3490 if (changed) {
3491 /* FIXME: Remove cast. */
3492 refresh_tile_mapcanvas((struct tile *) ptile, FALSE, FALSE);
3494 }
3495}
3496
3497/************************************************************************/
3502void mapdeco_remove_gotoline(const struct tile *ptile,
3503 enum direction8 dir)
3504{
3505 struct gotoline_counter *pglc;
3506 bool changed = FALSE;
3507
3508 if (!mapdeco_gotoline_table || !ptile
3509 || !(dir <= direction8_max())) {
3510 return;
3511 }
3512
3514 return;
3515 }
3516
3517 pglc->line_count[dir]--;
3518 if (pglc->line_count[dir] <= 0) {
3519 pglc->line_count[dir] = 0;
3520 changed = TRUE;
3521 }
3522
3523 if (changed) {
3524 /* FIXME: Remove the casts. */
3525 refresh_tile_mapcanvas((struct tile *) ptile, FALSE, FALSE);
3526 ptile = mapstep(&(wld.map), ptile, dir);
3527 if (ptile != NULL) {
3528 refresh_tile_mapcanvas((struct tile *) ptile, FALSE, FALSE);
3529 }
3530 }
3531}
3532
3533/************************************************************************/
3539{
3540 const struct unit_order *porder;
3541 const struct tile *ptile;
3542 int i, ind;
3543
3545 || punit->orders.length < 1) {
3546 return;
3547 }
3548
3549 ptile = unit_tile(punit);
3550
3551 for (i = 0; ptile != NULL && i < punit->orders.length; i++) {
3552 if (punit->orders.index + i >= punit->orders.length
3553 && !punit->orders.repeat) {
3554 break;
3555 }
3556
3558 porder = &punit->orders.list[ind];
3559 if (porder->order != ORDER_MOVE) {
3560 /* FIXME: should display some indication of non-move orders here. */
3561 continue;
3562 }
3563
3564 mapdeco_add_gotoline(ptile, porder->dir);
3565 ptile = mapstep(&(wld.map), ptile, porder->dir);
3566 }
3567}
3568
3569/************************************************************************/
3573bool mapdeco_is_gotoline_set(const struct tile *ptile,
3574 enum direction8 dir)
3575{
3576 struct gotoline_counter *pglc;
3577
3578 if (!ptile || !(dir <= direction8_max())
3580 return FALSE;
3581 }
3582
3584 return FALSE;
3585 }
3586
3587 return pglc->line_count[dir] > 0;
3588}
3589
3590/************************************************************************/
3595{
3597 return;
3598 }
3599
3602 adjc_dir_iterate(&(wld.map), ptile, ptile_dest, dir) {
3603 if (pglc->line_count[dir] > 0) {
3605 }
3609}
3610
3611/************************************************************************/
3617{
3621 int tile_width = (width + tileset_tile_width(tileset) * map_zoom - 1) /
3623 int tile_height = (height + tileset_tile_height(tileset) * map_zoom - 1) /
3625 int full_width = tile_width * tileset_tile_width(tileset) * map_zoom;
3626 int full_height = tile_height * tileset_tile_height(tileset) * map_zoom;
3628
3629 /* Resized */
3630
3631 /* Since a resize is only triggered when the tile_*** changes, the canvas
3632 * width and height must include the entire backing store - otherwise
3633 * small resizings may lead to undrawn tiles. */
3634 mapview.tile_width = tile_width;
3635 mapview.tile_height = tile_height;
3640
3641 /* Check for what's changed. */
3642 tile_size_changed = (tile_width != old_tile_width
3643 || tile_height != old_tile_height);
3645
3646 /* If the tile size has changed, resize the canvas. */
3647 if (tile_size_changed) {
3648 if (mapview.store) {
3651 }
3658
3662 }
3663
3665 if (tile_size_changed) {
3666 if (center_tile != NULL) {
3667 int x_left, y_top;
3668 float gui_x, gui_y;
3669
3672
3673 /* Put the center pixel of the tile at the exact center of the mapview. */
3676
3680 }
3683
3684 /* Do not draw to the screen here as that could cause problems
3685 * when we are only initially setting up the view and some widgets
3686 * are not yet ready. */
3688 redrawn = TRUE;
3689 }
3690
3691 /* If the width/height has changed, update the scrollbars even if
3692 * the backing store is not resized. */
3693 if (size_changed) {
3696 }
3697 }
3698
3700
3701 return redrawn;
3702}
3703
3704/************************************************************************/
3708{
3709 /* Create a dummy map to make sure mapview.store is never NULL. */
3710 map_canvas_resized(1, 1);
3711}
3712
3713/************************************************************************/
3721
3722/************************************************************************/
3726{
3727 struct sprite *sprite
3729
3731 *width *= 7;
3732 *height *= 7;
3733}
3734
3735/************************************************************************/
3739 const struct player *pplayer)
3740{
3741 int i, x, y;
3742 const struct player_spaceship *ship = &pplayer->spaceship;
3743 int w, h;
3744 struct sprite *spr;
3745 struct tileset *t = tileset;
3746
3748 get_sprite_dimensions(spr, &w, &h);
3749
3752 0, 0, w * 7, h * 7);
3753
3754 for (i = 0; i < NUM_SS_MODULES; i++) {
3755 const int j = i / 3;
3756 const int k = i % 3;
3757
3758 if ((k == 0 && j >= ship->habitation)
3759 || (k == 1 && j >= ship->life_support)
3760 || (k == 2 && j >= ship->solar_panels)) {
3761 continue;
3762 }
3763 x = modules_info[i].x * w / 4 - w / 2;
3764 y = modules_info[i].y * h / 4 - h / 2;
3765
3770 }
3771
3772 for (i = 0; i < NUM_SS_COMPONENTS; i++) {
3773 const int j = i / 2;
3774 const int k = i % 2;
3775
3776 if ((k == 0 && j >= ship->fuel)
3777 || (k == 1 && j >= ship->propulsion)) {
3778 continue;
3779 }
3780 x = components_info[i].x * w / 4 - w / 2;
3781 y = components_info[i].y * h / 4 - h / 2;
3782
3783 spr = ((k == 0) ? get_spaceship_sprite(t, SPACESHIP_FUEL)
3785
3787
3788 if (k && ship->state == SSHIP_LAUNCHED) {
3791 }
3792 }
3793
3794 for (i = 0; i < NUM_SS_STRUCTURALS; i++) {
3795 if (!BV_ISSET(ship->structure, i)) {
3796 continue;
3797 }
3798 x = structurals_info[i].x * w / 4 - w / 2;
3799 y = structurals_info[i].y * h / 4 - h / 2;
3800
3803 }
3804}
3805
3806/****************************************************************************
3807 Map link mark module: it makes link marks when a link is sent by chating,
3808 or restore a mark with clicking a link on the chatline.
3809****************************************************************************/
3811 enum text_link_type type; /* The target type. */
3812 int id; /* The city or unit id, or tile index. */
3813 int turn_counter; /* The turn counter before it disappears. */
3814};
3815
3816#define SPECLIST_TAG link_mark
3817#define SPECLIST_TYPE struct link_mark
3818#include "speclist.h"
3819#define link_marks_iterate(pmark) \
3820 TYPED_LIST_ITERATE(struct link_mark, link_marks, pmark)
3821#define link_marks_iterate_end LIST_ITERATE_END
3822
3824
3825/************************************************************************/
3828static struct link_mark *link_mark_find(enum text_link_type type, int id)
3829{
3831 if (pmark->type == type && pmark->id == id) {
3832 return pmark;
3833 }
3835
3836 return NULL;
3837}
3838
3839/************************************************************************/
3843 int id, int turns)
3844{
3845 struct link_mark *pmark = fc_malloc(sizeof(struct link_mark));
3846
3847 pmark->type = type;
3848 pmark->id = id;
3849 pmark->turn_counter = turns;
3850
3851 return pmark;
3852}
3853
3854/************************************************************************/
3858{
3859 free(pmark);
3860}
3861
3862/************************************************************************/
3865static struct tile *link_mark_tile(const struct link_mark *pmark)
3866{
3867 switch (pmark->type) {
3868 case TLT_CITY:
3869 {
3870 struct city *pcity = game_city_by_number(pmark->id);
3871 return pcity ? pcity->tile : NULL;
3872 }
3873 case TLT_TILE:
3874 return index_to_tile(&(wld.map), pmark->id);
3875 case TLT_UNIT:
3876 {
3877 struct unit *punit = game_unit_by_number(pmark->id);
3878 return punit ? unit_tile(punit) : NULL;
3879 }
3880 }
3881
3882 return NULL;
3883}
3884
3885/************************************************************************/
3888static struct color *link_mark_color(const struct link_mark *pmark)
3889{
3890 switch (pmark->type) {
3891 case TLT_CITY:
3893 case TLT_TILE:
3895 case TLT_UNIT:
3897 }
3898
3899 return NULL;
3900}
3901
3902/************************************************************************/
3905static void link_mark_draw(const struct link_mark *pmark)
3906{
3909 int xd = width / 20, yd = height / 20;
3910 int xlen = width / 3, ylen = height / 3;
3911 float canvas_x, canvas_y;
3913 struct tile *ptile = link_mark_tile(pmark);
3914 struct color *pcolor = link_mark_color(pmark);
3915
3916 if (!ptile || !tile_to_canvas_pos(&canvas_x, &canvas_y, map_zoom, ptile)) {
3917 return;
3918 }
3919
3920 x_left = canvas_x + xd;
3921 x_right = canvas_x + width - xd;
3922 y_top = canvas_y + yd;
3923 y_bottom = canvas_y + height - yd;
3924
3925 /* XXX: canvas_put_line() doesn't currently take map_zoom into account
3926 * itself, but it probably should? If so these will need adjusting */
3928 xlen, 0);
3930 0, ylen);
3931
3933 -xlen, 0);
3935 0, ylen);
3936
3938 xlen, 0);
3940 0, -ylen);
3941
3943 -xlen, 0);
3945 0, -ylen);
3946}
3947
3948/************************************************************************/
3952{
3953 if (link_marks) {
3955 }
3956
3958}
3959
3960/************************************************************************/
3964{
3965 if (!link_marks) {
3966 return;
3967 }
3968
3970 link_marks = NULL;
3971}
3972
3973/************************************************************************/
3982
3983/************************************************************************/
3991
3992/************************************************************************/
3996{
3998 if (--pmark->turn_counter <= 0) {
4000 }
4002
4003 /* update_map_canvas_visible(); not needed here. */
4004}
4005
4006/************************************************************************/
4010{
4011 struct link_mark *pmark = link_mark_find(type, id);
4012 struct tile *ptile;
4013
4014 if (pmark) {
4015 /* Already displayed, but maybe increase the turn counter. */
4016 pmark->turn_counter = MAX(pmark->turn_counter, 2);
4017 return;
4018 }
4019
4020 pmark = link_mark_new(type, id, 2);
4022 ptile = link_mark_tile(pmark);
4023 if (ptile && tile_visible_mapcanvas(ptile)) {
4025 }
4026}
4027
4028/************************************************************************/
4032{
4033 struct link_mark *pmark;
4034 struct tile *ptile;
4035
4036 if (link_mark_find(type, id)) {
4037 return;
4038 }
4039
4040 pmark = link_mark_new(type, id, 1);
4042 ptile = link_mark_tile(pmark);
4043 if (ptile && tile_visible_mapcanvas(ptile)) {
4045 }
4046}
4047
4048/************************************************************************/
4052 struct tileset *tset,
4053 int *tset_topo)
4054{
4055 int tileset_topology;
4056
4057 if (tileset_hex_width(tset) > 0) {
4060 } else if (tileset_hex_height(tset) > 0) {
4063 } else if (tileset_is_isometric(tset)) {
4065 } else {
4066 tileset_topology = 0;
4067 }
4068
4069 if (tset_topo != NULL) {
4071 }
4072
4073 if (tileset_topology & TF_HEX) {
4074 if ((topology_id & (TF_HEX | TF_ISO)) == tileset_topology) {
4075 return TOPO_COMPATIBLE;
4076 }
4077
4078 /* Hex topology must match for both hexness and iso/non-iso */
4079 return TOPO_INCOMP_HARD;
4080 }
4081
4082 if (topology_id & TF_HEX) {
4083 return TOPO_INCOMP_HARD;
4084 }
4085
4086 if ((topology_id & TF_ISO) != (tileset_topology & TF_ISO)) {
4087 /* Non-hex iso/non-iso incompatibility is a soft one */
4088 return TOPO_INCOMP_SOFT;
4089 }
4090
4091 return TOPO_COMPATIBLE;
4092}
4093
4094/************************************************************************/
4097const char *describe_topology(int topo)
4098{
4099 if (topo & TF_ISO) {
4100 if (topo & TF_HEX) {
4101 return _("ISO|Hex");
4102 }
4103 return _("ISO");
4104 }
4105 if (topo & TF_HEX) {
4106 return _("Hex");
4107 }
4108
4109 return _("Overhead");
4110}
4111
4112/************************************************************************/
4119
4120/************************************************************************/
4124{
4125 return infratile;
4126}
4127
4128/************************************************************************/
4131void client_infratile_set(struct tile *ptile)
4132{
4133 struct tile *old_tile = infratile;
4134
4135 infratile = ptile;
4136
4137 if (old_tile != NULL) {
4139 }
4140 if (ptile != NULL) {
4142 }
4143}
#define BV_ISSET(bv, bit)
Definition bitvector.h:78
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_get(const struct city *pcity)
Definition city.c:1137
int city_production_turns_to_build(const struct city *pcity, bool include_shield_stock)
Definition city.c:820
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:1487
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:717
int city_turns_to_grow(const struct city *pcity)
Definition city.c:1996
#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:564
#define cities_iterate(pcity)
Definition city.h:512
static citizens city_size_get(const struct city *pcity)
Definition city.h:569
#define output_type_iterate(output)
Definition city.h:845
#define city_owner(_pcity_)
Definition city.h:563
#define city_list_iterate_end
Definition city.h:510
#define city_tile_iterate(_nmap, _radius_sq, _city_tile, _tile)
Definition city.h:230
#define city_tile_iterate_end
Definition city.h:238
#define output_type_iterate_end
Definition city.h:851
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:75
void set_units_in_combat(struct unit *pattacker, struct unit *pdefender)
Definition control.c:1032
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:74
int int id
Definition editgui_g.h:28
bool editor_is_active(void)
Definition editor.c:347
bool editor_tile_is_selected(const struct tile *ptile)
Definition editor.c:1118
#define DIR8_MAGIC_MAX
Definition fc_types.h:471
@ O_SHIELD
Definition fc_types.h:101
#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:116
struct city * game_city_by_number(int id)
Definition game.c:107
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:2404
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:191
#define log_warn(message,...)
Definition log.h:105
#define fc_assert(condition)
Definition log.h:176
#define log_debug(message,...)
Definition log.h:115
const int DIR_DY[8]
Definition map.c:87
#define nat_x
#define nat_y
const int DIR_DX[8]
Definition map.c:86
struct tile * index_to_tile(const struct civ_map *imap, int mindex)
Definition map.c:456
struct tile * map_pos_to_tile(const struct civ_map *nmap, int map_x, int map_y)
Definition map.c:419
void base_map_distance_vector(int *dx, int *dy, int x0dv, int y0dv, int x1dv, int y1dv)
Definition map.c:1024
struct tile * mapstep(const struct civ_map *nmap, const struct tile *ptile, enum direction8 dir)
Definition map.c:371
struct tile * nearest_real_tile(const struct civ_map *nmap, int x, int y)
Definition map.c:995
bool map_is_empty(void)
Definition map.c:149
bool normalize_map_pos(const struct civ_map *nmap, int *x, int *y)
Definition map.c:979
#define current_topo_has_flag(flag)
Definition map.h:48
#define adjc_dir_iterate(nmap, center_tile, itr_tile, dir_itr)
Definition map.h:438
#define NATURAL_HEIGHT
Definition map.h:227
#define MAP_IS_ISOMETRIC
Definition map.h:42
#define MAP_TO_NATIVE_POS(pnat_x, pnat_y, map_x, map_y)
Definition map.h:172
#define NATIVE_TO_MAP_POS(pmap_x, pmap_y, nat_x, nat_y)
Definition map.h:166
#define adjc_dir_iterate_end
Definition map.h:442
#define current_wrap_has_flag(flag)
Definition map.h:51
#define adjc_dir_base_iterate(nmap, center_tile, dir_itr)
Definition map.h:445
#define NATURAL_WIDTH
Definition map.h:226
#define adjc_dir_base_iterate_end
Definition map.h:449
#define index_to_map_pos(pmap_x, pmap_y, mindex)
Definition map.h:233
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)
bool show_unit_orders(struct unit *punit)
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:72
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:131
bool can_player_see_units_in_city(const struct player *pplayer, const struct city *pcity)
Definition player.c:1133
#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
const struct sprite_vector * sprites
struct animation::@201::@203 movement
struct tile * loser_tile
struct tile * dest
struct unit * virt_loser
struct tile * winner_tile
struct tile * nuke_tile
struct animation::@201::@204 battle
struct tile * tile
struct tile * src
struct unit * mover
struct animation::@201::@205 expl
enum animation_type type
struct unit * virt_winner
struct animation::@201::@206 nuke
float zoom
Definition canvas.h:25
Definition city.h:320
int color_index
Definition city.h:470
int surplus[O_LAST]
Definition city.h:355
struct trade_route_list * routes
Definition city.h:344
bool occupied
Definition city.h:460
struct universal production
Definition city.h:396
int buy_cost
Definition city.h:466
struct tile * tile
Definition city.h:322
bool colored
Definition city.h:469
struct city::@17::@20 client
struct sprite * food
Definition tilespec.h:333
struct sprite * trade
Definition tilespec.h:334
struct sprite * shields
Definition tilespec.h:331
struct sprite * background
Definition tilespec.h:336
struct sprite * occupied
Definition tilespec.h:335
struct sprite_vector occupancy
Definition tilespec.h:337
struct packet_scenario_info scenario
Definition game.h:87
int xsize
Definition map_types.h:78
int ysize
Definition map_types.h:78
struct connection conn
Definition client_main.h:96
bool draw_native
Definition options.h:215
int smooth_center_slide_msec
Definition options.h:152
bool draw_city_names
Definition options.h:196
bool draw_city_productions
Definition options.h:198
int smooth_move_unit_msec
Definition options.h:151
bool draw_borders
Definition options.h:214
bool draw_city_buycost
Definition options.h:199
bool draw_fog_of_war
Definition options.h:213
bool draw_city_trade_routes
Definition options.h:200
bool draw_map_grid
Definition options.h:195
bool draw_city_growth
Definition options.h:197
int smooth_combat_step_msec
Definition options.h:153
bool draw_full_citybar
Definition options.h:216
Definition colors.h:21
struct player * playing
Definition connection.h:151
int line_count[DIR8_MAGIC_MAX]
struct city_list * cities
Definition player.h:279
struct player_spaceship spaceship
Definition player.h:284
Definition tile.h:50
char * label
Definition tile.h:65
struct unit_list * units
Definition tile.h:58
Definition timing.c:81
enum unit_orders order
Definition unit.h:93
enum direction8 dir
Definition unit.h:102
struct veteran_system * veteran
Definition unittype.h:544
Definition unit.h:138
int length
Definition unit.h:195
struct unit::@80 orders
struct unit::@81::@83 client
int id
Definition unit.h:145
int index
Definition unit.h:195
int hp
Definition unit.h:151
bool colored
Definition unit.h:222
enum direction8 facing
Definition unit.h:142
struct unit_order * list
Definition unit.h:198
bool repeat
Definition unit.h:196
int color_index
Definition unit.h:223
int veteran
Definition unit.h:152
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:974
size_t fc_strlcpy(char *dest, const char *src, size_t n)
Definition support.c:791
void fc_usleep(unsigned long usec)
Definition support.c:641
int cat_snprintf(char *str, size_t n, const char *format,...)
Definition support.c:1000
#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:88
#define tile_hash_iterate(hash, ptile)
Definition tile.h:82
#define tile_worked(_tile)
Definition tile.h:118
@ TILE_KNOWN_UNSEEN
Definition tile.h:37
@ TILE_UNKNOWN
Definition tile.h:36
#define tile_list_iterate(tile_list, ptile)
Definition tile.h:73
#define tile_hash_iterate_end
Definition tile.h:84
#define TILE_XY(ptile)
Definition tile.h:43
#define tile_list_iterate_end
Definition tile.h:75
struct sprite * get_nuke_explode_sprite(const struct tileset *t)
Definition tilespec.c:6940
int tileset_hex_width(const struct tileset *t)
Definition tilespec.c:721
int tileset_unit_width(const struct tileset *t)
Definition tilespec.c:797
struct unit * get_drawable_unit(const struct tileset *t, struct tile *ptile, const struct city *citymode)
Definition tilespec.c:6575
int tileset_unit_height(const struct tileset *t)
Definition tilespec.c:805
int tileset_svg_flag_height(struct tileset *t)
Definition tilespec.c:7581
struct sprite * get_city_flag_sprite(const struct tileset *t, const struct city *pcity)
Definition tilespec.c:4470
int tileset_full_tile_height(const struct tileset *t)
Definition tilespec.c:789
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:7046
int tileset_citybar_offset_y(const struct tileset *t)
Definition tilespec.c:910
bool unit_drawn_with_city_outline(const struct unit *punit, bool check_focus)
Definition tilespec.c:5516
const struct sprite_vector * get_unit_explode_animation(const struct tileset *t)
Definition tilespec.c:6929
bool tileset_is_isometric(const struct tileset *t)
Definition tilespec.c:712
int tileset_tile_height(const struct tileset *t)
Definition tilespec.c:765
struct sprite * get_spaceship_sprite(const struct tileset *t, enum spaceship_part part)
Definition tilespec.c:6702
int tileset_hex_height(const struct tileset *t)
Definition tilespec.c:730
const struct citybar_sprites * get_citybar_sprites(const struct tileset *t)
Definition tilespec.c:6948
struct sprite * get_unit_unhappy_sprite(const struct tileset *t, const struct unit *punit, int happy_cost)
Definition tilespec.c:7027
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:5818
int tileset_tile_width(const struct tileset *t)
Definition tilespec.c:753
int tileset_tilelabel_offset_y(const struct tileset *t)
Definition tilespec.c:919
#define mapview_layer_iterate(layer)
Definition tilespec.h:176
#define mapview_layer_iterate_end
Definition tilespec.h:184
@ SPACESHIP_STRUCTURAL
Definition tilespec.h:323
@ SPACESHIP_HABITATION
Definition tilespec.h:322
@ SPACESHIP_EXHAUST
Definition tilespec.h:326
@ SPACESHIP_FUEL
Definition tilespec.h:324
@ SPACESHIP_PROPULSION
Definition tilespec.h:325
@ SPACESHIP_SOLAR_PANEL
Definition tilespec.h:320
@ SPACESHIP_LIFE_SUPPORT
Definition tilespec.h:321
void timer_usleep_since_start(struct timer *t, long usec)
Definition timing.c:405
void timer_start(struct timer *t)
Definition timing.c:264
double timer_read_seconds(struct timer *t)
Definition timing.c:384
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:367
struct unit * unit_virtual_create(struct player *pplayer, struct city *pcity, const struct unit_type *punittype, int veteran_level)
Definition unit.c:1632
void unit_virtual_destroy(struct unit *punit)
Definition unit.c:1737
bool unit_has_orders(const struct unit *punit)
Definition unit.c:210
#define unit_tile(_pu)
Definition unit.h:397
@ ORDER_MOVE
Definition unit.h:39
#define unit_owner(_pu)
Definition unit.h:396
#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:123
float mouse_zoom
Definition zoom.c:28
float map_zoom
Definition zoom.c:25
#define zoom_is_enabled()
Definition zoom.h:26