Freeciv-3.1
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 "tilespec.h"
47#include "zoom.h"
48
49#include "mapview_common.h"
50
51
52struct tile_hash *mapdeco_highlight_table;
53struct tile_hash *mapdeco_crosshair_table;
54
58
59static inline struct gotoline_counter *gotoline_counter_new(void);
60static void gotoline_counter_destroy(struct gotoline_counter *pglc);
61
62#define SPECHASH_TAG gotoline
63#define SPECHASH_IKEY_TYPE struct tile *
64#define SPECHASH_IDATA_TYPE struct gotoline_counter *
65#define SPECHASH_IDATA_FREE gotoline_counter_destroy
66#include "spechash.h"
67#define gotoline_hash_iterate(hash, ptile, pglc) \
68 TYPED_HASH_ITERATE(struct tile *, struct gotoline_counter *, \
69 hash, ptile, pglc)
70#define gotoline_hash_iterate_end HASH_ITERATE_END
71
72struct gotoline_hash *mapdeco_gotoline_table;
73
76
78
79const struct tile *center_tile = NULL;
80
81struct tile *infratile = NULL;
82
83static void base_canvas_to_map_pos(float zoom, int *map_x, int *map_y,
84 float canvas_x, float canvas_y);
85
86static void show_full_citybar(struct canvas *pcanvas,
87 const int canvas_x0, const int canvas_y0,
88 struct city *pcity, int *width, int *height)
89 fc__attribute((nonnull(5, 6)));
90static void show_city_desc(struct canvas *pcanvas,
91 int canvas_x, int canvas_y,
92 struct city *pcity, int *width, int *height)
93 fc__attribute((nonnull(5, 6)));
94
102
103/* A tile update has a tile associated with it as well as an area type.
104 * See unqueue_mapview_updates() for a thorough explanation. */
114static void queue_mapview_update(enum update_type update);
115static void queue_mapview_tile_update(struct tile *ptile,
117
118/* Helper struct for drawing trade routes. */
120 float x, y, width, height;
121};
122
123/* A trade route line might need to be drawn in two parts. */
124static const int MAX_TRADE_ROUTE_DRAW_LINES = 2;
125
126static struct timer *anim_timer = NULL;
127
129
131{
133 int id;
135 int old_x;
136 int old_y;
137 int width;
139 union {
140 struct {
141 struct unit *mover;
142 struct tile *src;
143 struct tile *dest;
147 struct {
155 int steps;
157 struct {
158 struct tile *tile;
159 const struct sprite_vector *sprites;
162 struct {
163 bool shown;
166 };
167};
168
169#define SPECLIST_TAG animation
170#define SPECLIST_TYPE struct animation
171#include "speclist.h"
172
173struct animation_list *animations = NULL;
174
175/************************************************************************/
179{
180 animations = animation_list_new();
181}
182
183/************************************************************************/
187{
188 if (animations != NULL) {
189 int i;
190 size_t last = animation_list_size(animations);
191
192 for (i = 0; i < last; i++) {
193 struct animation *anim = animation_list_get(animations, i);
194
195 switch (anim->type) {
196 case ANIM_MOVEMENT:
198 break;
199 case ANIM_BATTLE:
202 break;
203 case ANIM_EXPL:
204 case ANIM_NUKE:
205 /* Nothing to free */
206 break;
207 }
208
209 free(anim);
210 }
211
212 animation_list_destroy(animations);
213 animations = NULL;
214 }
215}
216
217/************************************************************************/
220static void animation_add(struct animation *anim)
221{
222 if (animation_list_size(animations) == 0) {
225 }
226
227 anim->finished = FALSE;
228 anim->old_x = -1; /* Initial frame */
229 animation_list_append(animations, anim);
230}
231
232/************************************************************************/
235static bool movement_animation(struct animation *anim, double time_gone)
236{
237 float start_x, start_y;
238 int new_x, new_y;
239 double timing_sec = (double)gui_options.smooth_move_unit_msec / 1000.0;
240 double mytime = MIN(time_gone, timing_sec);
241 struct unit *punit = anim->movement.mover;
242
243 if (punit != NULL) {
244 tile_to_canvas_pos(&start_x, &start_y, map_zoom, anim->movement.src);
246 start_y -= tileset_tile_height(tileset) / 2 * map_zoom;
248 }
249 new_x = start_x + anim->movement.canvas_dx * (mytime / timing_sec);
250 new_y = start_y + anim->movement.canvas_dy * (mytime / timing_sec);
251
252 if (anim->old_x >= 0) {
253 update_map_canvas(anim->old_x, anim->old_y,
254 anim->width, anim->height);
255 }
256 put_unit(punit, mapview.store, map_zoom, new_x, new_y);
257 dirty_rect(new_x, new_y, anim->width, anim->height);
258 anim->old_x = new_x;
259 anim->old_y = new_y;
260
261 if (time_gone >= timing_sec) {
262 /* Animation over */
264
265 return TRUE;
266 }
267 } else {
268 return TRUE;
269 }
270
271 return FALSE;
272}
273
274/************************************************************************/
277static bool battle_animation(struct animation *anim, double time_gone)
278{
279 double time_per_step;
280 int step;
281 float canvas_x, canvas_y;
282 double timing_sec = (double)gui_options.smooth_combat_step_msec
283 * anim->battle.steps / 1000.0;
284
285 if (time_gone >= timing_sec) {
286 /* Animation over */
287
290
291 return TRUE;
292 }
293
294 time_per_step = timing_sec / anim->battle.steps;
295 step = time_gone / time_per_step;
296
298 anim->battle.virt_loser->hp
300 * step / anim->battle.steps);
301
305 }
306
312 }
313
315 anim->battle.virt_winner->hp
317 - anim->battle.winner_hp_end)
318 * step / anim->battle.steps);
319
323 }
329 }
330
331 return FALSE;
332}
333
334/************************************************************************/
337static bool explosion_animation(struct animation *anim, double time_gone)
338{
339 float canvas_x, canvas_y;
340 double timing_sec;
341
342 if (anim->expl.sprite_count <= 0) {
343 return TRUE;
344 }
345
346 timing_sec = (double)gui_options.smooth_combat_step_msec
347 * anim->expl.sprite_count / 1000.0;
348
350 double time_per_frame = timing_sec / anim->expl.sprite_count;
351 int frame = time_gone / time_per_frame;
352 struct sprite *spr;
353 int w, h;
354
355 frame = MIN(frame, anim->expl.sprite_count - 1);
356
357 if (anim->old_x >= 0) {
358 update_map_canvas(anim->old_x, anim->old_y,
359 anim->width, anim->height);
360 }
361
362 spr = *sprite_vector_get(anim->expl.sprites, frame);
363 get_sprite_dimensions(spr, &w, &h);
364
367 - w / 2,
369 - h / 2,
370 spr);
373
374 anim->old_x = canvas_x;
375 anim->old_y = canvas_y;
376 }
377
378 if (time_gone >= timing_sec) {
379 /* Animation over */
380 return TRUE;
381 }
382
383 return FALSE;
384}
385
386/************************************************************************/
389static bool nuke_animation(struct animation *anim, double time_gone)
390{
391 if (!anim->nuke.shown) {
392 float canvas_x, canvas_y;
393 struct sprite *nuke_spr = get_nuke_explode_sprite(tileset);
394 int w, h;
395
397 get_sprite_dimensions(nuke_spr, &w, &h);
398
401 - w / 2,
403 - h / 2,
404 nuke_spr);
407
408 anim->old_x = canvas_x;
409 anim->old_y = canvas_y;
410
411 anim->nuke.shown = TRUE;
412
413 return FALSE;
414 }
415
416 if (time_gone > 1.0) {
418
419 return TRUE;
420 }
421
422 return FALSE;
423}
424
425/************************************************************************/
429{
430 if (animations != NULL && animation_list_size(animations) > 0) {
431 struct animation *anim = animation_list_get(animations, 0);
432
433 if (anim->finished) {
434 /* Animation over */
435
436 anim->id = -1;
437 if (anim->old_x >= 0) {
438 update_map_canvas(anim->old_x, anim->old_y, anim->width, anim->height);
439 }
440 animation_list_remove(animations, anim);
441 free(anim);
442
443 if (animation_list_size(animations) > 0) {
444 /* Start next */
447 }
448 } else {
449 double time_gone = timer_read_seconds(anim_timer);
450 bool finished = FALSE;
451
452 switch (anim->type) {
453 case ANIM_MOVEMENT:
454 finished = movement_animation(anim, time_gone);
455 break;
456 case ANIM_BATTLE:
457 finished = battle_animation(anim, time_gone);
458 break;
459 case ANIM_EXPL:
460 finished = explosion_animation(anim, time_gone);
461 break;
462 case ANIM_NUKE:
463 finished = nuke_animation(anim, time_gone);
464 }
465
466 if (finished) {
467 anim->finished = TRUE;
468 }
469 }
470 }
471}
472
473/************************************************************************/
476static inline struct gotoline_counter *gotoline_counter_new(void)
477{
478 struct gotoline_counter *pglc = fc_calloc(1, sizeof(*pglc));
479 return pglc;
480}
481
482/************************************************************************/
486{
487 fc_assert_ret(NULL != pglc);
488 free(pglc);
489}
490
491/************************************************************************/
494void refresh_tile_mapcanvas(struct tile *ptile,
495 bool full_refresh, bool write_to_screen)
496{
497 if (full_refresh) {
499 } else {
501 }
502 if (write_to_screen) {
504 }
505}
506
507/************************************************************************/
510void refresh_unit_mapcanvas(struct unit *punit, struct tile *ptile,
511 bool full_refresh, bool write_to_screen)
512{
513 if (full_refresh && gui_options.draw_native) {
515 } else if (full_refresh && unit_drawn_with_city_outline(punit, TRUE)) {
517 } else {
519 }
520 if (write_to_screen) {
522 }
523}
524
525/************************************************************************/
531void refresh_city_mapcanvas(struct city *pcity, struct tile *ptile,
532 bool full_refresh, bool write_to_screen)
533{
534 if (full_refresh && (gui_options.draw_map_grid || gui_options.draw_borders)) {
536 } else {
538 }
539 if (write_to_screen) {
541 }
542}
543
544/************************************************************************/
553void map_to_gui_vector(const struct tileset *t, float zoom,
554 float *gui_dx, float *gui_dy, int map_dx, int map_dy)
555{
556 if (tileset_is_isometric(t)) {
557 /*
558 * Convert the map coordinates to isometric GUI
559 * coordinates. We'll make tile map(0,0) be the origin, and
560 * transform like this:
561 *
562 * 3
563 * 123 2 6
564 * 456 -> becomes -> 1 5 9
565 * 789 4 8
566 * 7
567 */
568 *gui_dx = (map_dx - map_dy) * tileset_tile_width(t) / 2 * zoom;
569 *gui_dy = (map_dx + map_dy) * tileset_tile_height(t) / 2 * zoom;
570 } else {
571 *gui_dx = map_dx * tileset_tile_height(t) * zoom;
572 *gui_dy = map_dy * tileset_tile_width(t) * zoom;
573 }
574}
575
576/************************************************************************/
582static void map_to_gui_pos(const struct tileset *t, float zoom,
583 float *gui_x, float *gui_y, int map_x, int map_y)
584{
585 /* Since the GUI origin is the same as the map origin we can just do a
586 * vector conversion. */
587 map_to_gui_vector(t, zoom, gui_x, gui_y, map_x, map_y);
588}
589
590/************************************************************************/
597static void gui_to_map_pos(const struct tileset *t, float zoom,
598 int *map_x, int *map_y, float gui_x, float gui_y)
599{
600 const float W = tileset_tile_width(t) * zoom, H = tileset_tile_height(t) * zoom;
601 const float HH = tileset_hex_height(t) * zoom, HW = tileset_hex_width(t) * zoom;
602
603 if (HH > 0 || HW > 0) {
604 /* To handle hexagonal cases we have to revert to a less elegant method
605 * of calculation. */
606 float x, y;
607 int dx, dy;
608 int xmult, ymult, mod, compar;
609
611
612 x = DIVIDE((int)gui_x, (int)W);
613 y = DIVIDE((int)gui_y, (int)H);
614 dx = gui_x - x * W;
615 dy = gui_y - y * H;
616
617 /* Now fold so we consider only one-quarter tile. */
618 xmult = (dx >= W / 2) ? -1 : 1;
619 ymult = (dy >= H / 2) ? -1 : 1;
620 dx = (dx >= W / 2) ? (W - 1 - dx) : dx;
621 dy = (dy >= H / 2) ? (H - 1 - dy) : dy;
622
623 /* Next compare to see if we're across onto the next tile. */
624 if (HW > 0) {
625 compar = (dx - HW / 2) * (H / 2) - (H / 2 - 1 - dy) * (W / 2 - HW);
626 } else {
627 compar = (dy - HH / 2) * (W / 2) - (W / 2 - 1 - dx) * (H / 2 - HH);
628 }
629 mod = (compar < 0) ? -1 : 0;
630
631 *map_x = (x + y) + mod * (xmult + ymult) / 2;
632 *map_y = (y - x) + mod * (ymult - xmult) / 2;
633 } else if (tileset_is_isometric(t)) {
634 /* The basic operation here is a simple pi/4 rotation; however, we
635 * have to first scale because the tiles have different width and
636 * height. Mathematically, this looks like
637 * | 1/W 1/H | |x| |x`|
638 * | | | | -> | |
639 * |-1/W 1/H | |y| |y`|
640 *
641 * Where W is the tile width and H the height.
642 *
643 * In simple terms, this is
644 * map_x = [ x / W + y / H ]
645 * map_y = [ - x / W + y / H ]
646 * where [q] stands for integer part of q.
647 *
648 * Here the division is proper mathematical floating point division.
649 *
650 * We have to subtract off a half-tile in the X direction before doing
651 * the transformation. This is because, although the origin of the tile
652 * is the top-left corner of the bounding box, after the transformation
653 * the top corner of the diamond-shaped tile moves into this position.
654 *
655 * The calculation is complicated somewhat because of two things: we
656 * only use integer math, and C integer division rounds toward zero
657 * instead of rounding down.
658 *
659 * For another example of this math, see canvas_to_city_pos().
660 */
661 gui_x -= W / 2;
662 *map_x = DIVIDE((int)(gui_x * H + gui_y * W), (int)(W * H));
663 *map_y = DIVIDE((int)(gui_y * W - gui_x * H), (int)(W * H));
664 } else { /* tileset_is_isometric(t) */
665 /* We use DIVIDE so that we will get the correct result even
666 * for negative coordinates. */
667 *map_x = DIVIDE((int)gui_x, (int)W);
668 *map_y = DIVIDE((int)gui_y, (int)H);
669 }
670}
671
672/************************************************************************/
694bool tile_to_canvas_pos(float *canvas_x, float *canvas_y, float zoom,
695 const struct tile *ptile)
696{
697 int center_map_x, center_map_y, dx, dy, tile_x, tile_y;
698
699 /*
700 * First we wrap the coordinates to hopefully be within the mapview
701 * window. We do this by finding the position closest to the center
702 * of the window.
703 */
704 /* TODO: Cache the value of this position */
705 base_canvas_to_map_pos(zoom, &center_map_x, &center_map_y,
706 mapview.width / 2,
707 mapview.height / 2);
708 index_to_map_pos(&tile_x, &tile_y, tile_index(ptile));
709 base_map_distance_vector(&dx, &dy, center_map_x, center_map_y, tile_x,
710 tile_y);
711
713 canvas_x, canvas_y, center_map_x + dx, center_map_y + dy);
716
717 /*
718 * Finally we clip.
719 *
720 * This check is tailored to work for both iso-view and classic view.
721 * Note that (canvas_x, canvas_y) need not be aligned to a tile boundary, and
722 * that the position is at the top-left of the NORMAL (not UNIT) tile.
723 * This checks to see if _any part_ of the tile is present on the backing
724 * store. Even if it's not visible on the canvas, if it's present on the
725 * backing store we need to draw it in case the canvas is resized.
726 */
733}
734
735/************************************************************************/
739static void base_canvas_to_map_pos(float zoom, int *map_x, int *map_y,
740 float canvas_x, float canvas_y)
741{
742 gui_to_map_pos(tileset, zoom, map_x, map_y,
743 canvas_x + mapview.gui_x0 / map_zoom * zoom,
744 canvas_y + mapview.gui_y0 / map_zoom * zoom);
745}
746
747/************************************************************************/
752 float zoom)
753{
754 int map_x, map_y;
755
756 base_canvas_to_map_pos(zoom, &map_x, &map_y, canvas_x, canvas_y);
757 if (normalize_map_pos(&(wld.map), &map_x, &map_y)) {
758 return map_pos_to_tile(&(wld.map), map_x, map_y);
759 } else {
760 return NULL;
761 }
762}
763
764/************************************************************************/
769 float zoom)
770{
771 int map_x, map_y;
772
773 base_canvas_to_map_pos(zoom, &map_x, &map_y, canvas_x, canvas_y);
774 return nearest_real_tile(&(wld.map), map_x, map_y);
775}
776
777/************************************************************************/
781static void normalize_gui_pos(const struct tileset *t, float zoom,
782 float *gui_x, float *gui_y)
783{
784 int map_x, map_y, nat_x, nat_y, diff_x, diff_y;
785 float gui_x0, gui_y0;
786
787 /* Convert the (gui_x, gui_y) into a (map_x, map_y) plus a GUI offset
788 * from this tile. */
789 gui_to_map_pos(t, zoom, &map_x, &map_y, *gui_x, *gui_y);
790 map_to_gui_pos(t, zoom, &gui_x0, &gui_y0, map_x, map_y);
791 diff_x = *gui_x - gui_x0;
792 diff_y = *gui_y - gui_y0;
793
794 /* Perform wrapping without any realness check. It's important that
795 * we wrap even if the map position is unreal, which normalize_map_pos
796 * doesn't necessarily do. */
797 MAP_TO_NATIVE_POS(&nat_x, &nat_y, map_x, map_y);
798 if (current_topo_has_flag(TF_WRAPX)) {
800 }
801 if (current_topo_has_flag(TF_WRAPY)) {
803 }
804 NATIVE_TO_MAP_POS(&map_x, &map_y, nat_x, nat_y);
805
806 /* Now convert the wrapped map position back to a GUI position and add the
807 * offset back on. */
808 map_to_gui_pos(t, zoom, gui_x, gui_y, map_x, map_y);
809 *gui_x += diff_x;
810 *gui_y += diff_y;
811}
812
813/************************************************************************/
817static void gui_distance_vector(const struct tileset *t, float zoom,
818 float *gui_dx, float *gui_dy,
819 float gui_x0, float gui_y0,
820 float gui_x1, float gui_y1)
821{
822 int map_x0, map_y0, map_x1, map_y1;
823 float gui_x0_base, gui_y0_base, gui_x1_base, gui_y1_base;
824 int gui_x0_diff, gui_y0_diff, gui_x1_diff, gui_y1_diff;
825 int map_dx, map_dy;
826
827 /* Make sure positions are canonical. Yes, this is the only way. */
828 normalize_gui_pos(t, zoom, &gui_x0, &gui_y0);
829 normalize_gui_pos(t, zoom, &gui_x1, &gui_y1);
830
831 /* Now we have to find the offset of each GUI position from its tile
832 * origin. This is complicated: it means converting to a map position and
833 * then back to the GUI position to find the tile origin, then subtracting
834 * to get the offset. */
835 gui_to_map_pos(t, zoom, &map_x0, &map_y0, gui_x0, gui_y0);
836 gui_to_map_pos(t, zoom, &map_x1, &map_y1, gui_x1, gui_y1);
837
838 map_to_gui_pos(t, zoom, &gui_x0_base, &gui_y0_base, map_x0, map_y0);
839 map_to_gui_pos(t, zoom, &gui_x1_base, &gui_y1_base, map_x1, map_y1);
840
841 gui_x0_diff = gui_x0 - gui_x0_base;
842 gui_y0_diff = gui_y0 - gui_y0_base;
843 gui_x1_diff = gui_x1 - gui_x1_base;
844 gui_y1_diff = gui_y1 - gui_y1_base;
845
846 /* Next we find the map distance vector and convert this into a GUI
847 * vector. */
848 base_map_distance_vector(&map_dx, &map_dy, map_x0, map_y0, map_x1, map_y1);
849 map_to_gui_pos(t, zoom, gui_dx, gui_dy, map_dx, map_dy);
850
851 /* Finally we add on the difference in offsets to retain pixel
852 * resolution. */
853 *gui_dx += gui_x1_diff - gui_x0_diff;
854 *gui_dy += gui_y1_diff - gui_y0_diff;
855}
856
857/************************************************************************/
861static void base_set_mapview_origin(float gui_x0, float gui_y0, float zoom)
862{
863 float old_gui_x0, old_gui_y0;
864 float dx, dy;
865 const int width = mapview.width, height = mapview.height;
866 int common_x0, common_x1, common_y0, common_y1;
867 int update_x0, update_x1, update_y0, update_y1;
868
869 /* Then update everything. This does some tricky math to avoid having
870 * to do unnecessary redraws in update_map_canvas(). This makes for ugly
871 * code but speeds up the mapview by a large factor. */
872
873 /* We need to calculate the vector of movement of the mapview. So
874 * we find the GUI distance vector and then use this to calculate
875 * the original mapview origin relative to the current position. Thus
876 * if we move one tile to the left, even if this causes GUI positions
877 * to wrap the distance vector is only one tile. */
878 normalize_gui_pos(tileset, zoom, &gui_x0, &gui_y0);
879 gui_distance_vector(tileset, zoom, &dx, &dy,
881 gui_x0, gui_y0);
882 old_gui_x0 = gui_x0 - dx;
883 old_gui_y0 = gui_y0 - dy;
884
885 mapview.gui_x0 = gui_x0;
886 mapview.gui_y0 = gui_y0;
887
888 /* Find the overlapping area of the new and old mapview. This is
889 * done in GUI coordinates. Note that if the GUI coordinates wrap
890 * no overlap will be found. */
891 common_x0 = MAX(old_gui_x0, gui_x0);
892 common_x1 = MIN(old_gui_x0, gui_x0) + width;
893 common_y0 = MAX(old_gui_y0, gui_y0);
894 common_y1 = MIN(old_gui_y0, gui_y0) + height;
895
897 && common_x1 > common_x0 && common_y1 > common_y0) {
898 /* Do a partial redraw only. This means the area of overlap (a
899 * rectangle) is copied. Then the remaining areas (two rectangles)
900 * are updated through update_map_canvas. */
901 struct canvas *target = mapview.tmp_store;
902
903 if (old_gui_x0 < gui_x0) {
904 update_x0 = MAX(old_gui_x0 + width, gui_x0);
905 update_x1 = gui_x0 + width;
906 } else {
907 update_x0 = gui_x0;
908 update_x1 = MIN(old_gui_x0, gui_x0 + width);
909 }
910 if (old_gui_y0 < gui_y0) {
911 update_y0 = MAX(old_gui_y0 + height, gui_y0);
912 update_y1 = gui_y0 + height;
913 } else {
914 update_y0 = gui_y0;
915 update_y1 = MIN(old_gui_y0, gui_y0 + height);
916 }
917
918 dirty_all();
919 canvas_copy(target, mapview.store,
920 common_x0 - old_gui_x0,
921 common_y0 - old_gui_y0,
922 common_x0 - gui_x0, common_y0 - gui_y0,
923 common_x1 - common_x0, common_y1 - common_y0);
925 mapview.store = target;
926
927 if (update_y1 > update_y0) {
928 update_map_canvas(0, update_y0 - gui_y0,
929 width, update_y1 - update_y0);
930 }
931 if (update_x1 > update_x0) {
932 update_map_canvas(update_x0 - gui_x0, common_y0 - gui_y0,
933 update_x1 - update_x0, common_y1 - common_y0);
934 }
935 } else {
936 dirty_all();
938 }
939
941 switch (hover_state) {
942 case HOVER_GOTO:
943 case HOVER_PATROL:
944 case HOVER_CONNECT:
946 break;
948 case HOVER_NONE:
949 case HOVER_PARADROP:
951 break;
952 };
953 if (rectangle_active) {
955 }
956}
957
958/************************************************************************/
962static bool calc_mapview_origin(float *gui_x0, float *gui_y0, float zoom)
963{
964 float xmin, ymin, xmax, ymax;
965 int xsize, ysize;
966
967 /* Normalize (wrap) the mapview origin. */
968 normalize_gui_pos(tileset, zoom, gui_x0, gui_y0);
969
970 /* First wrap/clip the position. Wrapping is done in native positions
971 * while clipping is done in scroll (native) positions. */
972 get_mapview_scroll_window(&xmin, &ymin, &xmax, &ymax, &xsize, &ysize);
973
974 if (!current_topo_has_flag(TF_WRAPX)) {
975 *gui_x0 = CLIP(xmin, *gui_x0, xmax - xsize);
976 }
977
978 if (!current_topo_has_flag(TF_WRAPY)) {
979 *gui_y0 = CLIP(ymin, *gui_y0, ymax - ysize);
980 }
981
982 if (mapview.gui_x0 == *gui_x0 && mapview.gui_y0 == *gui_y0) {
983 return FALSE;
984 }
985
986 return TRUE;
987}
988
989/************************************************************************/
992void set_mapview_origin(float gui_x0, float gui_y0, float zoom)
993{
994 if (!calc_mapview_origin(&gui_x0, &gui_y0, zoom)) {
995 return;
996 }
997
1000 /* TODO: Implement animation */
1001 base_set_mapview_origin(gui_x0, gui_y0, zoom);
1002 } else {
1003 int start_x = mapview.gui_x0, start_y = mapview.gui_y0;
1004 float diff_x, diff_y;
1005 double timing_sec = (double)gui_options.smooth_center_slide_msec / 1000.0;
1006 double currtime;
1007 int frames = 0;
1008
1009 /* We track the average FPS, which is used to predict how long the
1010 * next draw will take. We start with a 100 FPS estimate - this
1011 * value will quickly become irrelevant as the correct value is
1012 * calculated, but it's needed to give an estimate of the FPS for
1013 * the first draw.
1014 *
1015 * Note that the initial value shouldn't be larger than the sliding
1016 * time, or we'll jump straight to the last frame. The FPS should
1017 * therefore be a "high" estimate. */
1018 static double total_frames = 0.01;
1019 static double total_time = 0.0001;
1020
1022 &diff_x, &diff_y, start_x, start_y, gui_x0, gui_y0);
1025
1027
1028 do {
1029 double mytime;
1030
1031 /* Get the current time, and add on the average 1/FPS, which is the
1032 * expected time this frame will take. This is done so that the
1033 * frame's position is calculated from the expected time when the
1034 * frame will complete, rather than the time when the frame drawing
1035 * is started. */
1036 currtime = timer_read_seconds(anim_timer);
1037 currtime += total_time / total_frames;
1038
1039 mytime = MIN(currtime, timing_sec);
1040 base_set_mapview_origin(start_x + diff_x * (mytime / timing_sec),
1041 start_y + diff_y * (mytime / timing_sec),
1042 zoom);
1043 flush_dirty();
1044 gui_flush();
1045 frames++;
1046 } while (currtime < timing_sec);
1047
1048 currtime = timer_read_seconds(anim_timer);
1049 total_frames += frames;
1050 total_time += currtime;
1051 log_debug("Got %d frames in %f seconds: %f FPS (avg %f).",
1052 frames, currtime, (double)frames / currtime,
1053 total_frames / total_time);
1054
1055 /* A very small decay factor to make things more accurate when something
1056 * changes (mapview size, tileset change, etc.). This gives a
1057 * half-life of 68 slides. */
1058 total_frames *= 0.99;
1059 total_time *= 0.99;
1060 }
1061 } else {
1062 base_set_mapview_origin(gui_x0, gui_y0, zoom);
1063 }
1064
1066}
1067
1068/************************************************************************/
1093void get_mapview_scroll_window(float *xmin, float *ymin,
1094 float *xmax, float *ymax,
1095 int *xsize, int *ysize)
1096{
1097 int diff;
1098
1099 *xsize = mapview.width;
1100 *ysize = mapview.height;
1101
1103 /* If the map and view line up, it's easy. */
1104 NATIVE_TO_MAP_POS(xmin, ymin, 0, 0);
1105 map_to_gui_pos(tileset, map_zoom, xmin, ymin, *xmin, *ymin);
1106
1107 NATIVE_TO_MAP_POS(xmax, ymax, wld.map.xsize - 1, wld.map.ysize - 1);
1108 map_to_gui_pos(tileset, map_zoom, xmax, ymax, *xmax, *ymax);
1111
1112 /* To be able to center on positions near the edges, we have to be
1113 * allowed to scroll all the way to those edges. To allow wrapping the
1114 * clipping boundary needs to extend past the edge - a half-tile in
1115 * iso-view or a full tile in non-iso view. The above math already has
1116 * taken care of some of this so all that's left is to fix the corner
1117 * cases. */
1118 if (current_topo_has_flag(TF_WRAPX)) {
1119 *xmax += *xsize;
1120
1121 /* We need to be able to scroll a little further to the left. */
1123 }
1124 if (current_topo_has_flag(TF_WRAPY)) {
1125 *ymax += *ysize;
1126
1127 /* We need to be able to scroll a little further up. */
1129 }
1130 } else {
1131 /* Otherwise it's hard. Very hard. Impossible, in fact. This is just
1132 * an approximation - a huge bounding box. */
1133 float gui_x1, gui_y1, gui_x2, gui_y2, gui_x3, gui_y3, gui_x4, gui_y4;
1134 int map_x, map_y;
1135
1136 NATIVE_TO_MAP_POS(&map_x, &map_y, 0, 0);
1137 map_to_gui_pos(tileset, map_zoom, &gui_x1, &gui_y1, map_x, map_y);
1138
1139 NATIVE_TO_MAP_POS(&map_x, &map_y, wld.map.xsize - 1, 0);
1140 map_to_gui_pos(tileset, map_zoom, &gui_x2, &gui_y2, map_x, map_y);
1141
1142 NATIVE_TO_MAP_POS(&map_x, &map_y, 0, wld.map.ysize - 1);
1143 map_to_gui_pos(tileset, map_zoom, &gui_x3, &gui_y3, map_x, map_y);
1144
1145 NATIVE_TO_MAP_POS(&map_x, &map_y, wld.map.xsize - 1, wld.map.ysize - 1);
1146 map_to_gui_pos(tileset, map_zoom, &gui_x4, &gui_y4, map_x, map_y);
1147
1148 *xmin = MIN(gui_x1, MIN(gui_x2, gui_x3)) - mapview.width / 2;
1149 *ymin = MIN(gui_y1, MIN(gui_y2, gui_y3)) - mapview.height / 2;
1150
1151 *xmax = MAX(gui_x4, MAX(gui_x2, gui_x3)) + mapview.width / 2;
1152 *ymax = MAX(gui_y4, MAX(gui_y2, gui_y3)) + mapview.height / 2;
1153 }
1154
1155 /* Make sure the scroll window is big enough to hold the mapview. If
1156 * not scrolling will be very ugly and the GUI may become confused. */
1157 diff = *xsize - (*xmax - *xmin);
1158 if (diff > 0) {
1159 *xmin -= diff / 2;
1160 *xmax += (diff + 1) / 2;
1161 }
1162
1163 diff = *ysize - (*ymax - *ymin);
1164 if (diff > 0) {
1165 *ymin -= diff / 2;
1166 *ymax += (diff + 1) / 2;
1167 }
1168
1169 log_debug("x: %f<-%d->%f; y: %f<-%f->%d",
1170 *xmin, *xsize, *xmax, *ymin, *ymax, *ysize);
1171}
1172
1173/************************************************************************/
1177void get_mapview_scroll_step(int *xstep, int *ystep)
1178{
1181
1183 *xstep /= 2;
1184 *ystep /= 2;
1185 }
1186}
1187
1188/************************************************************************/
1191void get_mapview_scroll_pos(int *scroll_x, int *scroll_y)
1192{
1193 *scroll_x = mapview.gui_x0;
1194 *scroll_y = mapview.gui_y0;
1195}
1196
1197/************************************************************************/
1200void set_mapview_scroll_pos(int scroll_x, int scroll_y, float zoom)
1201{
1202 int gui_x0 = scroll_x, gui_y0 = scroll_y;
1203
1204 can_slide = FALSE;
1205 set_mapview_origin(gui_x0, gui_y0, zoom);
1206 can_slide = TRUE;
1207}
1208
1209/************************************************************************/
1213{
1215 mapview.height / 2,
1216 map_zoom);
1217}
1218
1219/************************************************************************/
1222void center_tile_mapcanvas(const struct tile *ptile)
1223{
1224 float gui_x, gui_y;
1225 int tile_x, tile_y;
1226 static bool first = TRUE;
1227
1228 if (first && can_slide) {
1229 return;
1230 }
1231 first = FALSE;
1232
1233 index_to_map_pos(&tile_x, &tile_y, tile_index(ptile));
1234 map_to_gui_pos(tileset, map_zoom, &gui_x, &gui_y, tile_x, tile_y);
1235
1236 /* Put the center pixel of the tile at the exact center of the mapview. */
1237 gui_x -= (mapview.width - tileset_tile_width(tileset) * map_zoom) / 2;
1239
1240 set_mapview_origin(gui_x, gui_y, map_zoom);
1241
1242 center_tile = ptile;
1243}
1244
1245/************************************************************************/
1249bool tile_visible_mapcanvas(struct tile *ptile)
1250{
1251 float dummy_x, dummy_y; /* well, it needs two pointers... */
1252
1253 return tile_to_canvas_pos(&dummy_x, &dummy_y, map_zoom, ptile);
1254}
1255
1256/************************************************************************/
1268{
1269 float canvas_x, canvas_y;
1270 float xmin, ymin, xmax, ymax;
1271 int xsize, ysize, scroll_x, scroll_y;
1272 const int border_x = (tileset_is_isometric(tileset) ? tileset_tile_width(tileset) / 2 * map_zoom
1274 const int border_y = (tileset_is_isometric(tileset) ? tileset_tile_height(tileset) / 2 * map_zoom
1277
1278 get_mapview_scroll_window(&xmin, &ymin, &xmax, &ymax, &xsize, &ysize);
1279 get_mapview_scroll_pos(&scroll_x, &scroll_y);
1280
1281 if (!tile_to_canvas_pos(&canvas_x, &canvas_y, map_zoom, ptile)) {
1282 /* The tile isn't visible at all. */
1283 return FALSE;
1284 }
1285
1286 /* For each direction: if the tile is too close to the mapview border
1287 * in that direction, and scrolling can get us any closer to the
1288 * border, then it's a border tile. We can only really check the
1289 * scrolling when the mapview window lines up with the map. */
1290 if (canvas_x < border_x
1291 && (!same || scroll_x > xmin || current_topo_has_flag(TF_WRAPX))) {
1292 return FALSE;
1293 }
1294 if (canvas_y < border_y
1295 && (!same || scroll_y > ymin || current_topo_has_flag(TF_WRAPY))) {
1296 return FALSE;
1297 }
1299 && (!same || scroll_x + xsize < xmax || current_topo_has_flag(TF_WRAPX))) {
1300 return FALSE;
1301 }
1303 && (!same || scroll_y + ysize < ymax || current_topo_has_flag(TF_WRAPY))) {
1304 return FALSE;
1305 }
1306
1307 return TRUE;
1308}
1309
1310/************************************************************************/
1313void put_drawn_sprites(struct canvas *pcanvas, float zoom,
1314 int canvas_x, int canvas_y,
1315 int count, struct drawn_sprite *pdrawn,
1316 bool fog)
1317{
1318 int i;
1319
1320 for (i = 0; i < count; i++) {
1321 if (!pdrawn[i].sprite) {
1322 /* This can happen, although it should probably be avoided. */
1323 continue;
1324 }
1325
1326 if (fog && pdrawn[i].foggable) {
1328 canvas_x / zoom + pdrawn[i].offset_x,
1329 canvas_y / zoom + pdrawn[i].offset_y,
1330 pdrawn[i].sprite,
1331 TRUE,
1333 } else {
1334 /* We avoid calling canvas_put_sprite_fogged, even though it
1335 * should be a valid thing to do, because gui-gtk-2.0 doesn't have
1336 * a full implementation. */
1338 canvas_x / zoom + pdrawn[i].offset_x,
1339 canvas_y / zoom + pdrawn[i].offset_y,
1340 pdrawn[i].sprite);
1341 }
1342 }
1343}
1344
1345/************************************************************************/
1349void put_one_element(struct canvas *pcanvas, float zoom,
1350 enum mapview_layer layer,
1351 const struct tile *ptile,
1352 const struct tile_edge *pedge,
1353 const struct tile_corner *pcorner,
1354 const struct unit *punit, const struct city *pcity,
1355 int canvas_x, int canvas_y,
1356 const struct city *citymode,
1357 const struct unit_type *putype)
1358{
1359 struct drawn_sprite tile_sprs[80];
1360 int count = fill_sprite_array(tileset, tile_sprs, layer,
1361 ptile, pedge, pcorner,
1362 punit, pcity, citymode, putype);
1363 bool fog = (ptile && gui_options.draw_fog_of_war
1365
1366 /*** Draw terrain and specials ***/
1367 put_drawn_sprites(pcanvas, zoom, canvas_x, canvas_y, count, tile_sprs, fog);
1368}
1369
1370/************************************************************************/
1374void put_unit(const struct unit *punit, struct canvas *pcanvas, float zoom,
1375 int canvas_x, int canvas_y)
1376{
1378 mapview_layer_iterate(layer) {
1379 put_one_element(pcanvas, zoom, layer, NULL, NULL, NULL,
1380 punit, NULL, canvas_x, canvas_y, NULL, NULL);
1382}
1383
1384/************************************************************************/
1388void put_unittype(const struct unit_type *putype, struct canvas *pcanvas, float zoom,
1389 int canvas_x, int canvas_y)
1390{
1392 mapview_layer_iterate(layer) {
1393 put_one_element(pcanvas, zoom, layer, NULL, NULL, NULL,
1394 NULL, NULL, canvas_x, canvas_y, NULL, putype);
1396}
1397
1398/************************************************************************/
1403void put_city(struct city *pcity, struct canvas *pcanvas, float zoom,
1404 int canvas_x, int canvas_y)
1405{
1407 mapview_layer_iterate(layer) {
1408 put_one_element(pcanvas, zoom, layer,
1409 NULL, NULL, NULL, NULL, pcity,
1410 canvas_x, canvas_y, NULL, NULL);
1412}
1413
1414/************************************************************************/
1420void put_terrain(struct tile *ptile, struct canvas *pcanvas, float zoom,
1421 int canvas_x, int canvas_y)
1422{
1423 /* Use full tile height, even for terrains. */
1425 mapview_layer_iterate(layer) {
1426 put_one_element(pcanvas, zoom, layer, ptile, NULL, NULL, NULL, NULL,
1427 canvas_x, canvas_y, NULL, NULL);
1429}
1430
1431/************************************************************************/
1439 struct canvas *pcanvas,
1440 int canvas_x, int canvas_y, int *upkeep_cost,
1441 int happy_cost)
1442{
1443 struct sprite *sprite;
1444
1446 if (sprite) {
1448 }
1449
1451 sprite = get_unit_upkeep_sprite(tileset, o, punit, upkeep_cost);
1452 if (sprite) {
1454 }
1456}
1457
1458/*
1459 * pcity->client.color_index is an index into the city_colors array.
1460 * When toggle_city_color is called the city's coloration is toggled. When
1461 * a city is newly colored its color is taken from color_index and
1462 * color_index is moved forward one position. Each color in the array
1463 * tells what color the citymap will be drawn on the mapview.
1464 *
1465 * This array can be added to without breaking anything elsewhere.
1466 */
1467static int color_index = 0;
1468#define NUM_CITY_COLORS tileset_num_city_colors(tileset)
1469
1470
1471/************************************************************************/
1476void toggle_city_color(struct city *pcity)
1477{
1478 if (pcity->client.colored) {
1479 pcity->client.colored = FALSE;
1480 } else {
1481 pcity->client.colored = TRUE;
1484 }
1485
1486 refresh_city_mapcanvas(pcity, pcity->tile, TRUE, FALSE);
1487}
1488
1489/************************************************************************/
1495{
1496 if (punit->client.colored) {
1498 } else {
1502 }
1503
1505}
1506
1507/************************************************************************/
1511{
1512 float canvas_x, canvas_y;
1513 struct sprite *mysprite = get_nuke_explode_sprite(tileset);
1514 int width, height;
1515
1516 get_sprite_dimensions(mysprite, &width, &height);
1517
1519 struct animation *anim = fc_malloc(sizeof(struct animation));
1520
1521 anim->type = ANIM_NUKE;
1522 anim->id = -1;
1523 anim->nuke.shown = FALSE;
1524 anim->nuke.nuke_tile = ptile;
1525
1526 (void) tile_to_canvas_pos(&canvas_x, &canvas_y, map_zoom, ptile);
1527 get_sprite_dimensions(mysprite, &width, &height);
1528
1529 anim->width = width * map_zoom;
1530 anim->height = height * map_zoom;
1531 animation_add(anim);
1532 } else {
1533 /* We can't count on the return value of tile_to_canvas_pos since the
1534 * sprite may span multiple tiles. */
1535 (void) tile_to_canvas_pos(&canvas_x, &canvas_y, map_zoom, ptile);
1536
1539
1540 /* Make sure everything is flushed and synced before proceeding. First
1541 * we update everything to the store, but don't write this to screen.
1542 * Then add the nuke graphic to the store. Finally flush everything to
1543 * the screen and wait 1 second. */
1545
1548
1549 flush_dirty();
1550 gui_flush();
1551
1552 fc_usleep(1000000);
1553
1555 }
1556}
1557
1558/************************************************************************/
1561static void put_one_tile(struct canvas *pcanvas, enum mapview_layer layer,
1562 struct tile *ptile, int canvas_x, int canvas_y,
1563 const struct city *citymode)
1564{
1566 || (editor_is_active() && editor_tile_is_selected(ptile))) {
1567 struct unit *punit = get_drawable_unit(tileset, ptile, citymode);
1568 struct animation *anim = NULL;
1569
1570 if (animation_list_size(animations) > 0) {
1571 anim = animation_list_get(animations, 0);
1572 }
1573
1574 if (anim != NULL && punit != NULL
1575 && punit->id == anim->id) {
1576 punit = NULL;
1577 }
1578
1579 put_one_element(pcanvas, map_zoom, layer, ptile, NULL, NULL, punit,
1580 tile_city(ptile), canvas_x, canvas_y, citymode, NULL);
1581 }
1582}
1583
1584/************************************************************************/
1595static int trade_route_to_canvas_lines(const struct tile *ptile1,
1596 const struct tile *ptile2,
1597 struct trade_route_line *lines)
1598{
1599 int dx, dy;
1600
1601 if (!ptile1 || !ptile2 || !lines) {
1602 return 0;
1603 }
1604
1605 base_map_distance_vector(&dx, &dy, TILE_XY(ptile1), TILE_XY(ptile2));
1607
1608 /* FIXME: Remove these casts. */
1609 tile_to_canvas_pos(&lines[0].x, &lines[0].y, map_zoom, (struct tile *)ptile1);
1610 tile_to_canvas_pos(&lines[1].x, &lines[1].y, map_zoom, (struct tile *)ptile2);
1611
1612 if (lines[1].x - lines[0].x == lines[0].width
1613 && lines[1].y - lines[0].y == lines[0].height) {
1614 return 1;
1615 }
1616
1617 lines[1].width = -lines[0].width;
1618 lines[1].height = -lines[0].height;
1619 return 2;
1620}
1621
1622/************************************************************************/
1625static void draw_trade_route_line(const struct tile *ptile1,
1626 const struct tile *ptile2,
1627 enum color_std color)
1628{
1630 int line_count, i;
1631 struct color *pcolor;
1632
1633 if (!ptile1 || !ptile2) {
1634 return;
1635 }
1636
1638 if (!pcolor) {
1639 return;
1640 }
1641
1642 /* Order the source and destination tiles consistently
1643 * so that if a line is drawn twice it does not produce
1644 * ugly effects due to dashes not lining up. */
1645 if (tile_index(ptile2) > tile_index(ptile1)) {
1646 const struct tile *tmp;
1647 tmp = ptile1;
1648 ptile1 = ptile2;
1649 ptile2 = tmp;
1650 }
1651
1652 line_count = trade_route_to_canvas_lines(ptile1, ptile2, lines);
1653 for (i = 0; i < line_count; i++) {
1654 /* XXX: canvas_put_line doesn't currently take map_zoom into account
1655 * itself, but it probably should? */
1659 lines[i].width, lines[i].height);
1660 }
1661}
1662
1663/************************************************************************/
1666static void draw_trade_routes_for_city(const struct city *pcity_src)
1667{
1668 if (!pcity_src) {
1669 return;
1670 }
1671
1672 trade_partners_iterate(pcity_src, pcity_dest) {
1673 if (pcity_dest != NULL) {
1674 draw_trade_route_line(city_tile(pcity_src), city_tile(pcity_dest),
1675 COLOR_MAPVIEW_TRADE_ROUTE_LINE);
1676 }
1678}
1679
1680/************************************************************************/
1683static void draw_trade_routes(void)
1684{
1686 return;
1687 }
1688
1690 cities_iterate(pcity) {
1693 } else {
1694 struct player *pplayer = client_player();
1695 if (!pplayer) {
1696 return;
1697 }
1698 city_list_iterate(pplayer->cities, pcity) {
1701 }
1702}
1703
1704/************************************************************************/
1720{
1721 int gui_x0, gui_y0;
1722 bool full;
1723 struct canvas *tmp;
1724
1725 if (canvas_x < 0) {
1726 width += canvas_x;
1727 canvas_x = 0;
1728 } else if (canvas_x > mapview.store_width) {
1731 }
1732
1733 if (canvas_y < 0) {
1734 height += canvas_y;
1735 canvas_y = 0;
1736 } else if (canvas_y > mapview.store_height) {
1739 }
1740
1741 if (width <= 0 || height <= 0) {
1742 /* Area outside mapview */
1743 return;
1744 }
1745
1746 gui_x0 = mapview.gui_x0 + canvas_x;
1747 gui_y0 = mapview.gui_y0 + canvas_y;
1748 full = (canvas_x == 0 && canvas_y == 0
1751
1752 log_debug("update_map_canvas(pos=(%d,%d), size=(%d,%d))",
1754
1755 /* If a full redraw is done, we just draw everything onto the canvas.
1756 * However if a partial redraw is done we draw everything onto the
1757 * tmp_canvas then copy *just* the area of update onto the canvas. */
1758 if (!full) {
1759 /* Swap store and tmp_store. */
1760 tmp = mapview.store;
1762 mapview.tmp_store = tmp;
1763 }
1764
1765 /* Clear the area. This is necessary since some parts of the rectangle
1766 * may not actually have any tiles drawn on them. This will happen when
1767 * the mapview is large enough so that the tile is visible in multiple
1768 * locations. In this case it will only be drawn in one place.
1769 *
1770 * Of course it's necessary to draw to the whole area to cover up any old
1771 * drawing that was done there. */
1773 get_color(tileset, COLOR_MAPVIEW_UNKNOWN),
1775
1776 mapview_layer_iterate(layer) {
1777 if (layer == LAYER_TILELABEL) {
1779 }
1780 if (layer == LAYER_CITYBAR) {
1782 continue;
1783 }
1784 gui_rect_iterate_coord(gui_x0, gui_y0, width,
1786 ? (tileset_tile_height(tileset) / 2 * map_zoom) : 0),
1787 ptile, pedge, pcorner, gui_x, gui_y, map_zoom) {
1788 const int cx = gui_x - mapview.gui_x0, cy = gui_y - mapview.gui_y0;
1789
1790 if (ptile) {
1791 put_one_tile(mapview.store, layer, ptile, cx, cy, NULL);
1792 } else if (pedge) {
1793 put_one_element(mapview.store, map_zoom, layer, NULL, pedge, NULL,
1794 NULL, NULL, cx, cy, NULL, NULL);
1795 } else if (pcorner) {
1796 put_one_element(mapview.store, map_zoom, layer, NULL, NULL, pcorner,
1797 NULL, NULL, cx, cy, NULL, NULL);
1798 } else {
1799 /* This can happen, for instance for unreal tiles. */
1800 }
1803
1806
1807 /* Draw the goto lines on top of the whole thing. This is done last as
1808 * we want it completely on top.
1809 *
1810 * Note that a pixel right on the border of a tile may actually contain a
1811 * goto line from an adjacent tile. Thus we draw any extra goto lines
1812 * from adjacent tiles (if they're close enough). */
1813 gui_rect_iterate(gui_x0 - GOTO_WIDTH, gui_y0 - GOTO_WIDTH,
1814 width + 2 * GOTO_WIDTH, height + 2 * GOTO_WIDTH,
1815 ptile, pedge, pcorner, map_zoom) {
1816 if (!ptile) {
1817 continue;
1818 }
1819 adjc_dir_base_iterate(&(wld.map), ptile, dir) {
1820 if (mapdeco_is_gotoline_set(ptile, dir)) {
1821 draw_segment(ptile, dir);
1822 }
1825
1826 if (!full) {
1827 /* Swap store and tmp_store back. */
1828 tmp = mapview.store;
1830 mapview.tmp_store = tmp;
1831
1832 /* And copy store to tmp_store. */
1835 }
1836
1838}
1839
1840/************************************************************************/
1847
1848/* The maximum city description width and height. This gives the dimensions
1849 * of a rectangle centered directly beneath the tile a city is on, that
1850 * contains the city description.
1851 *
1852 * These values are increased when drawing is done. This may mean that
1853 * the change (from increasing the value) won't take place until the
1854 * next redraw. */
1856
1857/* Same for tile labels */
1859
1860/************************************************************************/
1867
1868/************************************************************************/
1871void update_tile_label(struct tile *ptile)
1872{
1874}
1875
1876/************************************************************************/
1881 const int canvas_x0, const int canvas_y0,
1882 struct city *pcity, int *width, int *height)
1883{
1884 const struct citybar_sprites *citybar = get_citybar_sprites(tileset);
1885 static char name[512], growth[32], prod[512], size[32], trade_routes[32];
1886 enum color_std growth_color;
1887 enum color_std production_color;
1888 /* trade_routes_color initialized just to get rid of gcc warning
1889 * on optimization level 3 when it misdiagnoses that it would be used
1890 * uninitialized otherwise. Funny thing here is that warning would
1891 * go away also by *not* setting it to values other than
1892 * COLOR_MAPVIEW_CITYTEXT in get_city_mapview_trade_routes() */
1893 enum color_std trade_routes_color = COLOR_MAPVIEW_CITYTEXT;
1894 struct color *owner_color;
1895 struct {
1896 int x, y, w, h;
1897 } name_rect = {0, 0, 0, 0}, growth_rect = {0, 0, 0, 0},
1898 prod_rect = {0, 0, 0, 0}, size_rect = {0, 0, 0, 0},
1899 flag_rect = {0, 0, 0, 0}, occupy_rect = {0, 0, 0, 0},
1900 food_rect = {0, 0, 0, 0}, shield_rect = {0, 0, 0, 0},
1901 trade_routes_rect = {0,}, trade_rect = {0,};
1902 int width1 = 0, width2 = 0, height1 = 0, height2 = 0;
1903 struct sprite *bg = citybar->background;
1904 struct sprite *flag = get_city_flag_sprite(tileset, pcity);
1905 struct sprite *occupy = NULL;
1906 int bg_w, bg_h, x, y;
1907 const int canvas_x = canvas_x0 + tileset_tile_width(tileset) / 2 * map_zoom;
1908 const int canvas_y = canvas_y0 + tileset_citybar_offset_y(tileset) * map_zoom;
1909 const int border = 6;
1910 const enum client_font FONT_CITY_SIZE = FONT_CITY_NAME; /* TODO: new font */
1911
1912 /* We can see the city's production or growth values if
1913 * we are observing or playing as the owner of the city. */
1914 const bool can_see_inside
1916 const bool should_draw_productions
1917 = can_see_inside && gui_options.draw_city_productions;
1918 const bool should_draw_growth = can_see_inside && gui_options.draw_city_growth;
1919 const bool should_draw_trade_routes = can_see_inside
1921 const bool should_draw_lower_bar
1922 = should_draw_productions || should_draw_growth
1923 || should_draw_trade_routes;
1924
1925
1926 *width = 0;
1927 *height = 0;
1928
1929 if (!gui_options.draw_city_names && !should_draw_lower_bar) {
1930 return;
1931 }
1932
1933
1934 /* First: calculate rect dimensions (but not positioning). */
1935
1936 get_sprite_dimensions(bg, &bg_w, &bg_h);
1937 bg_w *= map_zoom; bg_h *= map_zoom;
1939 growth, sizeof(growth),
1940 &growth_color, &production_color);
1941
1943 fc_snprintf(size, sizeof(size), "%d", city_size_get(pcity));
1944
1945 get_text_size(&size_rect.w, &size_rect.h, FONT_CITY_SIZE, size);
1946 get_text_size(&name_rect.w, &name_rect.h, FONT_CITY_NAME, name);
1947
1949 int count = unit_list_size(pcity->tile->units);
1950
1951 count = CLIP(0, count, citybar->occupancy.size - 1);
1952 occupy = citybar->occupancy.p[count];
1953 } else {
1954 if (pcity->client.occupied) {
1955 occupy = citybar->occupied;
1956 } else {
1957 occupy = citybar->occupancy.p[0];
1958 }
1959 }
1960
1961 get_sprite_dimensions(flag, &flag_rect.w, &flag_rect.h);
1962 flag_rect.w *= map_zoom; flag_rect.h *= map_zoom;
1963 get_sprite_dimensions(occupy, &occupy_rect.w, &occupy_rect.h);
1964 occupy_rect.w *= map_zoom; occupy_rect.h *= map_zoom;
1965
1966 width1 = (flag_rect.w + occupy_rect.w + name_rect.w
1967 + 2 * border + size_rect.w);
1968 height1 = MAX(flag_rect.h,
1969 MAX(occupy_rect.h,
1970 MAX(name_rect.h + border,
1971 size_rect.h + border)));
1972 }
1973
1974 if (should_draw_lower_bar) {
1975 width2 = 0;
1976 height2 = 0;
1977
1978 if (should_draw_productions) {
1979 get_city_mapview_production(pcity, prod, sizeof(prod));
1980 get_text_size(&prod_rect.w, &prod_rect.h, FONT_CITY_PROD, prod);
1981
1982 get_sprite_dimensions(citybar->shields, &shield_rect.w, &shield_rect.h);
1983 shield_rect.w *= map_zoom; shield_rect.h *= map_zoom;
1984 width2 += shield_rect.w + prod_rect.w + border;
1985 height2 = MAX(height2, shield_rect.h);
1986 height2 = MAX(height2, prod_rect.h + border);
1987 }
1988
1989 if (should_draw_growth) {
1990 get_text_size(&growth_rect.w, &growth_rect.h, FONT_CITY_PROD, growth);
1991 get_sprite_dimensions(citybar->food, &food_rect.w, &food_rect.h);
1992 food_rect.w *= map_zoom; food_rect.h *= map_zoom;
1993 width2 += food_rect.w + growth_rect.w + border;
1994 height2 = MAX(height2, food_rect.h);
1995 height2 = MAX(height2, growth_rect.h + border);
1996 }
1997
1998 if (should_draw_trade_routes) {
1999 get_city_mapview_trade_routes(pcity, trade_routes,
2000 sizeof(trade_routes),
2001 &trade_routes_color);
2002 get_text_size(&trade_routes_rect.w, &trade_routes_rect.h,
2003 FONT_CITY_PROD, trade_routes);
2004 get_sprite_dimensions(citybar->trade, &trade_rect.w, &trade_rect.h);
2005 trade_rect.w *= map_zoom; trade_rect.h *= map_zoom;
2006 width2 += trade_rect.w + trade_routes_rect.w + border;
2007 height2 = MAX(height2, trade_rect.h);
2008 height2 = MAX(height2, trade_routes_rect.h + border);
2009 }
2010 }
2011
2012 *width = MAX(width1, width2);
2013 *height = height1 + height2;
2014
2015 /* Next fill in X and Y locations. */
2016
2018 flag_rect.x = canvas_x - *width / 2;
2019 flag_rect.y = canvas_y + (height1 - flag_rect.h) / 2;
2020
2021 occupy_rect.x = flag_rect.x + flag_rect.w;
2022 occupy_rect.y = canvas_y + (height1 - occupy_rect.h) / 2;
2023
2024 name_rect.x = canvas_x + (flag_rect.w + occupy_rect.w
2025 - name_rect.w - size_rect.w - border) / 2;
2026 name_rect.y = canvas_y + (height1 - name_rect.h) / 2;
2027
2028 size_rect.x = canvas_x + (*width + 1) / 2 - size_rect.w - border / 2;
2029 size_rect.y = canvas_y + (height1 - size_rect.h) / 2;
2030 }
2031
2032 if (should_draw_lower_bar) {
2033 if (should_draw_productions) {
2034 shield_rect.x = canvas_x - *width / 2;
2035 shield_rect.y = canvas_y + height1 + (height2 - shield_rect.h) / 2;
2036
2037 prod_rect.x = shield_rect.x + shield_rect.w + border / 2;
2038 prod_rect.y = canvas_y + height1 + (height2 - prod_rect.h) / 2;
2039 }
2040
2041 if (should_draw_trade_routes) {
2042 trade_routes_rect.x = canvas_x + (*width + 1) / 2
2043 - trade_routes_rect.w - border / 2;
2044 trade_routes_rect.y = canvas_y + height1
2045 + (height2 - trade_routes_rect.h) / 2;
2046
2047 trade_rect.x = trade_routes_rect.x - border / 2 - trade_rect.w;
2048 trade_rect.y = canvas_y + height1 + (height2 - trade_rect.h) / 2;
2049 }
2050
2051 if (should_draw_growth) {
2052 growth_rect.x = canvas_x + (*width + 1) / 2
2053 - growth_rect.w - border / 2;
2054 if (trade_routes_rect.w > 0) {
2055 growth_rect.x = growth_rect.x
2056 - trade_routes_rect.w - border / 2 - trade_rect.w - border / 2;
2057 }
2058 growth_rect.y = canvas_y + height1 + (height2 - growth_rect.h) / 2;
2059
2060 food_rect.x = growth_rect.x - border / 2 - food_rect.w;
2061 food_rect.y = canvas_y + height1 + (height2 - food_rect.h) / 2;
2062 }
2063 }
2064
2065
2066 /* Now draw. */
2067
2068 /* Draw the city bar's background. */
2069 for (x = 0; x < *width; x += bg_w) {
2070 for (y = 0; y < *height; y += bg_h) {
2072 (canvas_y + y) / map_zoom,
2073 bg, 0, 0,
2074 (*width - x) / map_zoom, (*height - y) / map_zoom);
2075 }
2076 }
2077
2078 owner_color = get_player_color(tileset, city_owner(pcity));
2079
2082 flag_rect.x / map_zoom, flag_rect.y / map_zoom,
2083 flag);
2084 /* XXX: canvas_put_line() doesn't currently take map_zoom into account.
2085 * Should it?
2086 * In the meantime, don't compensate with '/ map_zoom' here, unlike
2087 * for canvas_put_sprite/text/rectangle */
2088 canvas_put_line(pcanvas, owner_color, LINE_NORMAL,
2089 (flag_rect.x + flag_rect.w) /* / map_zoom */ - 1,
2090 canvas_y /* / map_zoom */,
2091 0, height1 /* / map_zoom */);
2093 occupy_rect.x / map_zoom, occupy_rect.y / map_zoom,
2094 occupy);
2095 canvas_put_text(pcanvas, name_rect.x / map_zoom, name_rect.y / map_zoom,
2097 get_color(tileset, COLOR_MAPVIEW_CITYTEXT), name);
2098 canvas_put_rectangle(pcanvas, owner_color,
2099 (size_rect.x - border / 2) / map_zoom,
2101 (size_rect.w + border) / map_zoom,
2102 height1 / map_zoom);
2103 {
2104 /* Try to pick a color for city size text that contrasts with
2105 * player color */
2106 struct color *textcolors[2] = {
2107 get_color(tileset, COLOR_MAPVIEW_CITYTEXT),
2108 get_color(tileset, COLOR_MAPVIEW_CITYTEXT_DARK)
2109 };
2110
2111 canvas_put_text(pcanvas, size_rect.x / map_zoom, size_rect.y / map_zoom,
2113 color_best_contrast(owner_color, textcolors,
2114 ARRAY_SIZE(textcolors)), size);
2115 }
2116 }
2117
2118 if (should_draw_lower_bar) {
2119
2120 if (should_draw_productions) {
2122 shield_rect.x / map_zoom, shield_rect.y / map_zoom,
2123 citybar->shields);
2124 canvas_put_text(pcanvas, prod_rect.x / map_zoom, prod_rect.y / map_zoom,
2126 get_color(tileset, production_color), prod);
2127 }
2128
2129 if (should_draw_trade_routes) {
2131 trade_rect.x / map_zoom, trade_rect.y / map_zoom,
2132 citybar->trade);
2134 trade_routes_rect.x / map_zoom, trade_routes_rect.y / map_zoom,
2136 get_color(tileset, trade_routes_color), trade_routes);
2137 }
2138
2139 if (should_draw_growth) {
2141 food_rect.x / map_zoom, food_rect.y / map_zoom,
2142 citybar->food);
2143 canvas_put_text(pcanvas, growth_rect.x / map_zoom, growth_rect.y / map_zoom,
2145 get_color(tileset, growth_color), growth);
2146 }
2147 }
2148
2149 /* Draw the city bar's outline. */
2150 /* XXX not scaling by map_zoom, see above */
2151 canvas_put_line(pcanvas, owner_color, LINE_NORMAL,
2152 (canvas_x - *width / 2) /* / map_zoom */,
2153 canvas_y /* / map_zoom */,
2154 *width /* / map_zoom */, 0);
2155 canvas_put_line(pcanvas, owner_color, LINE_NORMAL,
2156 (canvas_x - *width / 2) /* / map_zoom */,
2157 canvas_y /* / map_zoom */,
2158 0, *height /* / map_zoom */);
2159 canvas_put_line(pcanvas, owner_color, LINE_NORMAL,
2160 (canvas_x - *width / 2) /* / map_zoom */,
2161 (canvas_y + *height) /* / map_zoom */ - 1,
2162 *width /* / map_zoom */, 0);
2163 canvas_put_line(pcanvas, owner_color, LINE_NORMAL,
2164 (canvas_x - *width / 2 + *width) /* / map_zoom */,
2165 canvas_y /* / map_zoom */,
2166 0, *height /* / map_zoom */);
2167
2168 /* Draw the dividing line if we drew both the
2169 * upper and lower parts. */
2170 if (gui_options.draw_city_names && should_draw_lower_bar) {
2171 canvas_put_line(pcanvas, owner_color, LINE_NORMAL,
2172 (canvas_x - *width / 2) /* / map_zoom */,
2173 (canvas_y + height1) /* / map_zoom */ - 1,
2174 *width /* / map_zoom */, 0);
2175 }
2176}
2177
2178/************************************************************************/
2184 int canvas_x, int canvas_y,
2185 struct city *pcity, int *width, int *height)
2186{
2187 static char name[512], growth[32], prod[512], trade_routes[32];
2188 enum color_std growth_color;
2189 enum color_std production_color;
2190 /* trade_routes_color initialized just to get rid off gcc warning
2191 * on optimization level 3 when it misdiagnoses that it would be used
2192 * uninitialized otherwise. Funny thing here is that warning would
2193 * go away also by *not* setting it to values other than
2194 * COLOR_MAPVIEW_CITYTEXT in get_city_mapview_trade_routes() */
2195 enum color_std trade_routes_color = COLOR_MAPVIEW_CITYTEXT;
2196 struct {
2197 int x, y, w, h;
2198 } name_rect = {0, 0, 0, 0}, growth_rect = {0, 0, 0, 0},
2199 prod_rect = {0, 0, 0, 0}, trade_routes_rect = {0,};
2200 int total_width, total_height;
2201 int spacer_width = 0;
2202 const bool can_see_inside = (client_is_global_observer()
2203 || city_owner(pcity) == client_player());
2204
2205 *width = *height = 0;
2206
2209
2211 growth, sizeof(growth), &growth_color,
2212 &production_color);
2213
2215 int drawposx;
2216
2217 /* HACK: put a character's worth of space between the two
2218 * strings if needed. */
2219 get_text_size(&spacer_width, NULL, FONT_CITY_NAME, "M");
2220
2221 total_width = 0;
2222 total_height = 0;
2223
2224 get_text_size(&name_rect.w, &name_rect.h, FONT_CITY_NAME, name);
2225 total_width += name_rect.w;
2226 total_height = MAX(total_height, name_rect.h);
2227
2228 if (gui_options.draw_city_growth && can_see_inside) {
2229 get_text_size(&growth_rect.w, &growth_rect.h, FONT_CITY_PROD, growth);
2230 total_width += spacer_width + growth_rect.w;
2231 total_height = MAX(total_height, growth_rect.h);
2232 }
2233
2234 if (gui_options.draw_city_trade_routes && can_see_inside) {
2235 get_city_mapview_trade_routes(pcity, trade_routes,
2236 sizeof(trade_routes),
2237 &trade_routes_color);
2238 get_text_size(&trade_routes_rect.w, &trade_routes_rect.h,
2239 FONT_CITY_PROD, trade_routes);
2240 total_width += spacer_width + trade_routes_rect.w;
2241 total_height = MAX(total_height, trade_routes_rect.h);
2242 }
2243
2244 drawposx = canvas_x;
2245 drawposx -= total_width / 2;
2248 get_color(tileset, COLOR_MAPVIEW_CITYTEXT), name);
2249 drawposx += name_rect.w;
2250
2251 if (gui_options.draw_city_growth && can_see_inside) {
2252 drawposx += spacer_width;
2253 canvas_put_text(pcanvas, drawposx / map_zoom,
2254 (canvas_y + total_height - growth_rect.h) / map_zoom,
2256 get_color(tileset, growth_color), growth);
2257 drawposx += growth_rect.w;
2258 }
2259
2260 if (gui_options.draw_city_trade_routes && can_see_inside) {
2261 drawposx += spacer_width;
2262 canvas_put_text(pcanvas, drawposx / map_zoom,
2263 (canvas_y + total_height - trade_routes_rect.h) / map_zoom,
2265 get_color(tileset, trade_routes_color), trade_routes);
2266 }
2267
2268 canvas_y += total_height + 3;
2269
2270 *width = MAX(*width, total_width);
2271 *height += total_height + 3;
2272 }
2273
2274 if (gui_options.draw_city_productions && can_see_inside) {
2275 get_city_mapview_production(pcity, prod, sizeof(prod));
2276 get_text_size(&prod_rect.w, &prod_rect.h, FONT_CITY_PROD, prod);
2277
2278 total_width = prod_rect.w;
2279 total_height = prod_rect.h;
2280
2281 canvas_put_text(pcanvas, (canvas_x - total_width / 2) / map_zoom,
2284 get_color(tileset, production_color), prod);
2285
2286 *width = MAX(*width, total_width);
2287 *height += total_height;
2288 }
2289}
2290
2291/************************************************************************/
2303static void show_city_desc(struct canvas *pcanvas,
2304 int canvas_x, int canvas_y,
2305 struct city *pcity, int *width, int *height)
2306{
2309 } else {
2311 }
2312}
2313
2314/************************************************************************/
2324static void show_tile_label(struct canvas *pcanvas,
2325 int canvas_x, int canvas_y,
2326 struct tile *ptile, int *width, int *height)
2327{
2328 const enum client_font FONT_TILE_LABEL = FONT_CITY_NAME; /* TODO: new font */
2329#define COLOR_MAPVIEW_TILELABEL COLOR_MAPVIEW_CITYTEXT
2330
2333
2334 get_text_size(width, height, FONT_TILE_LABEL, ptile->label);
2335
2337 FONT_TILE_LABEL,
2339#undef COLOR_MAPVIEW_TILELABEL
2340}
2341
2342/************************************************************************/
2345void show_city_descriptions(int canvas_base_x, int canvas_base_y,
2346 int width_base, int height_base)
2347{
2349 const int dy = max_desc_height;
2351 int new_max_width = max_desc_width, new_max_height = max_desc_height;
2352
2356 return;
2357 }
2358
2361 return;
2362 }
2363
2364 /* A city description is shown below the city. It has a specified
2365 * maximum width and height (although these are only estimates). Thus
2366 * we need to update some tiles above the mapview and some to the left
2367 * and right.
2368 *
2369 * /--W1--\ (W1 = tileset_tile_width(tileset))
2370 * -------- \
2371 * | CITY | H1 (H1 = tileset_tile_height(tileset))
2372 * | | /
2373 * ------------------ \
2374 * | DESCRIPTION | H2 (H2 = MAX_CITY_DESC_HEIGHT)
2375 * | | /
2376 * ------------------
2377 * \-------W2-------/ (W2 = MAX_CITY_DESC_WIDTH)
2378 *
2379 * We must draw H2 extra pixels above and (W2 - W1) / 2 extra pixels
2380 * to each side of the mapview.
2381 */
2382 gui_rect_iterate_coord(mapview.gui_x0 + canvas_base_x - dx / 2,
2383 mapview.gui_y0 + canvas_base_y - dy,
2384 width_base + dx, height_base + dy - offset_y,
2385 ptile, pedge, pcorner, gui_x, gui_y, map_zoom) {
2386 const int canvas_x = gui_x - mapview.gui_x0;
2387 const int canvas_y = gui_y - mapview.gui_y0;
2388
2389 if (ptile && tile_city(ptile)) {
2390 int width = 0, height = 0;
2391 struct city *pcity = tile_city(ptile);
2392
2394 pcity, &width, &height);
2395 log_debug("Drawing %s.", city_name_get(pcity));
2396
2398 /* The update was incomplete! We queue a new update. Note that
2399 * this is recursively queueing an update within a dequeuing of an
2400 * update. This is allowed specifically because of the code in
2401 * unqueue_mapview_updates. See that function for more. */
2402 log_debug("Re-queuing %s.", city_name_get(pcity));
2404 }
2405 new_max_width = MAX(width, new_max_width);
2406 new_max_height = MAX(height, new_max_height);
2407 }
2409
2410 /* We don't update the new max values until the end, so that the
2411 * check above to see what cities need redrawing will be complete. */
2412 max_desc_width = MAX(max_desc_width, new_max_width);
2413 max_desc_height = MAX(max_desc_height, new_max_height);
2414}
2415
2416/************************************************************************/
2419void show_tile_labels(int canvas_base_x, int canvas_base_y,
2420 int width_base, int height_base)
2421{
2423 const int dy = max_label_height;
2424 int new_max_width = max_label_width, new_max_height = max_label_height;
2425
2426 gui_rect_iterate_coord(mapview.gui_x0 + canvas_base_x - dx / 2,
2427 mapview.gui_y0 + canvas_base_y - dy,
2428 width_base + dx, height_base + dy,
2429 ptile, pedge, pcorner, gui_x, gui_y, map_zoom) {
2430 const int canvas_x = gui_x - mapview.gui_x0;
2431 const int canvas_y = gui_y - mapview.gui_y0;
2432
2433 if (ptile && ptile->label != NULL) {
2434 int width = 0, height = 0;
2435
2437 ptile, &width, &height);
2438 log_debug("Drawing label %s.", ptile->label);
2439
2441 /* The update was incomplete! We queue a new update. Note that
2442 * this is recursively queueing an update within a dequeuing of an
2443 * update. This is allowed specifically because of the code in
2444 * unqueue_mapview_updates(). See that function for more. */
2445 log_debug("Re-queuing tile label %s drawing.", ptile->label);
2446 update_tile_label(ptile);
2447 }
2448 new_max_width = MAX(width, new_max_width);
2449 new_max_height = MAX(height, new_max_height);
2450 }
2452
2453 /* We don't update the new max values until the end, so that the
2454 * check above to see what cities need redrawing will be complete. */
2455 max_label_width = MAX(max_label_width, new_max_width);
2456 max_label_height = MAX(max_label_height, new_max_height);
2457}
2458
2459/************************************************************************/
2465{
2466 if (punit && unit_has_orders(punit)) {
2467 struct tile *ptile = unit_tile(punit);
2468 int i;
2469
2470 for (i = 0; i < punit->orders.length; i++) {
2471 int idx = (punit->orders.index + i) % punit->orders.length;
2472 struct unit_order *order;
2473
2474 if (punit->orders.index + i >= punit->orders.length
2475 && !punit->orders.repeat) {
2476 break;
2477 }
2478
2479 if (ptile == NULL) {
2480 /* This shouldn't happen unless the server gives us invalid
2481 * data. */
2482 log_warn("Unit orders with illegal tile.");
2483 break;
2484 }
2485
2486 order = &punit->orders.list[idx];
2487
2488 switch (order->order) {
2489 case ORDER_MOVE:
2490 draw_segment(ptile, order->dir);
2491 ptile = mapstep(&(wld.map), ptile, order->dir);
2492 break;
2493 default:
2494 /* TODO: Graphics for other orders. */
2495 break;
2496 }
2497 }
2498 return TRUE;
2499 } else {
2500 return FALSE;
2501 }
2502}
2503
2504/************************************************************************/
2508void draw_segment(struct tile *src_tile, enum direction8 dir)
2509{
2510 float canvas_x, canvas_y, canvas_dx, canvas_dy;
2511
2512 /* Determine the source position of the segment. */
2513 (void) tile_to_canvas_pos(&canvas_x, &canvas_y, map_zoom, src_tile);
2516
2517 /* Determine the vector of the segment. */
2518 map_to_gui_vector(tileset, map_zoom, &canvas_dx, &canvas_dy,
2519 DIR_DX[dir], DIR_DY[dir]);
2520
2521 /* Draw the segment. */
2522 /* XXX: canvas_put_line doesn't currently take map_zoom into account
2523 * itself, but it probably should? If so this will need adjusting */
2525 get_color(tileset, COLOR_MAPVIEW_GOTO), LINE_GOTO,
2526 canvas_x, canvas_y, canvas_dx, canvas_dy);
2527
2528 /* The actual area drawn will extend beyond the base rectangle, since
2529 * the goto lines have width. */
2530 dirty_rect(MIN(canvas_x, canvas_x + canvas_dx) - GOTO_WIDTH,
2531 MIN(canvas_y, canvas_y + canvas_dy) - GOTO_WIDTH,
2532 ABS(canvas_dx) + 2 * GOTO_WIDTH,
2533 ABS(canvas_dy) + 2 * GOTO_WIDTH);
2534
2535 /* It is possible that the mapview wraps between the source and dest
2536 * tiles. In this case they will not be next to each other; they'll be
2537 * on the opposite sides of the screen. If this happens then the dest
2538 * tile will not be updated. This is consistent with the mapview design
2539 * which fails when the size of the mapview approaches that of the map. */
2540}
2541
2542/************************************************************************/
2546void decrease_unit_hp_smooth(struct unit *punit0, int hp0,
2547 struct unit *punit1, int hp1)
2548{
2549 struct unit *losing_unit = (hp0 == 0 ? punit0 : punit1);
2550 float canvas_x, canvas_y;
2551 int i;
2552
2553 set_units_in_combat(punit0, punit1);
2554
2555 /* Make sure we don't start out with fewer HP than we're supposed to
2556 * end up with (which would cause the following loop to break). */
2557 punit0->hp = MAX(punit0->hp, hp0);
2558 punit1->hp = MAX(punit1->hp, hp1);
2559
2561
2563 struct animation *anim = fc_malloc(sizeof(struct animation));
2564 struct unit *winning_unit;
2565 int winner_end_hp;
2568
2569 if (losing_unit == punit1) {
2570 winning_unit = punit0;
2571 winner_end_hp = hp0;
2572 } else {
2573 winning_unit = punit1;
2574 winner_end_hp = hp1;
2575 }
2576
2577 anim->type = ANIM_BATTLE;
2578 anim->id = -1;
2579 anim->battle.virt_loser = unit_virtual_create(unit_owner(losing_unit),
2580 NULL, unit_type_get(losing_unit),
2581 losing_unit->veteran);
2582 anim->battle.loser_tile = unit_tile(losing_unit);
2583 anim->battle.virt_loser->facing = losing_unit->facing;
2584 anim->battle.loser_hp_start = losing_unit->hp;
2585 anim->battle.virt_winner = unit_virtual_create(unit_owner(winning_unit),
2586 NULL, unit_type_get(winning_unit),
2587 winning_unit->veteran);
2588 anim->battle.winner_tile = unit_tile(winning_unit);
2589 anim->battle.virt_winner->facing = winning_unit->facing;
2590 anim->battle.winner_hp_start = MAX(winning_unit->hp, winner_end_hp);
2591 anim->battle.winner_hp_end = winner_end_hp;
2592 anim->battle.steps = MAX(losing_unit->hp,
2593 anim->battle.winner_hp_start - winner_end_hp);
2594 anim->width = aw;
2595 anim->height = ah;
2596 animation_add(anim);
2597
2598 anim = fc_malloc(sizeof(struct animation));
2599 anim->type = ANIM_EXPL;
2600 anim->id = winning_unit->id;
2601 anim->expl.tile = losing_unit->tile;
2603 anim->expl.sprite_count = sprite_vector_size(anim->expl.sprites);
2604 anim->width = aw;
2605 anim->height = ah;
2606 animation_add(anim);
2607 } else {
2608 const struct sprite_vector *anim = get_unit_explode_animation(tileset);
2609 const int num_tiles_explode_unit = sprite_vector_size(anim);
2610
2611 while (punit0->hp > hp0 || punit1->hp > hp1) {
2612 const int diff0 = punit0->hp - hp0, diff1 = punit1->hp - hp1;
2613
2616
2617 if (fc_rand(diff0 + diff1) < diff0) {
2618 punit0->hp--;
2619 refresh_unit_mapcanvas(punit0, unit_tile(punit0), FALSE, FALSE);
2620 } else {
2621 punit1->hp--;
2622 refresh_unit_mapcanvas(punit1, unit_tile(punit1), FALSE, FALSE);
2623 }
2624
2626 gui_flush();
2627
2630 }
2631
2632 if (num_tiles_explode_unit > 0
2634 unit_tile(losing_unit))) {
2635 refresh_unit_mapcanvas(losing_unit, unit_tile(losing_unit), FALSE, FALSE);
2641
2642 for (i = 0; i < num_tiles_explode_unit; i++) {
2643 int w, h;
2644 struct sprite *sprite = *sprite_vector_get(anim, i);
2645
2649
2650 /* We first draw the explosion onto the unit and draw draw the
2651 * complete thing onto the map canvas window. This avoids
2652 * flickering. */
2659 - w / 2,
2661 - h / 2,
2662 sprite);
2665
2666 flush_dirty();
2667 gui_flush();
2668
2671 }
2672 }
2673 }
2674
2675 set_units_in_combat(NULL, NULL);
2676 refresh_unit_mapcanvas(punit0, unit_tile(punit0), TRUE, FALSE);
2677 refresh_unit_mapcanvas(punit1, unit_tile(punit1), TRUE, FALSE);
2678}
2679
2680/************************************************************************/
2685 struct tile *src_tile, int dx, int dy)
2686{
2687 struct tile *dest_tile;
2688 int dest_x, dest_y, src_x, src_y;
2689 int prev_x = -1;
2690 int prev_y = -1;
2691 int tuw;
2692 int tuh;
2693
2694 /* only works for adjacent-square moves */
2695 if (dx < -1 || dx > 1 || dy < -1 || dy > 1 || (dx == 0 && dy == 0)) {
2696 return;
2697 }
2698
2699 index_to_map_pos(&src_x, &src_y, tile_index(src_tile));
2700 dest_x = src_x + dx;
2701 dest_y = src_y + dy;
2702 dest_tile = map_pos_to_tile(&(wld.map), dest_x, dest_y);
2703 if (!dest_tile) {
2704 return;
2705 }
2706
2707 if (tile_visible_mapcanvas(src_tile)
2708 || tile_visible_mapcanvas(dest_tile)) {
2709 float start_x, start_y;
2710 float canvas_dx, canvas_dy;
2711 double timing_sec = (double)gui_options.smooth_move_unit_msec / 1000.0;
2712 double mytime;
2713
2715
2716 map_to_gui_vector(tileset, map_zoom, &canvas_dx, &canvas_dy, dx, dy);
2717
2718 tile_to_canvas_pos(&start_x, &start_y, map_zoom, src_tile);
2720 start_y -= tileset_tile_height(tileset) / 2 * map_zoom;
2722 }
2723
2724 /* Bring the backing store up to date, but don't flush. */
2726
2729
2731 struct animation *anim = fc_malloc(sizeof(struct animation));
2732
2733 anim->type = ANIM_MOVEMENT;
2734 anim->id = punit->id;
2736 NULL, unit_type_get(punit),
2737 punit->veteran);
2738 anim->movement.mover->hp = punit->hp;
2739 anim->movement.mover->facing = punit->facing;
2740 anim->movement.src = src_tile;
2741 anim->movement.dest = dest_tile;
2744 anim->width = tuw;
2745 anim->height = tuh;
2746 animation_add(anim);
2747 } else {
2748
2749 /* Start the timer (AFTER the unqueue above). */
2752
2753 do {
2754 int new_x, new_y;
2755 double asecs = timer_read_seconds(anim_timer);
2756
2757 mytime = MIN(asecs, timing_sec);
2758
2759 new_x = start_x + canvas_dx * (mytime / timing_sec);
2760 new_y = start_y + canvas_dy * (mytime / timing_sec);
2761
2762 if (new_x != prev_x || new_y != prev_y) {
2763 /* Backup the canvas store to the temp store. */
2765 new_x, new_y, new_x, new_y,
2766 tuw, tuh);
2767
2768 /* Draw */
2769 put_unit(punit, mapview.store, map_zoom, new_x, new_y);
2770 dirty_rect(new_x, new_y, tuw, tuh);
2771
2772 /* Flush. */
2773 flush_dirty();
2774 gui_flush();
2775
2776 /* Restore the backup. It won't take effect until the next flush. */
2778 new_x, new_y, new_x, new_y,
2779 tuw, tuh);
2780 dirty_rect(new_x, new_y, tuw, tuh);
2781
2782 prev_x = new_x;
2783 prev_y = new_y;
2784 } else {
2785 fc_usleep(500);
2786 }
2787 } while (mytime < timing_sec);
2788 }
2789 }
2790}
2791
2792/************************************************************************/
2804struct city *find_city_or_settler_near_tile(const struct tile *ptile,
2805 struct unit **punit)
2806{
2807 struct city *closest_city;
2808 struct city *pcity;
2809 struct unit *closest_settler = NULL, *best_settler = NULL;
2810 int max_rad = rs_max_city_radius_sq();
2811
2812 if (punit) {
2813 *punit = NULL;
2814 }
2815
2816 /* Check if there is visible city working that tile */
2817 pcity = tile_worked(ptile);
2818 if (pcity && pcity->tile) {
2819 if (NULL == client.conn.playing
2820 || city_owner(pcity) == client.conn.playing) {
2821 /* Rule a */
2822 return pcity;
2823 } else {
2824 /* Rule b */
2825 return NULL;
2826 }
2827 }
2828
2829 /* Rule e */
2830 closest_city = NULL;
2831
2832 /* Check within maximum (squared) city radius */
2833 city_tile_iterate(&(wld.map), max_rad, ptile, tile1) {
2834 pcity = tile_city(tile1);
2835 if (pcity
2836 && (NULL == client.conn.playing
2837 || city_owner(pcity) == client.conn.playing)
2838 && client_city_can_work_tile(pcity, tile1)) {
2839 /*
2840 * Note, we must explicitly check if the tile is workable (with
2841 * city_can_work_tile() above), since it is possible that another
2842 * city (perhaps an UNSEEN city) may be working it!
2843 */
2844
2845 if (mapdeco_is_highlight_set(city_tile(pcity))) {
2846 /* rule c */
2847 return pcity;
2848 }
2849 if (!closest_city) {
2850 closest_city = pcity;
2851 }
2852 }
2854
2855 /* rule d */
2856 if (closest_city || !punit) {
2857 return closest_city;
2858 }
2859
2861 /* Check within maximum (squared) city radius */
2862 city_tile_iterate(&(wld.map), max_rad, ptile, tile1) {
2863 unit_list_iterate(tile1->units, psettler) {
2864 if ((NULL == client.conn.playing
2865 || unit_owner(psettler) == client.conn.playing)
2866 && unit_can_do_action(psettler, ACTION_FOUND_CITY)
2867 && city_can_be_built_here(&(wld.map), unit_tile(psettler),
2868 psettler, FALSE)) {
2869 if (closest_settler == NULL) {
2870 closest_settler = psettler;
2871 }
2872 if (best_settler == NULL && psettler->client.colored) {
2873 best_settler = psettler;
2874 }
2875 }
2878
2879 if (best_settler != NULL) {
2880 /* Rule e */
2881 *punit = best_settler;
2882 } else if (closest_settler != NULL) {
2883 /* Rule f */
2884 *punit = closest_settler;
2885 }
2886 }
2887
2888 /* rule g */
2889 return NULL;
2890}
2891
2892/************************************************************************/
2895struct city *find_city_near_tile(const struct tile *ptile)
2896{
2897 return find_city_or_settler_near_tile(ptile, NULL);
2898}
2899
2900/************************************************************************/
2906static void append_city_buycost_string(const struct city *pcity,
2907 char *buffer, int buffer_len)
2908{
2909 if (!pcity || !buffer || buffer_len < 1) {
2910 return;
2911 }
2912
2913 if (!gui_options.draw_city_buycost || !city_can_buy(pcity)) {
2914 return;
2915 }
2916
2917 cat_snprintf(buffer, buffer_len, "/%d", pcity->client.buy_cost);
2918}
2919
2920/************************************************************************/
2925 char *buffer, size_t buffer_len)
2926{
2927 int turns;
2928
2929 universal_name_translation(&pcity->production, buffer, buffer_len);
2930
2931 if (city_production_has_flag(pcity, IF_GOLD)) {
2932 return;
2933 }
2934 turns = city_production_turns_to_build(pcity, TRUE);
2935
2936 if (999 < turns) {
2937 cat_snprintf(buffer, buffer_len, " -");
2938 } else {
2939 cat_snprintf(buffer, buffer_len, " %d", turns);
2940 }
2941
2942 append_city_buycost_string(pcity, buffer, buffer_len);
2943}
2944
2945/************************************************************************/
2951 char *trade_routes_buffer,
2952 size_t trade_routes_buffer_len,
2953 enum color_std *pcolor)
2954{
2955 int num_trade_routes;
2956 int max_routes;
2957
2958 if (!trade_routes_buffer || trade_routes_buffer_len <= 0) {
2959 return;
2960 }
2961
2962 if (!pcity) {
2963 trade_routes_buffer[0] = '\0';
2964 if (pcolor) {
2965 *pcolor = COLOR_MAPVIEW_CITYTEXT;
2966 }
2967 return;
2968 }
2969
2970 num_trade_routes = trade_route_list_size(pcity->routes);
2971 max_routes = max_trade_routes(pcity);
2972
2973 fc_snprintf(trade_routes_buffer, trade_routes_buffer_len,
2974 "%d/%d", num_trade_routes, max_routes);
2975
2976 if (pcolor) {
2977 if (num_trade_routes == max_routes) {
2978 *pcolor = COLOR_MAPVIEW_TRADE_ROUTES_ALL_BUILT;
2979 } else if (num_trade_routes == 0) {
2980 *pcolor = COLOR_MAPVIEW_TRADE_ROUTES_NO_BUILT;
2981 } else {
2982 *pcolor = COLOR_MAPVIEW_TRADE_ROUTES_SOME_BUILT;
2983 }
2984 }
2985}
2986
2987/***************************************************************************/
2990
2991/* These values hold the tiles that need city, unit, or tile updates.
2992 * These different types of updates just tell what area need to be updated,
2993 * not necessarily what's sitting on the tile. A city update covers the
2994 * whole citymap area. A unit update covers just the "full" unit tile
2995 * area. A tile update covers the base tile plus half a tile in each
2996 * direction. */
2998
2999/************************************************************************/
3003static void queue_callback(void *data)
3004{
3007}
3008
3009/************************************************************************/
3013static void queue_add_callback(void)
3014{
3015 if (!callback_queued) {
3018 }
3019}
3020
3021/************************************************************************/
3039{
3040 if (can_client_change_view()) {
3041 needed_updates |= update;
3043 }
3044}
3045
3046/************************************************************************/
3055{
3056 if (can_client_change_view()) {
3057 if (!tile_updates[type]) {
3058 tile_updates[type] = tile_list_new();
3059 }
3060 tile_list_append(tile_updates[type], ptile);
3062 }
3063}
3064
3065/************************************************************************/
3068void unqueue_mapview_updates(bool write_to_screen)
3069{
3070 /* Calculate the area covered by each update type. The area array gives
3071 * the offset from the tile origin as well as the width and height of the
3072 * area to be updated. This is initialized each time when entering the
3073 * function from the existing tileset variables.
3074 *
3075 * A TILE update covers the base tile (W x H) plus a half-tile in each
3076 * direction (for edge/corner graphics), making its area 2W x 2H.
3077 *
3078 * A UNIT update covers a UW x UH area. This is centered horizontally
3079 * over the tile but extends up above the tile (e.g., units in iso-view).
3080 *
3081 * A CITYMAP update covers the whole citymap of a tile. This includes
3082 * the citymap area itself plus an extra half-tile in each direction (for
3083 * edge/corner graphics).
3084 */
3085 const float W = tileset_tile_width(tileset) * map_zoom;
3086 const float H = tileset_tile_height(tileset) * map_zoom;
3087 const float UW = tileset_unit_width(tileset) * map_zoom;
3088 const float UH = tileset_unit_height(tileset) * map_zoom;
3089 const float city_width = get_citydlg_canvas_width() * map_zoom + W;
3090 const float city_height = get_citydlg_canvas_height() * map_zoom + H;
3091 const struct {
3092 float dx, dy, w, h;
3093 } area[TILE_UPDATE_COUNT] = {
3094 {0, 0, W, H},
3095 {-W / 2, -H / 2, 2 * W, 2 * H},
3096 {(W - UW) / 2, H - UH, UW, UH},
3098 {-(city_width - W) / 2, -(city_height - H) / 2, city_width, city_height},
3100 };
3101 struct tile_list *my_tile_updates[TILE_UPDATE_COUNT];
3102
3103 int i;
3104
3105 if (!can_client_change_view()) {
3106 /* Double sanity check: make sure we don't unqueue an invalid update
3107 * after we've already detached. */
3108 return;
3109 }
3110
3111 log_debug("unqueue_mapview_update: needed_updates=%d",
3113
3114 /* This code "pops" the lists of tile updates off of the static array and
3115 * stores them locally. This allows further updates to be queued within
3116 * the function itself (namely, within update_map_canvas). */
3117 for (i = 0; i < TILE_UPDATE_COUNT; i++) {
3118 my_tile_updates[i] = tile_updates[i];
3119 tile_updates[i] = NULL;
3120 }
3121
3122 if (!map_is_empty()) {
3126 dirty_all();
3129 /* Have to update the overview too, since some tiles may have changed. */
3131 } else {
3132 int min_x = mapview.width, min_y = mapview.height;
3133 int max_x = 0, max_y = 0;
3134
3135 for (i = 0; i < TILE_UPDATE_COUNT; i++) {
3136 if (my_tile_updates[i]) {
3137 tile_list_iterate(my_tile_updates[i], ptile) {
3138 float xl, yt;
3139 int xr, yb;
3140
3141 (void) tile_to_canvas_pos(&xl, &yt, map_zoom, ptile);
3142
3143 xl += area[i].dx;
3144 yt += area[i].dy;
3145 xr = xl + area[i].w;
3146 yb = yt + area[i].h;
3147
3148 if (xr > 0 && xl < mapview.width
3149 && yb > 0 && yt < mapview.height) {
3150 min_x = MIN(min_x, xl);
3151 min_y = MIN(min_y, yt);
3152 max_x = MAX(max_x, xr);
3153 max_y = MAX(max_y, yb);
3154 }
3155
3156 /* FIXME: These overview updates should be batched as well.
3157 * Right now they account for as much as 90% of the runtime of
3158 * the unqueue. */
3159 overview_update_tile(ptile);
3161 }
3162 }
3163
3164 if (min_x < max_x && min_y < max_y) {
3165 update_map_canvas(min_x, min_y, max_x - min_x, max_y - min_y);
3166 }
3167 }
3168 }
3169
3170 for (i = 0; i < TILE_UPDATE_COUNT; i++) {
3171 if (my_tile_updates[i]) {
3172 tile_list_destroy(my_tile_updates[i]);
3173 }
3174 }
3176
3177 if (write_to_screen) {
3178 flush_dirty();
3180 }
3181}
3182
3183/************************************************************************/
3188 char *name_buffer,
3189 size_t name_buffer_len,
3190 char *growth_buffer,
3191 size_t growth_buffer_len,
3192 enum color_std *growth_color,
3193 enum color_std *production_color)
3194{
3195 fc_strlcpy(name_buffer, city_name_get(pcity), name_buffer_len);
3196
3197 *production_color = COLOR_MAPVIEW_CITYTEXT;
3198 if (NULL == client.conn.playing
3199 || city_owner(pcity) == client.conn.playing) {
3200 int turns = city_turns_to_grow(pcity);
3201
3202 if (turns == 0) {
3203 fc_snprintf(growth_buffer, growth_buffer_len, "X");
3204 } else if (turns == FC_INFINITY) {
3205 fc_snprintf(growth_buffer, growth_buffer_len, "-");
3206 } else {
3207 /* Negative turns means we're shrinking, but that's handled
3208 down below. */
3209 fc_snprintf(growth_buffer, growth_buffer_len, "%d", abs(turns));
3210 }
3211
3212 if (turns <= 0) {
3213 /* A blocked or shrinking city has its growth status shown in red. */
3214 *growth_color = COLOR_MAPVIEW_CITYGROWTH_BLOCKED;
3215 } else {
3216 *growth_color = COLOR_MAPVIEW_CITYTEXT;
3217 }
3218
3219 if (pcity->surplus[O_SHIELD] < 0) {
3220 *production_color = COLOR_MAPVIEW_CITYPROD_NEGATIVE;
3221 }
3222 } else {
3223 growth_buffer[0] = '\0';
3224 *growth_color = COLOR_MAPVIEW_CITYTEXT;
3225 }
3226}
3227
3228/************************************************************************/
3232static bool can_do_cached_drawing(void)
3233{
3234 const int W = tileset_tile_width(tileset) * map_zoom;
3235 const int H = tileset_tile_height(tileset) * map_zoom;
3237
3238 /* If the mapview window is too large, cached drawing is not possible.
3239 *
3240 * BACKGROUND: cached drawing occurs when the mapview is scrolled just
3241 * a short distance. The majority of the mapview window can simply be
3242 * copied while the newly visible areas must be drawn from scratch. This
3243 * speeds up drawing significantly, especially when using the scrollbars
3244 * or mapview sliding.
3245 *
3246 * When the mapview is larger than the map, however, some tiles may become
3247 * visible twice. In this case one instance of the tile will be drawn
3248 * while all others are drawn black. When this happens the cached drawing
3249 * system breaks since it assumes the mapview canvas is an "ideal" window
3250 * over the map. So black tiles may be scrolled from the edges of the
3251 * mapview into the center, while drawn tiles may be scrolled from the
3252 * center of the mapview out to the edges. The result is very bad.
3253 *
3254 * There are a few different ways this could be solved. One way is simply
3255 * to turn off cached drawing, which is what we do now. If the mapview
3256 * window gets to be too large, the caching is disabled. Another would
3257 * be to prevent the window from getting too large in the first place -
3258 * but because the window boundaries aren't at an even tile this would
3259 * mean the entire map could never be shown. Yet another way would be
3260 * to draw tiles more than once if they are visible in multiple locations
3261 * on the mapview.
3262 *
3263 * The logic below is complicated and determined in part by
3264 * trial-and-error. */
3265 if (!current_topo_has_flag(TF_WRAPX) && !current_topo_has_flag(TF_WRAPY)) {
3266 /* An unwrapping map: no limitation. On an unwrapping map no tile can
3267 * be visible twice so there's no problem. */
3268 return TRUE;
3269 }
3270 if (XOR(current_topo_has_flag(TF_ISO) || current_topo_has_flag(TF_HEX),
3272 /* Non-matching. In this case the mapview does not line up with the
3273 * map's axis of wrapping. This will give very bad results for the
3274 * player!
3275 * We can never show more than half of the map.
3276 *
3277 * We divide by 4 below because we have to divide by 2 twice. The
3278 * first division by 2 is because the square must be half the size
3279 * of the (width+height). The second division by two is because for
3280 * an iso-map, NATURAL_XXX has a scale of 2, whereas for iso-view
3281 * NORMAL_TILE_XXX has a scale of 2. */
3282 return (w <= (NATURAL_WIDTH + NATURAL_HEIGHT) * W / 4
3283 && h <= (NATURAL_WIDTH + NATURAL_HEIGHT) * H / 4);
3284 } else {
3285 /* Matching. */
3286 const int isofactor = (tileset_is_isometric(tileset) ? 2 : 1);
3287 const int isodiff = (tileset_is_isometric(tileset) ? 6 : 2);
3288
3289 /* Now we can use the full width and height, with the exception of a small
3290 * area on each side. */
3291 if (current_topo_has_flag(TF_WRAPX)
3292 && w > (NATURAL_WIDTH - isodiff) * W / isofactor) {
3293 return FALSE;
3294 }
3295 if (current_topo_has_flag(TF_WRAPY)
3296 && h > (NATURAL_HEIGHT - isodiff) * H / isofactor) {
3297 return FALSE;
3298 }
3299 return TRUE;
3300 }
3301}
3302
3303/************************************************************************/
3308{
3309 /* HACK: this must be called on a map_info packet. */
3311
3312 mapdeco_free();
3313 mapdeco_highlight_table = tile_hash_new();
3314 mapdeco_crosshair_table = tile_hash_new();
3315 mapdeco_gotoline_table = gotoline_hash_new();
3316}
3317
3318/************************************************************************/
3322{
3324 tile_hash_destroy(mapdeco_highlight_table);
3326 }
3328 tile_hash_destroy(mapdeco_crosshair_table);
3330 }
3332 gotoline_hash_destroy(mapdeco_gotoline_table);
3334 }
3335}
3336
3337/************************************************************************/
3341void mapdeco_set_highlight(const struct tile *ptile, bool highlight)
3342{
3343 bool changed = FALSE;
3344
3345 if (!ptile || !mapdeco_highlight_table) {
3346 return;
3347 }
3348
3349 if (highlight) {
3350 changed = tile_hash_insert(mapdeco_highlight_table, ptile, NULL);
3351 } else {
3352 changed = tile_hash_remove(mapdeco_highlight_table, ptile);
3353 }
3354
3355 if (changed) {
3356 /* FIXME: Remove the cast. */
3357 refresh_tile_mapcanvas((struct tile *) ptile, TRUE, FALSE);
3358 }
3359}
3360
3361/************************************************************************/
3364bool mapdeco_is_highlight_set(const struct tile *ptile)
3365{
3366 if (!ptile || !mapdeco_highlight_table) {
3367 return FALSE;
3368 }
3369 return tile_hash_lookup(mapdeco_highlight_table, ptile, NULL);
3370}
3371
3372/************************************************************************/
3377{
3379 return;
3380 }
3381
3385
3386 tile_hash_clear(mapdeco_highlight_table);
3387}
3388
3389/************************************************************************/
3392void mapdeco_set_crosshair(const struct tile *ptile, bool crosshair)
3393{
3394 bool changed;
3395
3396 if (!mapdeco_crosshair_table || !ptile) {
3397 return;
3398 }
3399
3400 if (crosshair) {
3401 changed = tile_hash_insert(mapdeco_crosshair_table, ptile, NULL);
3402 } else {
3403 changed = tile_hash_remove(mapdeco_crosshair_table, ptile);
3404 }
3405
3406 if (changed) {
3407 /* FIXME: Remove the cast. */
3408 refresh_tile_mapcanvas((struct tile *) ptile, FALSE, FALSE);
3409 }
3410}
3411
3412/************************************************************************/
3415bool mapdeco_is_crosshair_set(const struct tile *ptile)
3416{
3417 if (!mapdeco_crosshair_table || !ptile) {
3418 return FALSE;
3419 }
3420 return tile_hash_lookup(mapdeco_crosshair_table, ptile, NULL);
3421}
3422
3423/************************************************************************/
3428{
3430 return;
3431 }
3432
3436
3437 tile_hash_clear(mapdeco_crosshair_table);
3438}
3439
3440/************************************************************************/
3445void mapdeco_add_gotoline(const struct tile *ptile, enum direction8 dir)
3446{
3447 struct gotoline_counter *pglc;
3448 const struct tile *ptile_dest;
3449 bool changed;
3450
3451 if (!mapdeco_gotoline_table || !ptile
3452 || !(dir <= direction8_max())) {
3453 return;
3454 }
3455 ptile_dest = mapstep(&(wld.map), ptile, dir);
3456 if (!ptile_dest) {
3457 return;
3458 }
3459
3460 if (!gotoline_hash_lookup(mapdeco_gotoline_table, ptile, &pglc)) {
3461 pglc = gotoline_counter_new();
3462 gotoline_hash_insert(mapdeco_gotoline_table, ptile, pglc);
3463 }
3464 changed = (pglc->line_count[dir] < 1);
3465 pglc->line_count[dir]++;
3466
3467 if (changed) {
3468 /* FIXME: Remove cast. */
3469 refresh_tile_mapcanvas((struct tile *) ptile, FALSE, FALSE);
3470 refresh_tile_mapcanvas((struct tile *) ptile_dest, FALSE, FALSE);
3471 }
3472}
3473
3474/************************************************************************/
3479void mapdeco_remove_gotoline(const struct tile *ptile,
3480 enum direction8 dir)
3481{
3482 struct gotoline_counter *pglc;
3483 bool changed = FALSE;
3484
3485 if (!mapdeco_gotoline_table || !ptile
3486 || !(dir <= direction8_max())) {
3487 return;
3488 }
3489
3490 if (!gotoline_hash_lookup(mapdeco_gotoline_table, ptile, &pglc)) {
3491 return;
3492 }
3493
3494 pglc->line_count[dir]--;
3495 if (pglc->line_count[dir] <= 0) {
3496 pglc->line_count[dir] = 0;
3497 changed = TRUE;
3498 }
3499
3500 if (changed) {
3501 /* FIXME: Remove the casts. */
3502 refresh_tile_mapcanvas((struct tile *) ptile, FALSE, FALSE);
3503 ptile = mapstep(&(wld.map), ptile, dir);
3504 if (ptile != NULL) {
3505 refresh_tile_mapcanvas((struct tile *) ptile, FALSE, FALSE);
3506 }
3507 }
3508}
3509
3510/************************************************************************/
3516{
3517 const struct unit_order *porder;
3518 const struct tile *ptile;
3519 int i, ind;
3520
3522 || punit->orders.length < 1) {
3523 return;
3524 }
3525
3526 ptile = unit_tile(punit);
3527
3528 for (i = 0; ptile != NULL && i < punit->orders.length; i++) {
3529 if (punit->orders.index + i >= punit->orders.length
3530 && !punit->orders.repeat) {
3531 break;
3532 }
3533
3534 ind = (punit->orders.index + i) % punit->orders.length;
3535 porder = &punit->orders.list[ind];
3536 if (porder->order != ORDER_MOVE) {
3537 /* FIXME: should display some indication of non-move orders here. */
3538 continue;
3539 }
3540
3541 mapdeco_add_gotoline(ptile, porder->dir);
3542 ptile = mapstep(&(wld.map), ptile, porder->dir);
3543 }
3544}
3545
3546/************************************************************************/
3550bool mapdeco_is_gotoline_set(const struct tile *ptile,
3551 enum direction8 dir)
3552{
3553 struct gotoline_counter *pglc;
3554
3555 if (!ptile || !(dir <= direction8_max())
3557 return FALSE;
3558 }
3559
3560 if (!gotoline_hash_lookup(mapdeco_gotoline_table, ptile, &pglc)) {
3561 return FALSE;
3562 }
3563
3564 return pglc->line_count[dir] > 0;
3565}
3566
3567/************************************************************************/
3572{
3574 return;
3575 }
3576
3579 adjc_dir_iterate(&(wld.map), ptile, ptile_dest, dir) {
3580 if (pglc->line_count[dir] > 0) {
3581 refresh_tile_mapcanvas(ptile_dest, FALSE, FALSE);
3582 }
3585 gotoline_hash_clear(mapdeco_gotoline_table);
3586}
3587
3588/************************************************************************/
3594{
3595 int old_tile_width = mapview.tile_width;
3596 int old_tile_height = mapview.tile_height;
3597 int old_width = mapview.width, old_height = mapview.height;
3598 int tile_width = (width + tileset_tile_width(tileset) * map_zoom - 1) /
3600 int tile_height = (height + tileset_tile_height(tileset) * map_zoom - 1) /
3602 int full_width = tile_width * tileset_tile_width(tileset) * map_zoom;
3603 int full_height = tile_height * tileset_tile_height(tileset) * map_zoom;
3604 bool tile_size_changed, size_changed, redrawn = FALSE;
3605
3606 /* Resized */
3607
3608 /* Since a resize is only triggered when the tile_*** changes, the canvas
3609 * width and height must include the entire backing store - otherwise
3610 * small resizings may lead to undrawn tiles. */
3611 mapview.tile_width = tile_width;
3612 mapview.tile_height = tile_height;
3615 mapview.store_width = full_width;
3616 mapview.store_height = full_height;
3617
3618 /* Check for what's changed. */
3619 tile_size_changed = (tile_width != old_tile_width
3620 || tile_height != old_tile_height);
3621 size_changed = (width != old_width || height != old_height);
3622
3623 /* If the tile size has changed, resize the canvas. */
3624 if (tile_size_changed) {
3625 if (mapview.store) {
3628 }
3629 mapview.store = canvas_create(full_width, full_height);
3633 get_color(tileset, COLOR_MAPVIEW_UNKNOWN),
3634 0, 0, full_width / mouse_zoom, full_height / mouse_zoom);
3635
3636 mapview.tmp_store = canvas_create(full_width, full_height);
3639 }
3640
3642 if (tile_size_changed) {
3643 if (center_tile != NULL) {
3644 int x_left, y_top;
3645 float gui_x, gui_y;
3646
3647 index_to_map_pos(&x_left, &y_top, tile_index(center_tile));
3648 map_to_gui_pos(tileset, map_zoom, &gui_x, &gui_y, x_left, y_top);
3649
3650 /* Put the center pixel of the tile at the exact center of the mapview. */
3651 gui_x -= (mapview.width - tileset_tile_width(tileset) * map_zoom) / 2;
3653
3654 calc_mapview_origin(&gui_x, &gui_y, map_zoom);
3655 mapview.gui_x0 = gui_x;
3656 mapview.gui_y0 = gui_y;
3657 }
3660
3661 /* Do not draw to the screen here as that could cause problems
3662 * when we are only initially setting up the view and some widgets
3663 * are not yet ready. */
3665 redrawn = TRUE;
3666 }
3667
3668 /* If the width/height has changed, update the scrollbars even if
3669 * the backing store is not resized. */
3670 if (size_changed) {
3673 }
3674 }
3675
3677
3678 return redrawn;
3679}
3680
3681/************************************************************************/
3685{
3686 /* Create a dummy map to make sure mapview.store is never NULL. */
3687 map_canvas_resized(1, 1);
3688}
3689
3690/************************************************************************/
3698
3699/************************************************************************/
3703{
3704 struct sprite *sprite
3706
3708 *width *= 7;
3709 *height *= 7;
3710}
3711
3712/************************************************************************/
3716 const struct player *pplayer)
3717{
3718 int i, x, y;
3719 const struct player_spaceship *ship = &pplayer->spaceship;
3720 int w, h;
3721 struct sprite *spr;
3722 struct tileset *t = tileset;
3723
3725 get_sprite_dimensions(spr, &w, &h);
3726
3728 get_color(tileset, COLOR_SPACESHIP_BACKGROUND),
3729 0, 0, w * 7, h * 7);
3730
3731 for (i = 0; i < NUM_SS_MODULES; i++) {
3732 const int j = i / 3;
3733 const int k = i % 3;
3734
3735 if ((k == 0 && j >= ship->habitation)
3736 || (k == 1 && j >= ship->life_support)
3737 || (k == 2 && j >= ship->solar_panels)) {
3738 continue;
3739 }
3740 x = modules_info[i].x * w / 4 - w / 2;
3741 y = modules_info[i].y * h / 4 - h / 2;
3742
3743 spr = (k == 0 ? get_spaceship_sprite(t, SPACESHIP_HABITATION)
3746 canvas_put_sprite_full(pcanvas, x, y, spr);
3747 }
3748
3749 for (i = 0; i < NUM_SS_COMPONENTS; i++) {
3750 const int j = i / 2;
3751 const int k = i % 2;
3752
3753 if ((k == 0 && j >= ship->fuel)
3754 || (k == 1 && j >= ship->propulsion)) {
3755 continue;
3756 }
3757 x = components_info[i].x * w / 4 - w / 2;
3758 y = components_info[i].y * h / 4 - h / 2;
3759
3760 spr = ((k == 0) ? get_spaceship_sprite(t, SPACESHIP_FUEL)
3762
3763 canvas_put_sprite_full(pcanvas, x, y, spr);
3764
3765 if (k && ship->state == SSHIP_LAUNCHED) {
3767 canvas_put_sprite_full(pcanvas, x + w, y, spr);
3768 }
3769 }
3770
3771 for (i = 0; i < NUM_SS_STRUCTURALS; i++) {
3772 if (!BV_ISSET(ship->structure, i)) {
3773 continue;
3774 }
3775 x = structurals_info[i].x * w / 4 - w / 2;
3776 y = structurals_info[i].y * h / 4 - h / 2;
3777
3779 canvas_put_sprite_full(pcanvas, x, y, spr);
3780 }
3781}
3782
3783/****************************************************************************
3784 Map link mark module: it makes link marks when a link is sent by chating,
3785 or restore a mark with clicking a link on the chatline.
3786****************************************************************************/
3788 enum text_link_type type; /* The target type. */
3789 int id; /* The city or unit id, or tile index. */
3790 int turn_counter; /* The turn counter before it disappears. */
3791};
3792
3793#define SPECLIST_TAG link_mark
3794#define SPECLIST_TYPE struct link_mark
3795#include "speclist.h"
3796#define link_marks_iterate(pmark) \
3797 TYPED_LIST_ITERATE(struct link_mark, link_marks, pmark)
3798#define link_marks_iterate_end LIST_ITERATE_END
3799
3800static struct link_mark_list *link_marks = NULL;
3801
3802/************************************************************************/
3805static struct link_mark *link_mark_find(enum text_link_type type, int id)
3806{
3807 link_marks_iterate(pmark) {
3808 if (pmark->type == type && pmark->id == id) {
3809 return pmark;
3810 }
3812
3813 return NULL;
3814}
3815
3816/************************************************************************/
3820 int id, int turns)
3821{
3822 struct link_mark *pmark = fc_malloc(sizeof(struct link_mark));
3823
3824 pmark->type = type;
3825 pmark->id = id;
3826 pmark->turn_counter = turns;
3827
3828 return pmark;
3829}
3830
3831/************************************************************************/
3834static void link_mark_destroy(struct link_mark *pmark)
3835{
3836 free(pmark);
3837}
3838
3839/************************************************************************/
3842static struct tile *link_mark_tile(const struct link_mark *pmark)
3843{
3844 switch (pmark->type) {
3845 case TLT_CITY:
3846 {
3847 struct city *pcity = game_city_by_number(pmark->id);
3848 return pcity ? pcity->tile : NULL;
3849 }
3850 case TLT_TILE:
3851 return index_to_tile(&(wld.map), pmark->id);
3852 case TLT_UNIT:
3853 {
3854 struct unit *punit = game_unit_by_number(pmark->id);
3855 return punit ? unit_tile(punit) : NULL;
3856 }
3857 }
3858 return NULL;
3859}
3860
3861/************************************************************************/
3864static struct color *link_mark_color(const struct link_mark *pmark)
3865{
3866 switch (pmark->type) {
3867 case TLT_CITY:
3868 return get_color(tileset, COLOR_MAPVIEW_CITY_LINK);
3869 case TLT_TILE:
3870 return get_color(tileset, COLOR_MAPVIEW_TILE_LINK);
3871 case TLT_UNIT:
3872 return get_color(tileset, COLOR_MAPVIEW_UNIT_LINK);
3873 }
3874 return NULL;
3875}
3876
3877/************************************************************************/
3880static void link_mark_draw(const struct link_mark *pmark)
3881{
3884 int xd = width / 20, yd = height / 20;
3885 int xlen = width / 3, ylen = height / 3;
3886 float canvas_x, canvas_y;
3887 int x_left, x_right, y_top, y_bottom;
3888 struct tile *ptile = link_mark_tile(pmark);
3889 struct color *pcolor = link_mark_color(pmark);
3890
3891 if (!ptile || !tile_to_canvas_pos(&canvas_x, &canvas_y, map_zoom, ptile)) {
3892 return;
3893 }
3894
3895 x_left = canvas_x + xd;
3896 x_right = canvas_x + width - xd;
3897 y_top = canvas_y + yd;
3898 y_bottom = canvas_y + height - yd;
3899
3900 /* XXX: canvas_put_line doesn't currently take map_zoom into account
3901 * itself, but it probably should? If so these will need adjusting */
3902 canvas_put_line(mapview.store, pcolor, LINE_TILE_FRAME, x_left, y_top, xlen, 0);
3903 canvas_put_line(mapview.store, pcolor, LINE_TILE_FRAME, x_left, y_top, 0, ylen);
3904
3905 canvas_put_line(mapview.store, pcolor, LINE_TILE_FRAME, x_right, y_top, -xlen, 0);
3906 canvas_put_line(mapview.store, pcolor, LINE_TILE_FRAME, x_right, y_top, 0, ylen);
3907
3908 canvas_put_line(mapview.store, pcolor, LINE_TILE_FRAME, x_left, y_bottom, xlen, 0);
3909 canvas_put_line(mapview.store, pcolor, LINE_TILE_FRAME, x_left, y_bottom, 0, -ylen);
3910
3911 canvas_put_line(mapview.store, pcolor, LINE_TILE_FRAME, x_right, y_bottom, -xlen, 0);
3912 canvas_put_line(mapview.store, pcolor, LINE_TILE_FRAME, x_right, y_bottom, 0, -ylen);
3913}
3914
3915/************************************************************************/
3919{
3920 if (link_marks) {
3922 }
3923
3924 link_marks = link_mark_list_new_full(link_mark_destroy);
3925}
3926
3927/************************************************************************/
3931{
3932 if (!link_marks) {
3933 return;
3934 }
3935
3936 link_mark_list_destroy(link_marks);
3937 link_marks = NULL;
3938}
3939
3940/************************************************************************/
3944{
3945 link_marks_iterate(pmark) {
3946 link_mark_draw(pmark);
3948}
3949
3950/************************************************************************/
3954{
3955 link_mark_list_clear(link_marks);
3957}
3958
3959/************************************************************************/
3963{
3964 link_marks_iterate(pmark) {
3965 if (--pmark->turn_counter <= 0) {
3966 link_mark_list_remove(link_marks, pmark);
3967 }
3969
3970 /* update_map_canvas_visible(); not needed here. */
3971}
3972
3973/************************************************************************/
3977{
3978 struct link_mark *pmark = link_mark_find(type, id);
3979 struct tile *ptile;
3980
3981 if (pmark) {
3982 /* Already displayed, but maybe increase the turn counter. */
3983 pmark->turn_counter = MAX(pmark->turn_counter, 2);
3984 return;
3985 }
3986
3987 pmark = link_mark_new(type, id, 2);
3988 link_mark_list_append(link_marks, pmark);
3989 ptile = link_mark_tile(pmark);
3990 if (ptile && tile_visible_mapcanvas(ptile)) {
3992 }
3993}
3994
3995/************************************************************************/
3999{
4000 struct link_mark *pmark;
4001 struct tile *ptile;
4002
4003 if (link_mark_find(type, id)) {
4004 return;
4005 }
4006
4007 pmark = link_mark_new(type, id, 1);
4008 link_mark_list_append(link_marks, pmark);
4009 ptile = link_mark_tile(pmark);
4010 if (ptile && tile_visible_mapcanvas(ptile)) {
4012 }
4013}
4014
4015/************************************************************************/
4019 struct tileset *tset,
4020 int *tset_topo)
4021{
4022 int tileset_topology;
4023
4024 if (tileset_hex_width(tset) > 0) {
4026 tileset_topology = TF_HEX | TF_ISO;
4027 } else if (tileset_hex_height(tset) > 0) {
4029 tileset_topology = TF_HEX;
4030 } else if (tileset_is_isometric(tset)) {
4031 tileset_topology = TF_ISO;
4032 } else {
4033 tileset_topology = 0;
4034 }
4035
4036 if (tset_topo != NULL) {
4037 *tset_topo = tileset_topology;
4038 }
4039
4040 if (tileset_topology & TF_HEX) {
4041 if ((topology_id & (TF_HEX | TF_ISO)) == tileset_topology) {
4042 return TOPO_COMPATIBLE;
4043 }
4044
4045 /* Hex topology must match for both hexness and iso/non-iso */
4046 return TOPO_INCOMP_HARD;
4047 }
4048
4049 if (topology_id & TF_HEX) {
4050 return TOPO_INCOMP_HARD;
4051 }
4052
4053 if ((topology_id & TF_ISO) != (tileset_topology & TF_ISO)) {
4054 /* Non-hex iso/non-iso incompatibility is a soft one */
4055 return TOPO_INCOMP_SOFT;
4056 }
4057
4058 return TOPO_COMPATIBLE;
4059}
4060
4061/************************************************************************/
4064const char *describe_topology(int topo)
4065{
4066 if (topo & TF_ISO) {
4067 if (topo & TF_HEX) {
4068 return _("ISO|Hex");
4069 }
4070 return _("ISO");
4071 }
4072 if (topo & TF_HEX) {
4073 return _("Hex");
4074 }
4075
4076 return _("Overhead");
4077}
4078
4079/************************************************************************/
4086
4087/************************************************************************/
4091{
4092 return infratile;
4093}
4094
4095/************************************************************************/
4098void client_infratile_set(struct tile *ptile)
4099{
4100 struct tile *old_tile = infratile;
4101
4102 infratile = ptile;
4103
4104 if (old_tile != NULL) {
4105 refresh_tile_mapcanvas(old_tile, FALSE, TRUE);
4106 }
4107 if (ptile != NULL) {
4109 }
4110}
#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 struct sprite bool int int fog_y struct canvas struct sprite struct color * pcolor
Definition canvas_g.h:57
struct canvas int int canvas_y
Definition canvas_g.h:43
canvas_put_sprite_fogged
Definition canvas_g.h:48
struct canvas int canvas_x
Definition canvas_g.h:43
FONT_CITY_NAME
Definition canvas_g.h:70
canvas_put_sprite
Definition canvas_g.h:42
FONT_CITY_PROD
Definition canvas_g.h:71
struct canvas int int struct sprite bool int int fog_y struct canvas struct sprite struct color int int canvas_y struct canvas struct color enum line_type ltype int start_x int start_y int dx int dy enum client_font
Definition canvas_g.h:69
struct canvas int int struct sprite int int int width
Definition canvas_g.h:44
struct canvas * pcanvas
Definition canvas_g.h:42
canvas_put_text
Definition canvas_g.h:77
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
@ 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
struct canvas int int struct sprite bool fog
Definition canvas_g.h:51
const char * city_name_get(const struct city *pcity)
Definition city.c:1115
bool city_production_has_flag(const struct city *pcity, enum impr_flag_id flag)
Definition city.c:712
int city_production_turns_to_build(const struct city *pcity, bool include_shield_stock)
Definition city.c:805
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:1460
int rs_max_city_radius_sq(void)
Definition city.c:154
int city_turns_to_grow(const struct city *pcity)
Definition city.c:1969
#define cities_iterate_end
Definition city.h:497
#define city_list_iterate(citylist, pcity)
Definition city.h:488
#define city_tile(_pcity_)
Definition city.h:544
#define cities_iterate(pcity)
Definition city.h:492
static citizens city_size_get(const struct city *pcity)
Definition city.h:549
#define output_type_iterate(output)
Definition city.h:821
#define city_owner(_pcity_)
Definition city.h:543
#define city_list_iterate_end
Definition city.h:490
#define city_tile_iterate(_nmap, _radius_sq, _city_tile, _tile)
Definition city.h:222
#define city_tile_iterate_end
Definition city.h:230
#define output_type_iterate_end
Definition city.h:827
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)
void set_units_in_combat(struct unit *pattacker, struct unit *pdefender)
Definition control.c:1031
enum cursor_hover_state hover_state
Definition control.c:89
@ HOVER_GOTO
Definition control.h:27
@ HOVER_PARADROP
Definition control.h:28
@ HOVER_ACT_SEL_TGT
Definition control.h:31
@ HOVER_NONE
Definition control.h:26
@ HOVER_CONNECT
Definition control.h:29
@ HOVER_PATROL
Definition control.h:30
@ HOVER_GOTO_SEL_TGT
Definition control.h:32
struct unit struct city struct unit struct tile struct extra_type const struct act_prob *act_probs int actor_unit_id struct unit struct unit * punit
Definition dialogs_g.h:73
int int id
Definition editgui_g.h:28
bool editor_is_active(void)
Definition editor.c:346
bool editor_tile_is_selected(const struct tile *ptile)
Definition editor.c:1117
#define DIR8_MAGIC_MAX
Definition fc_types.h:441
@ O_SHIELD
Definition fc_types.h:91
#define _(String)
Definition fcintl.h:67
text_link_type
@ TLT_TILE
@ TLT_UNIT
@ TLT_CITY
struct civ_game game
Definition game.c:57
struct world wld
Definition game.c:58
struct unit * game_unit_by_number(int id)
Definition game.c:111
struct city * game_city_by_number(int id)
Definition game.c:102
void canvas_put_rectangle(struct canvas *pcanvas, struct color *pcolor, int canvas_x, int canvas_y, int width, int height)
Definition canvas.c:176
void canvas_put_sprite_full(struct canvas *pcanvas, int canvas_x, int canvas_y, struct sprite *sprite)
Definition canvas.c:148
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:347
void canvas_free(struct canvas *store)
Definition canvas.c:44
void canvas_put_line(struct canvas *pcanvas, struct color *pcolor, enum line_type ltype, int start_x, int start_y, int dx, int dy)
Definition canvas.c:223
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 add_idle_callback(void(callback)(void *), void *data)
Definition gui_main.c:2210
void create_line_at_mouse_pos(void)
Definition mapctrl.c:347
void update_rect_at_mouse_pos(void)
Definition mapctrl.c:373
void flush_dirty(void)
Definition mapview.c:450
void dirty_all(void)
Definition mapview.c:438
void update_map_canvas_scrollbars_size(void)
Definition mapview.c:675
void gui_flush(void)
Definition mapview.c:462
void update_map_canvas_scrollbars(void)
Definition mapview.c:660
GType type
Definition repodlgs.c:1312
void get_sprite_dimensions(struct sprite *sprite, int *width, int *height)
Definition sprite.c:107
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:454
struct tile * map_pos_to_tile(const struct civ_map *nmap, int map_x, int map_y)
Definition map.c:417
void base_map_distance_vector(int *dx, int *dy, int x0dv, int y0dv, int x1dv, int y1dv)
Definition map.c:1022
struct tile * mapstep(const struct civ_map *nmap, const struct tile *ptile, enum direction8 dir)
Definition map.c:369
struct tile * nearest_real_tile(const struct civ_map *nmap, int x, int y)
Definition map.c:993
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:977
#define current_topo_has_flag(flag)
Definition map.h:45
#define adjc_dir_iterate(nmap, center_tile, itr_tile, dir_itr)
Definition map.h:432
#define NATURAL_HEIGHT
Definition map.h:221
#define MAP_IS_ISOMETRIC
Definition map.h:40
#define MAP_TO_NATIVE_POS(pnat_x, pnat_y, map_x, map_y)
Definition map.h:166
#define NATIVE_TO_MAP_POS(pmap_x, pmap_y, nat_x, nat_y)
Definition map.h:160
#define adjc_dir_iterate_end
Definition map.h:436
#define adjc_dir_base_iterate(nmap, center_tile, dir_itr)
Definition map.h:439
#define NATURAL_WIDTH
Definition map.h:220
#define adjc_dir_base_iterate_end
Definition map.h:443
#define index_to_map_pos(pmap_x, pmap_y, mindex)
Definition map.h:227
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)
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:71
void flush_dirty_overview(void)
void refresh_overview_canvas(void)
void center_tile_overviewcanvas(void)
void overview_update_tile(struct tile *ptile)
char * lines
Definition packhand.c:129
bool can_player_see_units_in_city(const struct player *pplayer, const struct city *pcity)
Definition player.c:1113
#define fc_rand(_size)
Definition rand.h:34
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
const struct sprite_vector * sprites
struct tile * loser_tile
struct tile * dest
struct unit * virt_loser
struct tile * winner_tile
struct tile * nuke_tile
struct tile * tile
struct tile * src
struct animation::@216::@221 nuke
struct animation::@216::@219 battle
struct unit * mover
enum animation_type type
struct animation::@216::@218 movement
struct unit * virt_winner
struct animation::@216::@220 expl
float zoom
Definition canvas.h:24
Definition city.h:309
int color_index
Definition city.h:453
int surplus[O_LAST]
Definition city.h:343
struct trade_route_list * routes
Definition city.h:332
bool occupied
Definition city.h:443
struct universal production
Definition city.h:382
int buy_cost
Definition city.h:449
struct tile * tile
Definition city.h:311
bool colored
Definition city.h:452
struct city::@17::@20 client
struct sprite * food
Definition tilespec.h:329
struct sprite * trade
Definition tilespec.h:330
struct sprite * shields
Definition tilespec.h:327
struct sprite * background
Definition tilespec.h:332
struct sprite * occupied
Definition tilespec.h:331
struct sprite_vector occupancy
Definition tilespec.h:333
struct packet_scenario_info scenario
Definition game.h:87
int xsize
Definition map_types.h:77
int ysize
Definition map_types.h:77
struct connection conn
Definition client_main.h:96
bool draw_native
Definition options.h:207
int smooth_center_slide_msec
Definition options.h:145
bool draw_city_names
Definition options.h:188
bool draw_city_productions
Definition options.h:190
int smooth_move_unit_msec
Definition options.h:144
bool draw_borders
Definition options.h:206
bool draw_city_buycost
Definition options.h:191
bool draw_fog_of_war
Definition options.h:205
bool draw_city_trade_routes
Definition options.h:192
bool draw_map_grid
Definition options.h:187
bool draw_city_growth
Definition options.h:189
int smooth_combat_step_msec
Definition options.h:146
bool draw_full_citybar
Definition options.h:208
Definition colors.h:20
struct player * playing
Definition connection.h:156
int line_count[DIR8_MAGIC_MAX]
bv_spaceship_structure structure
Definition spaceship.h:100
enum spaceship_state state
Definition spaceship.h:108
struct city_list * cities
Definition player.h:281
struct player_spaceship spaceship
Definition player.h:286
Definition tile.h:49
char * label
Definition tile.h:64
struct unit_list * units
Definition tile.h:57
Definition timing.c:81
enum unit_orders order
Definition unit.h:93
enum direction8 dir
Definition unit.h:102
Definition unit.h:138
int length
Definition unit.h:195
int id
Definition unit.h:145
int index
Definition unit.h:195
int hp
Definition unit.h:151
struct unit::@80::@82 client
struct unit::@79 orders
bool colored
Definition unit.h:219
enum direction8 facing
Definition unit.h:142
struct tile * tile
Definition unit.h:140
struct unit_order * list
Definition unit.h:198
bool repeat
Definition unit.h:196
int color_index
Definition unit.h:220
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:969
size_t fc_strlcpy(char *dest, const char *src, size_t n)
Definition support.c:787
void fc_usleep(unsigned long usec)
Definition support.c:640
int cat_snprintf(char *str, size_t n, const char *format,...)
Definition support.c:995
#define fc__attribute(x)
Definition support.h:89
#define TRUE
Definition support.h:46
#define FALSE
Definition support.h:47
struct city * tile_city(const struct tile *ptile)
Definition tile.c:83
#define tile_index(_pt_)
Definition tile.h:87
#define tile_hash_iterate(hash, ptile)
Definition tile.h:81
#define tile_worked(_tile)
Definition tile.h:113
@ TILE_KNOWN_UNSEEN
Definition tile.h:36
@ TILE_UNKNOWN
Definition tile.h:35
#define tile_list_iterate(tile_list, ptile)
Definition tile.h:72
#define tile_hash_iterate_end
Definition tile.h:83
#define TILE_XY(ptile)
Definition tile.h:42
#define tile_list_iterate_end
Definition tile.h:74
struct sprite * get_nuke_explode_sprite(const struct tileset *t)
Definition tilespec.c:6625
int tileset_hex_width(const struct tileset *t)
Definition tilespec.c:684
int tileset_unit_width(const struct tileset *t)
Definition tilespec.c:760
struct unit * get_drawable_unit(const struct tileset *t, struct tile *ptile, const struct city *citymode)
Definition tilespec.c:6302
int tileset_unit_height(const struct tileset *t)
Definition tilespec.c:768
struct sprite * get_city_flag_sprite(const struct tileset *t, const struct city *pcity)
Definition tilespec.c:4227
int tileset_full_tile_height(const struct tileset *t)
Definition tilespec.c:752
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:6730
int tileset_citybar_offset_y(const struct tileset *t)
Definition tilespec.c:873
bool unit_drawn_with_city_outline(const struct unit *punit, bool check_focus)
Definition tilespec.c:5266
const struct sprite_vector * get_unit_explode_animation(const struct tileset *t)
Definition tilespec.c:6614
bool tileset_is_isometric(const struct tileset *t)
Definition tilespec.c:675
int tileset_tile_height(const struct tileset *t)
Definition tilespec.c:728
struct sprite * get_spaceship_sprite(const struct tileset *t, enum spaceship_part part)
Definition tilespec.c:6424
int tileset_hex_height(const struct tileset *t)
Definition tilespec.c:693
const struct citybar_sprites * get_citybar_sprites(const struct tileset *t)
Definition tilespec.c:6633
struct sprite * get_unit_unhappy_sprite(const struct tileset *t, const struct unit *punit, int happy_cost)
Definition tilespec.c:6711
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:5568
int tileset_tile_width(const struct tileset *t)
Definition tilespec.c:716
int tileset_tilelabel_offset_y(const struct tileset *t)
Definition tilespec.c:882
#define mapview_layer_iterate(layer)
Definition tilespec.h:174
#define mapview_layer_iterate_end
Definition tilespec.h:182
@ SPACESHIP_STRUCTURAL
Definition tilespec.h:319
@ SPACESHIP_HABITATION
Definition tilespec.h:318
@ SPACESHIP_EXHAUST
Definition tilespec.h:322
@ SPACESHIP_FUEL
Definition tilespec.h:320
@ SPACESHIP_PROPULSION
Definition tilespec.h:321
@ SPACESHIP_SOLAR_PANEL
Definition tilespec.h:316
@ SPACESHIP_LIFE_SUPPORT
Definition tilespec.h:317
void timer_usleep_since_start(struct timer *t, long usec)
Definition timing.c:364
void timer_start(struct timer *t)
Definition timing.c:224
double timer_read_seconds(struct timer *t)
Definition timing.c:344
struct timer * timer_renew(struct timer *t, enum timer_timetype type, enum timer_use use)
Definition timing.c:176
@ TIMER_ACTIVE
Definition timing.h:45
@ TIMER_USER
Definition timing.h:41
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:328
struct unit * unit_virtual_create(struct player *pplayer, struct city *pcity, const struct unit_type *punittype, int veteran_level)
Definition unit.c:1617
void unit_virtual_destroy(struct unit *punit)
Definition unit.c:1713
bool unit_has_orders(const struct unit *punit)
Definition unit.c:204
#define unit_tile(_pu)
Definition unit.h:395
@ ORDER_MOVE
Definition unit.h:39
#define unit_owner(_pu)
Definition unit.h:394
#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