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 
52 struct tile_hash *mapdeco_highlight_table;
53 struct tile_hash *mapdeco_crosshair_table;
54 
55 struct gotoline_counter {
56   int line_count[DIR8_MAGIC_MAX];
57 };
58 
59 static inline struct gotoline_counter *gotoline_counter_new(void);
60 static 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 
72 struct gotoline_hash *mapdeco_gotoline_table;
73 
74 struct view mapview;
75 bool can_slide = TRUE;
76 
77 static bool frame_by_frame_animation = FALSE;
78 
79 struct tile *center_tile = NULL;
80 
81 static void base_canvas_to_map_pos(int *map_x, int *map_y,
82                                    float canvas_x, float canvas_y);
83 
84 enum update_type {
85   /* Masks */
86   UPDATE_NONE = 0,
87   UPDATE_CITY_DESCRIPTIONS  = 1,
88   UPDATE_MAP_CANVAS_VISIBLE = 2,
89   UPDATE_TILE_LABELS        = 4
90 };
91 
92 /* A tile update has a tile associated with it as well as an area type.
93  * See unqueue_mapview_updates for a thorough explanation. */
94 enum tile_update_type {
95   TILE_UPDATE_TILE_SINGLE,
96   TILE_UPDATE_TILE_FULL,
97   TILE_UPDATE_UNIT,
98   TILE_UPDATE_CITY_DESC,
99   TILE_UPDATE_CITYMAP,
100   TILE_UPDATE_TILE_LABEL,
101   TILE_UPDATE_COUNT
102 };
103 static void queue_mapview_update(enum update_type update);
104 static void queue_mapview_tile_update(struct tile *ptile,
105 				      enum tile_update_type type);
106 
107 /* Helper struct for drawing trade routes. */
108 struct trade_route_line {
109   float x, y, width, height;
110 };
111 
112 /* A trade route line might need to be drawn in two parts. */
113 static const int MAX_TRADE_ROUTE_DRAW_LINES = 2;
114 
115 static struct timer *anim_timer = NULL;
116 
117 enum animation_type { ANIM_MOVEMENT, ANIM_BATTLE, ANIM_EXPL, ANIM_NUKE };
118 
119 struct animation
120 {
121   enum animation_type type;
122   int id;
123   bool finished;
124   int old_x;
125   int old_y;
126   int width;
127   int height;
128   union {
129     struct {
130       struct unit *mover;
131       struct tile *src;
132       struct tile *dest;
133       float canvas_dx;
134       float canvas_dy;
135     } movement;
136     struct {
137       struct unit *virt_loser;
138       struct tile *loser_tile;
139       int loser_hp_start;
140       struct unit *virt_winner;
141       struct tile *winner_tile;
142       int winner_hp_start;
143       int winner_hp_end;
144       int steps;
145     } battle;
146     struct {
147       struct tile *tile;
148       const struct sprite_vector *sprites;
149       int sprite_count;
150     } expl;
151     struct {
152       bool shown;
153       struct tile *nuke_tile;
154     } nuke;
155   };
156 };
157 
158 #define SPECLIST_TAG animation
159 #define SPECLIST_TYPE struct animation
160 #include "speclist.h"
161 
162 struct animation_list *animations = NULL;
163 
164 /************************************************************************//**
165   Initialize animations system
166 ****************************************************************************/
animations_init(void)167 void animations_init(void)
168 {
169   animations = animation_list_new();
170 }
171 
172 /************************************************************************//**
173   Clean up animations system
174 ****************************************************************************/
animations_free(void)175 void animations_free(void)
176 {
177   if (animations != NULL) {
178     animation_list_destroy(animations);
179   }
180 }
181 
182 /************************************************************************//**
183   Add new animation to the queue
184 ****************************************************************************/
animation_add(struct animation * anim)185 static void animation_add(struct animation *anim)
186 {
187   if (animation_list_size(animations) == 0) {
188     anim_timer = timer_renew(anim_timer, TIMER_USER, TIMER_ACTIVE);
189     timer_start(anim_timer);
190   }
191 
192   anim->finished = FALSE;
193   anim->old_x = -1; /* Initial frame */
194   animation_list_append(animations, anim);
195 }
196 
197 /************************************************************************//**
198   Progress animation of type 'movement'
199 ****************************************************************************/
movement_animation(struct animation * anim,double time_gone)200 static bool movement_animation(struct animation *anim, double time_gone)
201 {
202   float start_x, start_y;
203   int new_x, new_y;
204   double timing_sec = (double)gui_options.smooth_move_unit_msec / 1000.0;
205   double mytime = MIN(time_gone, timing_sec);
206   struct unit *punit = anim->movement.mover;
207 
208   if (punit != NULL) {
209     tile_to_canvas_pos(&start_x, &start_y, anim->movement.src);
210     if (tileset_is_isometric(tileset) && tileset_hex_height(tileset) == 0) {
211       start_y -= tileset_tile_height(tileset) / 2 * map_zoom;
212       start_y -= (tileset_unit_height(tileset) - tileset_full_tile_height(tileset)) * map_zoom;
213     }
214     new_x = start_x + anim->movement.canvas_dx * (mytime / timing_sec);
215     new_y = start_y + anim->movement.canvas_dy * (mytime / timing_sec);
216 
217     if (anim->old_x >= 0) {
218       update_map_canvas(anim->old_x, anim->old_y,
219                         anim->width, anim->height);
220     }
221     put_unit(punit, mapview.store, map_zoom, new_x, new_y);
222     dirty_rect(new_x, new_y, anim->width, anim->height);
223     anim->old_x = new_x;
224     anim->old_y = new_y;
225 
226     if (time_gone >= timing_sec) {
227       /* Animation over */
228       if (--anim->movement.mover->refcount <= 0) {
229         FC_FREE(anim->movement.mover);
230       }
231       return TRUE;
232     }
233   } else {
234     return TRUE;
235   }
236 
237   return FALSE;
238 }
239 
240 /************************************************************************//**
241   Progress animation of type 'battle'
242 ****************************************************************************/
battle_animation(struct animation * anim,double time_gone)243 static bool battle_animation(struct animation *anim, double time_gone)
244 {
245   double time_per_step;
246   int step;
247   float canvas_x, canvas_y;
248   double timing_sec = (double)gui_options.smooth_combat_step_msec
249     * anim->battle.steps / 1000.0;
250 
251   if (time_gone >= timing_sec) {
252     /* Animation over */
253 
254     unit_virtual_destroy(anim->battle.virt_winner);
255     unit_virtual_destroy(anim->battle.virt_loser);
256 
257     return TRUE;
258   }
259 
260   time_per_step = timing_sec / anim->battle.steps;
261   step = time_gone / time_per_step;
262 
263   if (tile_to_canvas_pos(&canvas_x, &canvas_y, anim->battle.loser_tile)) {
264     anim->battle.virt_loser->hp
265       = anim->battle.loser_hp_start - (anim->battle.loser_hp_start
266                                        * step / anim->battle.steps);
267 
268     if (tileset_is_isometric(tileset) && tileset_hex_height(tileset) == 0) {
269       canvas_y -= tileset_tile_height(tileset) / 2 * map_zoom;
270       canvas_y -= (tileset_unit_height(tileset) - tileset_full_tile_height(tileset)) * map_zoom;
271     }
272 
273     put_unit(anim->battle.virt_loser, mapview.store, map_zoom,
274              canvas_x, canvas_y);
275     dirty_rect(canvas_x, canvas_y,
276                tileset_tile_width(tileset),
277                tileset_tile_height(tileset));
278   }
279 
280   if (tile_to_canvas_pos(&canvas_x, &canvas_y, anim->battle.winner_tile)) {
281     anim->battle.virt_winner->hp
282       = anim->battle.winner_hp_start - ((anim->battle.winner_hp_start
283                                          - anim->battle.winner_hp_end)
284                                         * step / anim->battle.steps);
285 
286     if (tileset_is_isometric(tileset) && tileset_hex_height(tileset) == 0) {
287       canvas_y -= tileset_tile_height(tileset) / 2 * map_zoom;
288       canvas_y -= (tileset_unit_height(tileset) - tileset_full_tile_height(tileset)) * map_zoom;
289     }
290     put_unit(anim->battle.virt_winner, mapview.store, map_zoom,
291              canvas_x, canvas_y);
292     dirty_rect(canvas_x, canvas_y,
293                tileset_tile_width(tileset),
294                tileset_tile_height(tileset));
295   }
296 
297   return FALSE;
298 }
299 
300 /************************************************************************//**
301   Progress animation of type 'explosion'
302 ****************************************************************************/
explosion_animation(struct animation * anim,double time_gone)303 static bool explosion_animation(struct animation *anim, double time_gone)
304 {
305   float canvas_x, canvas_y;
306   double timing_sec;
307 
308   if (anim->expl.sprite_count <= 0) {
309     return TRUE;
310   }
311 
312   timing_sec = (double)gui_options.smooth_combat_step_msec
313     * anim->expl.sprite_count / 1000.0;
314 
315   if (tile_to_canvas_pos(&canvas_x, &canvas_y, anim->expl.tile)) {
316     double time_per_frame = timing_sec / anim->expl.sprite_count;
317     int frame = time_gone / time_per_frame;
318     struct sprite *spr;
319     int w, h;
320 
321     frame = MIN(frame, anim->expl.sprite_count - 1);
322 
323     if (anim->old_x >= 0) {
324       update_map_canvas(anim->old_x, anim->old_y,
325                         anim->width, anim->height);
326     }
327 
328     spr = *sprite_vector_get(anim->expl.sprites, frame);
329     get_sprite_dimensions(spr, &w, &h);
330 
331     canvas_put_sprite_full(mapview.store,
332                            canvas_x + tileset_tile_width(tileset) / 2 * map_zoom
333                            - w / 2,
334                            canvas_y + tileset_tile_height(tileset) / 2 * map_zoom
335                            - h / 2,
336                            spr);
337     dirty_rect(canvas_x, canvas_y, tileset_tile_width(tileset) * map_zoom,
338                tileset_tile_height(tileset) * map_zoom);
339 
340     anim->old_x = canvas_x;
341     anim->old_y = canvas_y;
342   }
343 
344   if (time_gone >= timing_sec) {
345     /* Animation over */
346     return TRUE;
347   }
348 
349   return FALSE;
350 }
351 
352 /************************************************************************//**
353   Progress animation of type 'nuke'
354 ****************************************************************************/
nuke_animation(struct animation * anim,double time_gone)355 static bool nuke_animation(struct animation *anim, double time_gone)
356 {
357   if (!anim->nuke.shown) {
358     float canvas_x, canvas_y;
359     struct sprite *nuke_spr = get_nuke_explode_sprite(tileset);
360     int w, h;
361 
362     (void) tile_to_canvas_pos(&canvas_x, &canvas_y, anim->nuke.nuke_tile);
363     get_sprite_dimensions(nuke_spr, &w, &h);
364 
365     canvas_put_sprite_full(mapview.store,
366                            canvas_x + tileset_tile_width(tileset) / 2 * map_zoom
367                            - w / 2,
368                            canvas_y + tileset_tile_height(tileset) / 2 * map_zoom
369                            - h / 2,
370                            nuke_spr);
371     dirty_rect(canvas_x, canvas_y, tileset_tile_width(tileset) * map_zoom,
372                tileset_tile_height(tileset) * map_zoom);
373 
374     anim->old_x = canvas_x;
375     anim->old_y = canvas_y;
376 
377     anim->nuke.shown = TRUE;
378 
379     return FALSE;
380   }
381 
382   if (time_gone > 1.0) {
383     update_map_canvas_visible();
384 
385     return TRUE;
386   }
387 
388   return FALSE;
389 }
390 
391 /************************************************************************//**
392   Progress current animation
393 ****************************************************************************/
update_animation(void)394 void update_animation(void)
395 {
396   if (animation_list_size(animations) > 0) {
397     struct animation *anim = animation_list_get(animations, 0);
398 
399     if (anim->finished) {
400       /* Animation over */
401 
402       anim->id = -1;
403       if (anim->old_x >= 0) {
404         update_map_canvas(anim->old_x, anim->old_y, anim->width, anim->height);
405       }
406       animation_list_remove(animations, anim);
407       free(anim);
408 
409       if (animation_list_size(animations) > 0) {
410         /* Start next */
411         anim_timer = timer_renew(anim_timer, TIMER_USER, TIMER_ACTIVE);
412         timer_start(anim_timer);
413       }
414     } else {
415       double time_gone = timer_read_seconds(anim_timer);
416       bool finished = FALSE;
417 
418       switch (anim->type) {
419       case ANIM_MOVEMENT:
420         finished = movement_animation(anim, time_gone);
421         break;
422       case ANIM_BATTLE:
423         finished = battle_animation(anim, time_gone);
424         break;
425       case ANIM_EXPL:
426         finished = explosion_animation(anim, time_gone);
427         break;
428       case ANIM_NUKE:
429         finished = nuke_animation(anim, time_gone);
430       }
431 
432       if (finished) {
433         anim->finished = TRUE;
434       }
435     }
436   }
437 }
438 
439 /****************************************************************************
440   Create a new goto line counter.
441 ****************************************************************************/
gotoline_counter_new(void)442 static inline struct gotoline_counter *gotoline_counter_new(void)
443 {
444   struct gotoline_counter *pglc = fc_calloc(1, sizeof(*pglc));
445   return pglc;
446 }
447 
448 /****************************************************************************
449   Create a new goto line counter.
450 ****************************************************************************/
gotoline_counter_destroy(struct gotoline_counter * pglc)451 static void gotoline_counter_destroy(struct gotoline_counter *pglc)
452 {
453   fc_assert_ret(NULL != pglc);
454   free(pglc);
455 }
456 
457 /**************************************************************************
458  Refreshes a single tile on the map canvas.
459 **************************************************************************/
refresh_tile_mapcanvas(struct tile * ptile,bool full_refresh,bool write_to_screen)460 void refresh_tile_mapcanvas(struct tile *ptile,
461 			    bool full_refresh, bool write_to_screen)
462 {
463   if (full_refresh) {
464     queue_mapview_tile_update(ptile, TILE_UPDATE_TILE_FULL);
465   } else {
466     queue_mapview_tile_update(ptile, TILE_UPDATE_TILE_SINGLE);
467   }
468   if (write_to_screen) {
469     unqueue_mapview_updates(TRUE);
470   }
471 }
472 
473 /**************************************************************************
474  Refreshes a single unit on the map canvas.
475 **************************************************************************/
refresh_unit_mapcanvas(struct unit * punit,struct tile * ptile,bool full_refresh,bool write_to_screen)476 void refresh_unit_mapcanvas(struct unit *punit, struct tile *ptile,
477 			    bool full_refresh, bool write_to_screen)
478 {
479   if (full_refresh && gui_options.draw_native) {
480     queue_mapview_update(UPDATE_MAP_CANVAS_VISIBLE);
481   } else if (full_refresh && unit_drawn_with_city_outline(punit, TRUE)) {
482     queue_mapview_tile_update(ptile, TILE_UPDATE_CITYMAP);
483   } else {
484     queue_mapview_tile_update(ptile, TILE_UPDATE_UNIT);
485   }
486   if (write_to_screen) {
487     unqueue_mapview_updates(TRUE);
488   }
489 }
490 
491 /**************************************************************************
492   Refreshes a single city on the map canvas.
493 
494   If full_refresh is given then the citymap area and the city text will
495   also be refreshed.  Otherwise only the base city sprite is refreshed.
496 **************************************************************************/
refresh_city_mapcanvas(struct city * pcity,struct tile * ptile,bool full_refresh,bool write_to_screen)497 void refresh_city_mapcanvas(struct city *pcity, struct tile *ptile,
498 			    bool full_refresh, bool write_to_screen)
499 {
500   if (full_refresh && (gui_options.draw_map_grid || gui_options.draw_borders)) {
501     queue_mapview_tile_update(ptile, TILE_UPDATE_CITYMAP);
502   } else {
503     queue_mapview_tile_update(ptile, TILE_UPDATE_UNIT);
504   }
505   if (write_to_screen) {
506     unqueue_mapview_updates(TRUE);
507   }
508 }
509 
510 /****************************************************************************
511   Translate from a cartesian system to the GUI system.  This function works
512   on vectors, meaning it can be passed a (dx,dy) pair and will return the
513   change in GUI coordinates corresponding to this vector.  It is thus more
514   general than map_to_gui_pos.
515 
516   Note that a gui_to_map_vector function is not possible, since the
517   resulting map vector may differ based on the origin of the gui vector.
518 ****************************************************************************/
map_to_gui_vector(const struct tileset * t,float zoom,float * gui_dx,float * gui_dy,int map_dx,int map_dy)519 void map_to_gui_vector(const struct tileset *t, float zoom,
520 		       float *gui_dx, float *gui_dy, int map_dx, int map_dy)
521 {
522   if (tileset_is_isometric(t)) {
523     /*
524      * Convert the map coordinates to isometric GUI
525      * coordinates.  We'll make tile map(0,0) be the origin, and
526      * transform like this:
527      *
528      *                     3
529      * 123                2 6
530      * 456 -> becomes -> 1 5 9
531      * 789                4 8
532      *                     7
533      */
534     *gui_dx = (map_dx - map_dy) * tileset_tile_width(t) / 2 * zoom;
535     *gui_dy = (map_dx + map_dy) * tileset_tile_height(t) / 2 * zoom;
536   } else {
537     *gui_dx = map_dx * tileset_tile_height(t) * zoom;
538     *gui_dy = map_dy * tileset_tile_width(t) * zoom;
539   }
540 }
541 
542 /****************************************************************************
543   Translate from map to gui coordinate systems.
544 
545   GUI coordinates are comparable to canvas coordinates but extend in all
546   directions.  gui(0,0) == map(0,0).
547 ****************************************************************************/
map_to_gui_pos(const struct tileset * t,float * gui_x,float * gui_y,int map_x,int map_y)548 static void map_to_gui_pos(const struct tileset *t,
549 			   float *gui_x, float *gui_y, int map_x, int map_y)
550 {
551   /* Since the GUI origin is the same as the map origin we can just do a
552    * vector conversion. */
553   map_to_gui_vector(t, map_zoom, gui_x, gui_y, map_x, map_y);
554 }
555 
556 /****************************************************************************
557   Translate from gui to map coordinate systems.  See map_to_gui_pos().
558 
559   Note that you lose some information in this conversion.  If you convert
560   from a gui position to a map position and back, you will probably not get
561   the same value you started with.
562 ****************************************************************************/
gui_to_map_pos(const struct tileset * t,int * map_x,int * map_y,float gui_x,float gui_y)563 static void gui_to_map_pos(const struct tileset *t,
564                            int *map_x, int *map_y, float gui_x, float gui_y)
565 {
566   const float W = tileset_tile_width(t) * map_zoom, H = tileset_tile_height(t) * map_zoom;
567   const float HH = tileset_hex_height(t) * map_zoom, HW = tileset_hex_width(t) * map_zoom;
568 
569   if (HH > 0 || HW > 0) {
570     /* To handle hexagonal cases we have to revert to a less elegant method
571      * of calculation. */
572     float x, y;
573     int dx, dy;
574     int xmult, ymult, mod, compar;
575 
576     fc_assert(tileset_is_isometric(t));
577 
578     x = DIVIDE((int)gui_x, (int)W);
579     y = DIVIDE((int)gui_y, (int)H);
580     dx = gui_x - x * W;
581     dy = gui_y - y * H;
582     fc_assert(dx >= 0 && dx < W);
583     fc_assert(dy >= 0 && dy < H);
584 
585     /* Now fold so we consider only one-quarter tile. */
586     xmult = (dx >= W / 2) ? -1 : 1;
587     ymult = (dy >= H / 2) ? -1 : 1;
588     dx = (dx >= W / 2) ? (W - 1 - dx) : dx;
589     dy = (dy >= H / 2) ? (H - 1 - dy) : dy;
590 
591     /* Next compare to see if we're across onto the next tile. */
592     if (HW > 0) {
593       compar = (dx - HW / 2) * (H / 2) - (H / 2 - 1 - dy) * (W / 2 - HW);
594     } else {
595       compar = (dy - HH / 2) * (W / 2) - (W / 2 - 1 - dx) * (H / 2 - HH);
596     }
597     mod = (compar < 0) ? -1 : 0;
598 
599     *map_x = (x + y) + mod * (xmult + ymult) / 2;
600     *map_y = (y - x) + mod * (ymult - xmult) / 2;
601   } else if (tileset_is_isometric(t)) {
602     /* The basic operation here is a simple pi/4 rotation; however, we
603      * have to first scale because the tiles have different width and
604      * height.  Mathematically, this looks like
605      *   | 1/W  1/H | |x|    |x`|
606      *   |          | | | -> |  |
607      *   |-1/W  1/H | |y|    |y`|
608      *
609      * Where W is the tile width and H the height.
610      *
611      * In simple terms, this is
612      *   map_x = [   x / W + y / H ]
613      *   map_y = [ - x / W + y / H ]
614      * where [q] stands for integer part of q.
615      *
616      * Here the division is proper mathematical floating point division.
617      *
618      * A picture demonstrating this can be seen at
619      * http://bugs.freeciv.org/Ticket/Attachment/16782/9982/grid1.png.
620      *
621      * We have to subtract off a half-tile in the X direction before doing
622      * the transformation.  This is because, although the origin of the tile
623      * is the top-left corner of the bounding box, after the transformation
624      * the top corner of the diamond-shaped tile moves into this position.
625      *
626      * The calculation is complicated somewhat because of two things: we
627      * only use integer math, and C integer division rounds toward zero
628      * instead of rounding down.
629      *
630      * For another example of this math, see canvas_to_city_pos().
631      */
632     gui_x -= W / 2;
633     *map_x = DIVIDE((int)(gui_x * H + gui_y * W), (int)(W * H));
634     *map_y = DIVIDE((int)(gui_y * W - gui_x * H), (int)(W * H));
635   } else {			/* tileset_is_isometric(t) */
636     /* We use DIVIDE so that we will get the correct result even
637      * for negative coordinates. */
638     *map_x = DIVIDE((int)gui_x, (int)W);
639     *map_y = DIVIDE((int)gui_y, (int)H);
640   }
641 }
642 
643 /**************************************************************************
644   Finds the canvas coordinates for a map position. Beside setting the results
645   in canvas_x, canvas_y it returns whether the tile is inside the
646   visible mapview canvas.
647 
648   The result represents the upper left pixel (origin) of the bounding box of
649   the tile.  Note that in iso-view this origin is not a part of the tile
650   itself - so to make the operation reversible you would have to call
651   canvas_to_map_pos on the center of the tile, not the origin.
652 
653   The center of a tile is defined as:
654   {
655     tile_to_canvas_pos(&canvas_x, &canvas_y, ptile);
656     canvas_x += tileset_tile_width(tileset) * map_zoom / 2;
657     canvas_y += tileset_tile_height(tileset) * map_zoom / 2;
658   }
659 
660   This pixel is one position closer to the lower right, which may be
661   important to remember when doing some round-off operations. Other
662   parts of the code assume tileset_tile_width(tileset) and tileset_tile_height(tileset)
663   to be even numbers.
664 **************************************************************************/
tile_to_canvas_pos(float * canvas_x,float * canvas_y,struct tile * ptile)665 bool tile_to_canvas_pos(float *canvas_x, float *canvas_y, struct tile *ptile)
666 {
667   int center_map_x, center_map_y, dx, dy, tile_x, tile_y;
668 
669   /*
670    * First we wrap the coordinates to hopefully be within the the mapview
671    * window.  We do this by finding the position closest to the center
672    * of the window.
673    */
674   /* TODO: Cache the value of this position */
675   base_canvas_to_map_pos(&center_map_x, &center_map_y,
676 			 mapview.width / 2,
677 			 mapview.height / 2);
678   index_to_map_pos(&tile_x, &tile_y, tile_index(ptile));
679   base_map_distance_vector(&dx, &dy, center_map_x, center_map_y, tile_x,
680                            tile_y);
681 
682   map_to_gui_pos(tileset,
683 		 canvas_x, canvas_y, center_map_x + dx, center_map_y + dy);
684   *canvas_x -= mapview.gui_x0;
685   *canvas_y -= mapview.gui_y0;
686 
687   /*
688    * Finally we clip.
689    *
690    * This check is tailored to work for both iso-view and classic view.  Note
691    * that (canvas_x, canvas_y) need not be aligned to a tile boundary, and
692    * that the position is at the top-left of the NORMAL (not UNIT) tile.
693    * This checks to see if _any part_ of the tile is present on the backing
694    * store.  Even if it's not visible on the canvas, if it's present on the
695    * backing store we need to draw it in case the canvas is resized.
696    */
697   return (*canvas_x > -tileset_tile_width(tileset) * map_zoom
698 	  && *canvas_x < mapview.store_width
699 	  && *canvas_y > -tileset_tile_height(tileset) * map_zoom
700 	  && *canvas_y < (mapview.store_height
701 			  + (tileset_full_tile_height(tileset)
702                              - tileset_tile_height(tileset)) * map_zoom));
703 }
704 
705 /****************************************************************************
706   Finds the map coordinates corresponding to pixel coordinates.  The
707   resulting position is unwrapped and may be unreal.
708 ****************************************************************************/
base_canvas_to_map_pos(int * map_x,int * map_y,float canvas_x,float canvas_y)709 static void base_canvas_to_map_pos(int *map_x, int *map_y,
710 				   float canvas_x, float canvas_y)
711 {
712   gui_to_map_pos(tileset, map_x, map_y,
713                  canvas_x + mapview.gui_x0,
714                  canvas_y + mapview.gui_y0);
715 }
716 
717 /**************************************************************************
718   Finds the tile corresponding to pixel coordinates.  Returns that tile,
719   or NULL if the position is off the map.
720 **************************************************************************/
canvas_pos_to_tile(float canvas_x,float canvas_y)721 struct tile *canvas_pos_to_tile(float canvas_x, float canvas_y)
722 {
723   int map_x, map_y;
724 
725   base_canvas_to_map_pos(&map_x, &map_y, canvas_x, canvas_y);
726   if (normalize_map_pos(&map_x, &map_y)) {
727     return map_pos_to_tile(map_x, map_y);
728   } else {
729     return NULL;
730   }
731 }
732 
733 /**************************************************************************
734   Finds the tile corresponding to pixel coordinates.  Returns that tile,
735   or the one nearest is the position is off the map.  Will never return NULL.
736 **************************************************************************/
canvas_pos_to_nearest_tile(float canvas_x,float canvas_y)737 struct tile *canvas_pos_to_nearest_tile(float canvas_x, float canvas_y)
738 {
739   int map_x, map_y;
740 
741   base_canvas_to_map_pos(&map_x, &map_y, canvas_x, canvas_y);
742   return nearest_real_tile(map_x, map_y);
743 }
744 
745 /****************************************************************************
746   Normalize (wrap) the GUI position.  This is equivalent to a map wrapping,
747   but in GUI coordinates so that pixel accuracy is preserved.
748 ****************************************************************************/
normalize_gui_pos(const struct tileset * t,float * gui_x,float * gui_y)749 static void normalize_gui_pos(const struct tileset *t,
750 			      float *gui_x, float *gui_y)
751 {
752   int map_x, map_y, nat_x, nat_y, diff_x, diff_y;
753   float gui_x0, gui_y0;
754 
755   /* Convert the (gui_x, gui_y) into a (map_x, map_y) plus a GUI offset
756    * from this tile. */
757   gui_to_map_pos(t, &map_x, &map_y, *gui_x, *gui_y);
758   map_to_gui_pos(t, &gui_x0, &gui_y0, map_x, map_y);
759   diff_x = *gui_x - gui_x0;
760   diff_y = *gui_y - gui_y0;
761 
762   /* Perform wrapping without any realness check.  It's important that
763    * we wrap even if the map position is unreal, which normalize_map_pos
764    * doesn't necessarily do. */
765   MAP_TO_NATIVE_POS(&nat_x, &nat_y, map_x, map_y);
766   if (current_topo_has_flag(TF_WRAPX)) {
767     nat_x = FC_WRAP(nat_x, game.map.xsize);
768   }
769   if (current_topo_has_flag(TF_WRAPY)) {
770     nat_y = FC_WRAP(nat_y, game.map.ysize);
771   }
772   NATIVE_TO_MAP_POS(&map_x, &map_y, nat_x, nat_y);
773 
774   /* Now convert the wrapped map position back to a GUI position and add the
775    * offset back on. */
776   map_to_gui_pos(t, gui_x, gui_y, map_x, map_y);
777   *gui_x += diff_x;
778   *gui_y += diff_y;
779 }
780 
781 /****************************************************************************
782   Find the vector with minimum "real" distance between two GUI positions.
783   This corresponds to map_to_distance_vector but works for GUI coordinates.
784 ****************************************************************************/
gui_distance_vector(const struct tileset * t,float * gui_dx,float * gui_dy,float gui_x0,float gui_y0,float gui_x1,float gui_y1)785 static void gui_distance_vector(const struct tileset *t,
786 				float *gui_dx, float *gui_dy,
787 				float gui_x0, float gui_y0,
788 				float gui_x1, float gui_y1)
789 {
790   int map_x0, map_y0, map_x1, map_y1;
791   float gui_x0_base, gui_y0_base, gui_x1_base, gui_y1_base;
792   int gui_x0_diff, gui_y0_diff, gui_x1_diff, gui_y1_diff;
793   int map_dx, map_dy;
794 
795   /* Make sure positions are canonical.  Yes, this is the only way. */
796   normalize_gui_pos(t, &gui_x0, &gui_y0);
797   normalize_gui_pos(t, &gui_x1, &gui_y1);
798 
799   /* Now we have to find the offset of each GUI position from its tile
800    * origin.  This is complicated: it means converting to a map position and
801    * then back to the GUI position to find the tile origin, then subtracting
802    * to get the offset. */
803   gui_to_map_pos(t, &map_x0, &map_y0, gui_x0, gui_y0);
804   gui_to_map_pos(t, &map_x1, &map_y1, gui_x1, gui_y1);
805 
806   map_to_gui_pos(t, &gui_x0_base, &gui_y0_base, map_x0, map_y0);
807   map_to_gui_pos(t, &gui_x1_base, &gui_y1_base, map_x1, map_y1);
808 
809   gui_x0_diff = gui_x0 - gui_x0_base;
810   gui_y0_diff = gui_y0 - gui_y0_base;
811   gui_x1_diff = gui_x1 - gui_x1_base;
812   gui_y1_diff = gui_y1 - gui_y1_base;
813 
814   /* Next we find the map distance vector and convert this into a GUI
815    * vector. */
816   base_map_distance_vector(&map_dx, &map_dy, map_x0, map_y0, map_x1, map_y1);
817   map_to_gui_pos(t, gui_dx, gui_dy, map_dx, map_dy);
818 
819   /* Finally we add on the difference in offsets to retain pixel
820    * resolution. */
821   *gui_dx += gui_x1_diff - gui_x0_diff;
822   *gui_dy += gui_y1_diff - gui_y0_diff;
823 }
824 
825 /****************************************************************************
826   Move the GUI origin to the given normalized, clipped origin.  This may
827   be called many times when sliding the mapview.
828 ****************************************************************************/
base_set_mapview_origin(float gui_x0,float gui_y0)829 static void base_set_mapview_origin(float gui_x0, float gui_y0)
830 {
831   float old_gui_x0, old_gui_y0;
832   float dx, dy;
833   const int width = mapview.width, height = mapview.height;
834   int common_x0, common_x1, common_y0, common_y1;
835   int update_x0, update_x1, update_y0, update_y1;
836 
837   /* Then update everything.  This does some tricky math to avoid having
838    * to do unnecessary redraws in update_map_canvas.  This makes for ugly
839    * code but speeds up the mapview by a large factor. */
840 
841   /* We need to calculate the vector of movement of the mapview.  So
842    * we find the GUI distance vector and then use this to calculate
843    * the original mapview origin relative to the current position.  Thus
844    * if we move one tile to the left, even if this causes GUI positions
845    * to wrap the distance vector is only one tile. */
846   normalize_gui_pos(tileset, &gui_x0, &gui_y0);
847   gui_distance_vector(tileset, &dx, &dy,
848 		      mapview.gui_x0, mapview.gui_y0,
849 		      gui_x0, gui_y0);
850   old_gui_x0 = gui_x0 - dx;
851   old_gui_y0 = gui_y0 - dy;
852 
853   mapview.gui_x0 = gui_x0;
854   mapview.gui_y0 = gui_y0;
855 
856   /* Find the overlapping area of the new and old mapview.  This is
857    * done in GUI coordinates.  Note that if the GUI coordinates wrap
858    * no overlap will be found. */
859   common_x0 = MAX(old_gui_x0, gui_x0);
860   common_x1 = MIN(old_gui_x0, gui_x0) + width;
861   common_y0 = MAX(old_gui_y0, gui_y0);
862   common_y1 = MIN(old_gui_y0, gui_y0) + height;
863 
864   if (mapview.can_do_cached_drawing && !zoom_is_enabled()
865       && common_x1 > common_x0 && common_y1 > common_y0) {
866     /* Do a partial redraw only.  This means the area of overlap (a
867      * rectangle) is copied.  Then the remaining areas (two rectangles)
868      * are updated through update_map_canvas. */
869     struct canvas *target = mapview.tmp_store;
870 
871     if (old_gui_x0 < gui_x0) {
872       update_x0 = MAX(old_gui_x0 + width, gui_x0);
873       update_x1 = gui_x0 + width;
874     } else {
875       update_x0 = gui_x0;
876       update_x1 = MIN(old_gui_x0, gui_x0 + width);
877     }
878     if (old_gui_y0 < gui_y0) {
879       update_y0 = MAX(old_gui_y0 + height, gui_y0);
880       update_y1 = gui_y0 + height;
881     } else {
882       update_y0 = gui_y0;
883       update_y1 = MIN(old_gui_y0, gui_y0 + height);
884     }
885 
886     dirty_all();
887     canvas_copy(target, mapview.store,
888 		common_x0 - old_gui_x0,
889 		common_y0 - old_gui_y0,
890 		common_x0 - gui_x0, common_y0 - gui_y0,
891 		common_x1 - common_x0, common_y1 - common_y0);
892     mapview.tmp_store = mapview.store;
893     mapview.store = target;
894 
895     if (update_y1 > update_y0) {
896       update_map_canvas(0, update_y0 - gui_y0,
897 			width, update_y1 - update_y0);
898     }
899     if (update_x1 > update_x0) {
900       update_map_canvas(update_x0 - gui_x0, common_y0 - gui_y0,
901 			update_x1 - update_x0, common_y1 - common_y0);
902     }
903   } else {
904     dirty_all();
905     update_map_canvas(0, 0, mapview.store_width, mapview.store_height);
906   }
907 
908   center_tile_overviewcanvas();
909   switch (hover_state) {
910   case HOVER_GOTO:
911   case HOVER_PATROL:
912   case HOVER_CONNECT:
913   case HOVER_NUKE:
914     create_line_at_mouse_pos();
915   case HOVER_NONE:
916   case HOVER_PARADROP:
917   case HOVER_ACT_SEL_TGT:
918     break;
919   };
920   if (rectangle_active) {
921     update_rect_at_mouse_pos();
922   }
923 }
924 
925 /****************************************************************************
926   Adjust mapview origin values. Returns TRUE iff values are different from
927   current mapview.
928 ****************************************************************************/
calc_mapview_origin(float * gui_x0,float * gui_y0)929 static bool calc_mapview_origin(float *gui_x0, float *gui_y0)
930 {
931   float xmin, ymin, xmax, ymax;
932   int xsize, ysize;
933 
934   /* Normalize (wrap) the mapview origin. */
935   normalize_gui_pos(tileset, gui_x0, gui_y0);
936 
937   /* First wrap/clip the position.  Wrapping is done in native positions
938    * while clipping is done in scroll (native) positions. */
939   get_mapview_scroll_window(&xmin, &ymin, &xmax, &ymax, &xsize, &ysize);
940 
941   if (!current_topo_has_flag(TF_WRAPX)) {
942     *gui_x0 = CLIP(xmin, *gui_x0, xmax - xsize);
943   }
944 
945   if (!current_topo_has_flag(TF_WRAPY)) {
946     *gui_y0 = CLIP(ymin, *gui_y0, ymax - ysize);
947   }
948 
949   if (mapview.gui_x0 == *gui_x0 && mapview.gui_y0 == *gui_y0) {
950     return FALSE;
951   }
952 
953   return TRUE;
954 }
955 
956 /****************************************************************************
957   Change the mapview origin, clip it, and update everything.
958 ****************************************************************************/
set_mapview_origin(float gui_x0,float gui_y0)959 void set_mapview_origin(float gui_x0, float gui_y0)
960 {
961   if (!calc_mapview_origin(&gui_x0, &gui_y0)) {
962     return;
963   }
964 
965   if (can_slide && gui_options.smooth_center_slide_msec > 0) {
966     if (frame_by_frame_animation) {
967       /* TODO: Implement animation */
968       base_set_mapview_origin(gui_x0, gui_y0);
969     } else {
970       int start_x = mapview.gui_x0, start_y = mapview.gui_y0;
971       float diff_x, diff_y;
972       double timing_sec = (double)gui_options.smooth_center_slide_msec / 1000.0;
973       double currtime;
974       int frames = 0;
975 
976       /* We track the average FPS, which is used to predict how long the
977        * next draw will take.  We start with a 100 FPS estimate - this
978        * value will quickly become irrelevant as the correct value is
979        * calculated, but it's needed to give an estimate of the FPS for
980        * the first draw.
981        *
982        * Note that the initial value shouldn't be larger than the sliding
983        * time, or we'll jump straight to the last frame.  The FPS should
984        * therefore be a "high" estimate. */
985       static double total_frames = 0.01;
986       static double total_time = 0.0001;
987 
988       gui_distance_vector(tileset,
989                           &diff_x, &diff_y, start_x, start_y, gui_x0, gui_y0);
990       anim_timer = timer_renew(anim_timer, TIMER_USER, TIMER_ACTIVE);
991       timer_start(anim_timer);
992 
993       unqueue_mapview_updates(TRUE);
994 
995       do {
996         double mytime;
997 
998         /* Get the current time, and add on the average 1/FPS, which is the
999          * expected time this frame will take.  This is done so that the
1000          * frame's position is calculated from the expected time when the
1001          * frame will complete, rather than the time when the frame drawing
1002          * is started. */
1003         currtime = timer_read_seconds(anim_timer);
1004         currtime += total_time / total_frames;
1005 
1006         mytime = MIN(currtime, timing_sec);
1007         base_set_mapview_origin(start_x + diff_x * (mytime / timing_sec),
1008                                 start_y + diff_y * (mytime / timing_sec));
1009         flush_dirty();
1010         gui_flush();
1011         frames++;
1012       } while (currtime < timing_sec);
1013 
1014       currtime = timer_read_seconds(anim_timer);
1015       total_frames += frames;
1016       total_time += currtime;
1017       log_debug("Got %d frames in %f seconds: %f FPS (avg %f).",
1018                 frames, currtime, (double)frames / currtime,
1019                 total_frames / total_time);
1020 
1021       /* A very small decay factor to make things more accurate when something
1022        * changes (mapview size, tileset change, etc.).  This gives a
1023        * half-life of 68 slides. */
1024       total_frames *= 0.99;
1025       total_time *= 0.99;
1026     }
1027   } else {
1028     base_set_mapview_origin(gui_x0, gui_y0);
1029   }
1030 
1031   update_map_canvas_scrollbars();
1032 }
1033 
1034 /****************************************************************************
1035   Return the scroll dimensions of the clipping window for the mapview window..
1036 
1037   Imagine the entire map in scroll coordinates.  It is a rectangle.  Now
1038   imagine the mapview "window" sliding around through this rectangle.  How
1039   far can it slide?  In most cases it has to be able to slide past the
1040   ends of the map rectangle so that it's capable of reaching the whole
1041   area.
1042 
1043   This function gives constraints on how far the window is allowed to
1044   slide.  xmin and ymin are the minimum values for the window origin.
1045   xsize and ysize give the scroll dimensions of the mapview window.
1046   xmax and ymax give the maximum values that the bottom/left ends of the
1047   window may reach.  The constraints, therefore, are that:
1048 
1049     get_mapview_scroll_pos(&scroll_x, &scroll_y);
1050     xmin <= scroll_x < xmax - xsize
1051     ymin <= scroll_y < ymax - ysize
1052 
1053   This function should be used anywhere and everywhere that scrolling is
1054   constrained.
1055 
1056   Note that scroll coordinates, not map coordinates, are used.  Currently
1057   these correspond to native coordinates.
1058 ****************************************************************************/
get_mapview_scroll_window(float * xmin,float * ymin,float * xmax,float * ymax,int * xsize,int * ysize)1059 void get_mapview_scroll_window(float *xmin, float *ymin,
1060                                float *xmax, float *ymax,
1061 			       int *xsize, int *ysize)
1062 {
1063   int diff;
1064 
1065   *xsize = mapview.width;
1066   *ysize = mapview.height;
1067 
1068   if (MAP_IS_ISOMETRIC == tileset_is_isometric(tileset)) {
1069     /* If the map and view line up, it's easy. */
1070     NATIVE_TO_MAP_POS(xmin, ymin, 0, 0);
1071     map_to_gui_pos(tileset, xmin, ymin, *xmin, *ymin);
1072 
1073     NATIVE_TO_MAP_POS(xmax, ymax, game.map.xsize - 1, game.map.ysize - 1);
1074     map_to_gui_pos(tileset, xmax, ymax, *xmax, *ymax);
1075     *xmax += tileset_tile_width(tileset) * map_zoom;
1076     *ymax += tileset_tile_height(tileset) * map_zoom;
1077 
1078     /* To be able to center on positions near the edges, we have to be
1079      * allowed to scroll all the way to those edges.  To allow wrapping the
1080      * clipping boundary needs to extend past the edge - a half-tile in
1081      * iso-view or a full tile in non-iso view.  The above math already has
1082      * taken care of some of this so all that's left is to fix the corner
1083      * cases. */
1084     if (current_topo_has_flag(TF_WRAPX)) {
1085       *xmax += *xsize;
1086 
1087       /* We need to be able to scroll a little further to the left. */
1088       *xmin -= tileset_tile_width(tileset) * map_zoom;
1089     }
1090     if (current_topo_has_flag(TF_WRAPY)) {
1091       *ymax += *ysize;
1092 
1093       /* We need to be able to scroll a little further up. */
1094       *ymin -= tileset_tile_height(tileset) * map_zoom;
1095     }
1096   } else {
1097     /* Otherwise it's hard.  Very hard.  Impossible, in fact.  This is just
1098      * an approximation - a huge bounding box. */
1099     float gui_x1, gui_y1, gui_x2, gui_y2, gui_x3, gui_y3, gui_x4, gui_y4;
1100     int map_x, map_y;
1101 
1102     NATIVE_TO_MAP_POS(&map_x, &map_y, 0, 0);
1103     map_to_gui_pos(tileset, &gui_x1, &gui_y1, map_x, map_y);
1104 
1105     NATIVE_TO_MAP_POS(&map_x, &map_y, game.map.xsize - 1, 0);
1106     map_to_gui_pos(tileset, &gui_x2, &gui_y2, map_x, map_y);
1107 
1108     NATIVE_TO_MAP_POS(&map_x, &map_y, 0, game.map.ysize - 1);
1109     map_to_gui_pos(tileset, &gui_x3, &gui_y3, map_x, map_y);
1110 
1111     NATIVE_TO_MAP_POS(&map_x, &map_y, game.map.xsize - 1, game.map.ysize - 1);
1112     map_to_gui_pos(tileset, &gui_x4, &gui_y4, map_x, map_y);
1113 
1114     *xmin = MIN(gui_x1, MIN(gui_x2, gui_x3)) - mapview.width / 2;
1115     *ymin = MIN(gui_y1, MIN(gui_y2, gui_y3)) - mapview.height / 2;
1116 
1117     *xmax = MAX(gui_x4, MAX(gui_x2, gui_x3)) + mapview.width / 2;
1118     *ymax = MAX(gui_y4, MAX(gui_y2, gui_y3)) + mapview.height / 2;
1119   }
1120 
1121   /* Make sure the scroll window is big enough to hold the mapview.  If
1122    * not scrolling will be very ugly and the GUI may become confused. */
1123   diff = *xsize - (*xmax - *xmin);
1124   if (diff > 0) {
1125     *xmin -= diff / 2;
1126     *xmax += (diff + 1) / 2;
1127   }
1128 
1129   diff = *ysize - (*ymax - *ymin);
1130   if (diff > 0) {
1131     *ymin -= diff / 2;
1132     *ymax += (diff + 1) / 2;
1133   }
1134 
1135   log_debug("x: %f<-%d->%f; y: %f<-%f->%d",
1136             *xmin, *xsize, *xmax, *ymin, *ymax, *ysize);
1137 }
1138 
1139 /****************************************************************************
1140   Find the scroll step for the mapview.  This is the amount to scroll (in
1141   scroll coordinates) on each "step".  See also get_mapview_scroll_window.
1142 ****************************************************************************/
get_mapview_scroll_step(int * xstep,int * ystep)1143 void get_mapview_scroll_step(int *xstep, int *ystep)
1144 {
1145   *xstep = tileset_tile_width(tileset) * map_zoom;
1146   *ystep = tileset_tile_height(tileset) * map_zoom;
1147 
1148   if (tileset_is_isometric(tileset)) {
1149     *xstep /= 2;
1150     *ystep /= 2;
1151   }
1152 }
1153 
1154 /****************************************************************************
1155   Find the current scroll position (origin) of the mapview.
1156 ****************************************************************************/
get_mapview_scroll_pos(int * scroll_x,int * scroll_y)1157 void get_mapview_scroll_pos(int *scroll_x, int *scroll_y)
1158 {
1159   *scroll_x = mapview.gui_x0;
1160   *scroll_y = mapview.gui_y0;
1161 }
1162 
1163 /****************************************************************************
1164   Set the scroll position (origin) of the mapview, and update the GUI.
1165 ****************************************************************************/
set_mapview_scroll_pos(int scroll_x,int scroll_y)1166 void set_mapview_scroll_pos(int scroll_x, int scroll_y)
1167 {
1168   int gui_x0 = scroll_x, gui_y0 = scroll_y;
1169 
1170   can_slide = FALSE;
1171   set_mapview_origin(gui_x0, gui_y0);
1172   can_slide = TRUE;
1173 }
1174 
1175 /**************************************************************************
1176   Finds the current center tile of the mapcanvas.
1177 **************************************************************************/
get_center_tile_mapcanvas(void)1178 struct tile *get_center_tile_mapcanvas(void)
1179 {
1180   return canvas_pos_to_nearest_tile(mapview.width / 2,
1181                                     mapview.height / 2);
1182 }
1183 
1184 /**************************************************************************
1185   Centers the mapview around (map_x, map_y).
1186 **************************************************************************/
center_tile_mapcanvas(struct tile * ptile)1187 void center_tile_mapcanvas(struct tile *ptile)
1188 {
1189   float gui_x, gui_y;
1190   int tile_x, tile_y;
1191   static bool first = TRUE;
1192 
1193   if (first && can_slide) {
1194     return;
1195   }
1196   first = FALSE;
1197 
1198   index_to_map_pos(&tile_x, &tile_y, tile_index(ptile));
1199   map_to_gui_pos(tileset, &gui_x, &gui_y, tile_x, tile_y);
1200 
1201   /* Put the center pixel of the tile at the exact center of the mapview. */
1202   gui_x -= (mapview.width - tileset_tile_width(tileset) * map_zoom) / 2;
1203   gui_y -= (mapview.height - tileset_tile_height(tileset) * map_zoom) / 2;
1204 
1205   set_mapview_origin(gui_x, gui_y);
1206 
1207   center_tile = ptile;
1208 }
1209 
1210 /**************************************************************************
1211   Return TRUE iff the given map position has a tile visible on the
1212   map canvas.
1213 **************************************************************************/
tile_visible_mapcanvas(struct tile * ptile)1214 bool tile_visible_mapcanvas(struct tile *ptile)
1215 {
1216   float dummy_x, dummy_y; /* well, it needs two pointers... */
1217 
1218   return tile_to_canvas_pos(&dummy_x, &dummy_y, ptile);
1219 }
1220 
1221 /**************************************************************************
1222   Return TRUE iff the given map position has a tile visible within the
1223   interior of the map canvas. This information is used to determine
1224   when we need to recenter the map canvas.
1225 
1226   The logic of this function is simple: if a tile is within 1.5 tiles
1227   of a border of the canvas and that border is not aligned with the
1228   edge of the map, then the tile is on the "border" of the map canvas.
1229 
1230   This function is only correct for the current topology.
1231 **************************************************************************/
tile_visible_and_not_on_border_mapcanvas(struct tile * ptile)1232 bool tile_visible_and_not_on_border_mapcanvas(struct tile *ptile)
1233 {
1234   float canvas_x, canvas_y;
1235   float xmin, ymin, xmax, ymax;
1236   int xsize, ysize, scroll_x, scroll_y;
1237   const int border_x = (tileset_is_isometric(tileset) ? tileset_tile_width(tileset) / 2 * map_zoom
1238 			: 2 * tileset_tile_width(tileset) * map_zoom);
1239   const int border_y = (tileset_is_isometric(tileset) ? tileset_tile_height(tileset) / 2 * map_zoom
1240 			: 2 * tileset_tile_height(tileset) * map_zoom);
1241   bool same = (tileset_is_isometric(tileset) == MAP_IS_ISOMETRIC);
1242 
1243   get_mapview_scroll_window(&xmin, &ymin, &xmax, &ymax, &xsize, &ysize);
1244   get_mapview_scroll_pos(&scroll_x, &scroll_y);
1245 
1246   if (!tile_to_canvas_pos(&canvas_x, &canvas_y, ptile)) {
1247     /* The tile isn't visible at all. */
1248     return FALSE;
1249   }
1250 
1251   /* For each direction: if the tile is too close to the mapview border
1252    * in that direction, and scrolling can get us any closer to the
1253    * border, then it's a border tile.  We can only really check the
1254    * scrolling when the mapview window lines up with the map. */
1255   if (canvas_x < border_x
1256       && (!same || scroll_x > xmin || current_topo_has_flag(TF_WRAPX))) {
1257     return FALSE;
1258   }
1259   if (canvas_y < border_y
1260       && (!same || scroll_y > ymin || current_topo_has_flag(TF_WRAPY))) {
1261     return FALSE;
1262   }
1263   if (canvas_x + tileset_tile_width(tileset) * map_zoom > mapview.width - border_x
1264       && (!same || scroll_x + xsize < xmax || current_topo_has_flag(TF_WRAPX))) {
1265     return FALSE;
1266   }
1267   if (canvas_y + tileset_tile_height(tileset) * map_zoom > mapview.height - border_y
1268       && (!same || scroll_y + ysize < ymax || current_topo_has_flag(TF_WRAPY))) {
1269     return FALSE;
1270   }
1271 
1272   return TRUE;
1273 }
1274 
1275 /**************************************************************************
1276   Draw an array of drawn sprites onto the canvas.
1277 **************************************************************************/
put_drawn_sprites(struct canvas * pcanvas,float zoom,int canvas_x,int canvas_y,int count,struct drawn_sprite * pdrawn,bool fog)1278 void put_drawn_sprites(struct canvas *pcanvas, float zoom,
1279                        int canvas_x, int canvas_y,
1280                        int count, struct drawn_sprite *pdrawn,
1281                        bool fog)
1282 {
1283   int i;
1284 
1285   for (i = 0; i < count; i++) {
1286     if (!pdrawn[i].sprite) {
1287       /* This can happen, although it should probably be avoided. */
1288       continue;
1289     }
1290 
1291     if (fog && pdrawn[i].foggable) {
1292       canvas_put_sprite_fogged(pcanvas,
1293                                canvas_x / zoom + pdrawn[i].offset_x,
1294                                canvas_y / zoom + pdrawn[i].offset_y,
1295 			       pdrawn[i].sprite,
1296 			       TRUE,
1297 			       canvas_x, canvas_y);
1298     } else {
1299       /* We avoid calling canvas_put_sprite_fogged, even though it
1300        * should be a valid thing to do, because gui-gtk-2.0 doesn't have
1301        * a full implementation. */
1302       canvas_put_sprite_full(pcanvas,
1303                              canvas_x / zoom + pdrawn[i].offset_x,
1304                              canvas_y / zoom + pdrawn[i].offset_y,
1305 			     pdrawn[i].sprite);
1306     }
1307   }
1308 }
1309 
1310 /**************************************************************************
1311   Draw one layer of a tile, edge, corner, unit, and/or city onto the
1312   canvas at the given position.
1313 **************************************************************************/
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)1314 void put_one_element(struct canvas *pcanvas, float zoom,
1315                      enum mapview_layer layer,
1316                      const struct tile *ptile,
1317                      const struct tile_edge *pedge,
1318                      const struct tile_corner *pcorner,
1319                      const struct unit *punit, const struct city *pcity,
1320                      int canvas_x, int canvas_y,
1321                      const struct city *citymode,
1322                      const struct unit_type *putype)
1323 {
1324   struct drawn_sprite tile_sprs[80];
1325   int count = fill_sprite_array(tileset, tile_sprs, layer,
1326                                 ptile, pedge, pcorner,
1327                                 punit, pcity, citymode, putype);
1328   bool fog = (ptile && gui_options.draw_fog_of_war
1329 	      && TILE_KNOWN_UNSEEN == client_tile_get_known(ptile));
1330 
1331   /*** Draw terrain and specials ***/
1332   put_drawn_sprites(pcanvas, zoom, canvas_x, canvas_y, count, tile_sprs, fog);
1333 }
1334 
1335 /*****************************************************************************
1336   Draw the given unit onto the canvas store at the given location. The area
1337   of drawing is tileset_unit_height(tileset) x tileset_unit_width(tileset).
1338 *****************************************************************************/
put_unit(const struct unit * punit,struct canvas * pcanvas,float zoom,int canvas_x,int canvas_y)1339 void put_unit(const struct unit *punit, struct canvas *pcanvas, float zoom,
1340               int canvas_x, int canvas_y)
1341 {
1342   canvas_y += (tileset_unit_height(tileset) - tileset_tile_height(tileset)) * zoom;
1343   mapview_layer_iterate(layer) {
1344     put_one_element(pcanvas, zoom, layer, NULL, NULL, NULL,
1345                     punit, NULL, canvas_x, canvas_y, NULL, NULL);
1346   } mapview_layer_iterate_end;
1347 }
1348 
1349 /*****************************************************************************
1350   Draw the given unit onto the canvas store at the given location. The area
1351   of drawing is tileset_unit_height(tileset) x tileset_unit_width(tileset).
1352 *****************************************************************************/
put_unittype(const struct unit_type * putype,struct canvas * pcanvas,float zoom,int canvas_x,int canvas_y)1353 void put_unittype(const struct unit_type *putype, struct canvas *pcanvas, float zoom,
1354                   int canvas_x, int canvas_y)
1355 {
1356   canvas_y += (tileset_unit_height(tileset) - tileset_tile_height(tileset)) * zoom;
1357   mapview_layer_iterate(layer) {
1358     put_one_element(pcanvas, zoom, layer, NULL, NULL, NULL,
1359                     NULL, NULL, canvas_x, canvas_y, NULL, putype);
1360   } mapview_layer_iterate_end;
1361 }
1362 
1363 /**************************************************************************
1364   Draw the given city onto the canvas store at the given location.  The
1365   area of drawing is
1366   tileset_full_tile_height(tileset) x tileset_full_tile_width(tileset).
1367 **************************************************************************/
put_city(struct city * pcity,struct canvas * pcanvas,float zoom,int canvas_x,int canvas_y)1368 void put_city(struct city *pcity, struct canvas *pcanvas, float zoom,
1369               int canvas_x, int canvas_y)
1370 {
1371   canvas_y += (tileset_full_tile_height(tileset) - tileset_tile_height(tileset)) * zoom;
1372   mapview_layer_iterate(layer) {
1373     put_one_element(pcanvas, zoom, layer,
1374 		    NULL, NULL, NULL, NULL, pcity,
1375 		    canvas_x, canvas_y, NULL, NULL);
1376   } mapview_layer_iterate_end;
1377 }
1378 
1379 /**************************************************************************
1380   Draw the given tile terrain onto the canvas store at the given location.
1381   The area of drawing is
1382   tileset_full_tile_height(tileset) x tileset_full_tile_width(tileset)
1383   (even though most tiles are not this tall).
1384 **************************************************************************/
put_terrain(struct tile * ptile,struct canvas * pcanvas,float zoom,int canvas_x,int canvas_y)1385 void put_terrain(struct tile *ptile, struct canvas *pcanvas, float zoom,
1386                  int canvas_x, int canvas_y)
1387 {
1388   /* Use full tile height, even for terrains. */
1389   canvas_y += (tileset_full_tile_height(tileset) - tileset_tile_height(tileset)) * zoom;
1390   mapview_layer_iterate(layer) {
1391     put_one_element(pcanvas, zoom, layer, ptile, NULL, NULL, NULL, NULL,
1392 		    canvas_x, canvas_y, NULL, NULL);
1393   } mapview_layer_iterate_end;
1394 }
1395 
1396 /****************************************************************************
1397   Draw food, gold, and shield upkeep values on the unit.
1398 
1399   The proper way to do this is probably something like what Civ II does
1400   (one sprite drawn N times on top of itself), but we just use separate
1401   sprites (limiting the number of combinations).
1402 ****************************************************************************/
put_unit_city_overlays(struct unit * punit,struct canvas * pcanvas,int canvas_x,int canvas_y,int * upkeep_cost,int happy_cost)1403 void put_unit_city_overlays(struct unit *punit,
1404                             struct canvas *pcanvas,
1405                             int canvas_x, int canvas_y, int *upkeep_cost,
1406                             int happy_cost)
1407 {
1408   struct sprite *sprite;
1409 
1410   sprite = get_unit_unhappy_sprite(tileset, punit, happy_cost);
1411   if (sprite) {
1412     canvas_put_sprite_full(pcanvas, canvas_x, canvas_y, sprite);
1413   }
1414 
1415   output_type_iterate(o) {
1416     sprite = get_unit_upkeep_sprite(tileset, o, punit, upkeep_cost);
1417     if (sprite) {
1418       canvas_put_sprite_full(pcanvas, canvas_x, canvas_y, sprite);
1419     }
1420   } output_type_iterate_end;
1421 }
1422 
1423 /*
1424  * pcity->client.color_index is an index into the city_colors array.
1425  * When toggle_city_color is called the city's coloration is toggled.  When
1426  * a city is newly colored its color is taken from color_index and
1427  * color_index is moved forward one position. Each color in the array
1428  * tells what color the citymap will be drawn on the mapview.
1429  *
1430  * This array can be added to without breaking anything elsewhere.
1431  */
1432 static int color_index = 0;
1433 #define NUM_CITY_COLORS tileset_num_city_colors(tileset)
1434 
1435 
1436 /****************************************************************************
1437   Toggle the city color.  This cycles through the possible colors for the
1438   citymap as shown on the mapview.  These colors are listed in the
1439   city_colors array; above.
1440 ****************************************************************************/
toggle_city_color(struct city * pcity)1441 void toggle_city_color(struct city *pcity)
1442 {
1443   if (pcity->client.colored) {
1444     pcity->client.colored = FALSE;
1445   } else {
1446     pcity->client.colored = TRUE;
1447     pcity->client.color_index = color_index;
1448     color_index = (color_index + 1) % NUM_CITY_COLORS;
1449   }
1450 
1451   refresh_city_mapcanvas(pcity, pcity->tile, TRUE, FALSE);
1452 }
1453 
1454 /****************************************************************************
1455   Toggle the unit color.  This cycles through the possible colors for the
1456   citymap as shown on the mapview.  These colors are listed in the
1457   city_colors array; above.
1458 ****************************************************************************/
toggle_unit_color(struct unit * punit)1459 void toggle_unit_color(struct unit *punit)
1460 {
1461   if (punit->client.colored) {
1462     punit->client.colored = FALSE;
1463   } else {
1464     punit->client.colored = TRUE;
1465     punit->client.color_index = color_index;
1466     color_index = (color_index + 1) % NUM_CITY_COLORS;
1467   }
1468 
1469   refresh_unit_mapcanvas(punit, unit_tile(punit), TRUE, FALSE);
1470 }
1471 
1472 /****************************************************************************
1473   Animate the nuke explosion at map(x, y).
1474 ****************************************************************************/
put_nuke_mushroom_pixmaps(struct tile * ptile)1475 void put_nuke_mushroom_pixmaps(struct tile *ptile)
1476 {
1477   float canvas_x, canvas_y;
1478   struct sprite *mysprite = get_nuke_explode_sprite(tileset);
1479   int width, height;
1480 
1481   get_sprite_dimensions(mysprite, &width, &height);
1482 
1483   if (frame_by_frame_animation) {
1484     struct animation *anim = fc_malloc(sizeof(struct animation));
1485 
1486     anim->type = ANIM_NUKE;
1487     anim->id = -1;
1488     anim->nuke.shown = FALSE;
1489     anim->nuke.nuke_tile = ptile;
1490 
1491     (void) tile_to_canvas_pos(&canvas_x, &canvas_y, ptile);
1492     get_sprite_dimensions(mysprite, &width, &height);
1493 
1494     anim->width = width * map_zoom;
1495     anim->height = height * map_zoom;
1496     animation_add(anim);
1497   } else {
1498     /* We can't count on the return value of tile_to_canvas_pos since the
1499      * sprite may span multiple tiles. */
1500     (void) tile_to_canvas_pos(&canvas_x, &canvas_y, ptile);
1501 
1502     canvas_x += (tileset_tile_width(tileset) - width) / 2 * map_zoom;
1503     canvas_y += (tileset_tile_height(tileset) - height) / 2 * map_zoom;
1504 
1505     /* Make sure everything is flushed and synced before proceeding.  First
1506      * we update everything to the store, but don't write this to screen.
1507      * Then add the nuke graphic to the store.  Finally flush everything to
1508      * the screen and wait 1 second. */
1509     unqueue_mapview_updates(FALSE);
1510 
1511     canvas_put_sprite_full(mapview.store, canvas_x, canvas_y, mysprite);
1512     dirty_rect(canvas_x, canvas_y, width, height);
1513 
1514     flush_dirty();
1515     gui_flush();
1516 
1517     fc_usleep(1000000);
1518 
1519     update_map_canvas_visible();
1520   }
1521 }
1522 
1523 /**************************************************************************
1524   Draw some or all of a tile onto the canvas.
1525 **************************************************************************/
put_one_tile(struct canvas * pcanvas,enum mapview_layer layer,struct tile * ptile,int canvas_x,int canvas_y,const struct city * citymode)1526 static void put_one_tile(struct canvas *pcanvas, enum mapview_layer layer,
1527 			 struct tile *ptile, int canvas_x, int canvas_y,
1528 			 const struct city *citymode)
1529 {
1530   if (client_tile_get_known(ptile) != TILE_UNKNOWN
1531       || (editor_is_active() && editor_tile_is_selected(ptile))) {
1532     struct unit *punit = get_drawable_unit(tileset, ptile, citymode);
1533     struct animation *anim = NULL;
1534 
1535     if (animation_list_size(animations) > 0) {
1536       anim = animation_list_get(animations, 0);
1537     }
1538 
1539     if (anim != NULL && punit != NULL
1540         && punit->id == anim->id) {
1541       punit = NULL;
1542     }
1543 
1544     put_one_element(pcanvas, map_zoom, layer, ptile, NULL, NULL, punit,
1545                     tile_city(ptile), canvas_x, canvas_y, citymode, NULL);
1546   }
1547 }
1548 
1549 /**************************************************************************
1550   Depending on where ptile1 and ptile2 are on the map canvas, a trade route
1551   line may need to be drawn as two disjointed line segments. This function
1552   fills the given line array 'lines' with the necessary line segments.
1553 
1554   The return value is the number of line segments that need to be drawn.
1555 
1556   NB: It is assumed ptile1 and ptile2 are already consistently ordered.
1557   NB: 'lines' must be able to hold least MAX_TRADE_ROUTE_DRAW_LINES
1558   elements.
1559 **************************************************************************/
trade_route_to_canvas_lines(const struct tile * ptile1,const struct tile * ptile2,struct trade_route_line * lines)1560 static int trade_route_to_canvas_lines(const struct tile *ptile1,
1561                                        const struct tile *ptile2,
1562                                        struct trade_route_line *lines)
1563 {
1564   int dx, dy;
1565 
1566   if (!ptile1 || !ptile2 || !lines) {
1567     return 0;
1568   }
1569 
1570   base_map_distance_vector(&dx, &dy, TILE_XY(ptile1), TILE_XY(ptile2));
1571   map_to_gui_pos(tileset, &lines[0].width, &lines[0].height, dx, dy);
1572 
1573   /* FIXME: Remove these casts. */
1574   tile_to_canvas_pos(&lines[0].x, &lines[0].y, (struct tile *)ptile1);
1575   tile_to_canvas_pos(&lines[1].x, &lines[1].y, (struct tile *)ptile2);
1576 
1577   if (lines[1].x - lines[0].x == lines[0].width
1578       && lines[1].y - lines[0].y == lines[0].height) {
1579     return 1;
1580   }
1581 
1582   lines[1].width = -lines[0].width;
1583   lines[1].height = -lines[0].height;
1584   return 2;
1585 }
1586 
1587 /**************************************************************************
1588   Draw a colored trade route line from one tile to another.
1589 **************************************************************************/
draw_trade_route_line(const struct tile * ptile1,const struct tile * ptile2,enum color_std color)1590 static void draw_trade_route_line(const struct tile *ptile1,
1591                                   const struct tile *ptile2,
1592                                   enum color_std color)
1593 {
1594   struct trade_route_line lines[MAX_TRADE_ROUTE_DRAW_LINES];
1595   int line_count, i;
1596   struct color *pcolor;
1597 
1598   if (!ptile1 || !ptile2) {
1599     return;
1600   }
1601 
1602   pcolor = get_color(tileset, color);
1603   if (!pcolor) {
1604     return;
1605   }
1606 
1607   /* Order the source and destination tiles consistently
1608    * so that if a line is drawn twice it does not produce
1609    * ugly effects due to dashes not lining up. */
1610   if (tile_index(ptile2) > tile_index(ptile1)) {
1611     const struct tile *tmp;
1612     tmp = ptile1;
1613     ptile1 = ptile2;
1614     ptile2 = tmp;
1615   }
1616 
1617   line_count = trade_route_to_canvas_lines(ptile1, ptile2, lines);
1618   for (i = 0; i < line_count; i++) {
1619     /* XXX: canvas_put_line doesn't currently take map_zoom into account
1620      * itself, but it probably should? */
1621     canvas_put_line(mapview.store, pcolor, LINE_BORDER,
1622                     lines[i].x + tileset_tile_width(tileset) / 2 * map_zoom,
1623                     lines[i].y + tileset_tile_height(tileset) / 2 * map_zoom,
1624                     lines[i].width, lines[i].height);
1625   }
1626 }
1627 
1628 /**************************************************************************
1629   Draw all trade routes for the given city.
1630 **************************************************************************/
draw_trade_routes_for_city(const struct city * pcity_src)1631 static void draw_trade_routes_for_city(const struct city *pcity_src)
1632 {
1633   if (!pcity_src) {
1634     return;
1635   }
1636 
1637   trade_routes_iterate(pcity_src, pcity_dest) {
1638     draw_trade_route_line(city_tile(pcity_src), city_tile(pcity_dest),
1639                           COLOR_MAPVIEW_TRADE_ROUTE_LINE);
1640   } trade_routes_iterate_end;
1641 }
1642 
1643 /**************************************************************************
1644   Draw trade routes between cities as lines on the main map canvas.
1645 **************************************************************************/
draw_trade_routes(void)1646 static void draw_trade_routes(void)
1647 {
1648   if (!gui_options.draw_city_trade_routes) {
1649     return;
1650   }
1651 
1652   if (client_is_global_observer()) {
1653     cities_iterate(pcity) {
1654       draw_trade_routes_for_city(pcity);
1655     } cities_iterate_end;
1656   } else {
1657     struct player *pplayer = client_player();
1658     if (!pplayer) {
1659       return;
1660     }
1661     city_list_iterate(pplayer->cities, pcity) {
1662       draw_trade_routes_for_city(pcity);
1663     } city_list_iterate_end;
1664   }
1665 }
1666 
1667 /**************************************************************************
1668   Update (refresh) the map canvas starting at the given tile (in map
1669   coordinates) and with the given dimensions (also in map coordinates).
1670 
1671   In non-iso view, this is easy.  In iso view, we have to use the
1672   Painter's Algorithm to draw the tiles in back first.  When we draw
1673   a tile, we tell the GUI which part of the tile to draw - which is
1674   necessary unless we have an extra buffering step.
1675 
1676   After refreshing the backing store tile-by-tile, we write the store
1677   out to the display if write_to_screen is specified.
1678 
1679   x, y, width, and height are in map coordinates; they need not be
1680   normalized or even real.
1681 **************************************************************************/
update_map_canvas(int canvas_x,int canvas_y,int width,int height)1682 void update_map_canvas(int canvas_x, int canvas_y, int width, int height)
1683 {
1684   int gui_x0, gui_y0;
1685   bool full;
1686   struct canvas *tmp;
1687 
1688   canvas_x = MAX(canvas_x, 0);
1689   canvas_y = MAX(canvas_y, 0);
1690   width = MIN(mapview.store_width - canvas_x, width);
1691   height = MIN(mapview.store_height - canvas_y, height);
1692 
1693   gui_x0 = mapview.gui_x0 + canvas_x;
1694   gui_y0 = mapview.gui_y0 + canvas_y;
1695   full = (canvas_x == 0 && canvas_y == 0
1696 	  && width == mapview.store_width
1697 	  && height == mapview.store_height);
1698 
1699   log_debug("update_map_canvas(pos=(%d,%d), size=(%d,%d))",
1700             canvas_x, canvas_y, width, height);
1701 
1702   /* If a full redraw is done, we just draw everything onto the canvas.
1703    * However if a partial redraw is done we draw everything onto the
1704    * tmp_canvas then copy *just* the area of update onto the canvas. */
1705   if (!full) {
1706     /* Swap store and tmp_store. */
1707     tmp = mapview.store;
1708     mapview.store = mapview.tmp_store;
1709     mapview.tmp_store = tmp;
1710   }
1711 
1712   /* Clear the area.  This is necessary since some parts of the rectangle
1713    * may not actually have any tiles drawn on them.  This will happen when
1714    * the mapview is large enough so that the tile is visible in multiple
1715    * locations.  In this case it will only be drawn in one place.
1716    *
1717    * Of course it's necessary to draw to the whole area to cover up any old
1718    * drawing that was done there. */
1719   canvas_put_rectangle(mapview.store,
1720 		       get_color(tileset, COLOR_MAPVIEW_UNKNOWN),
1721 		       canvas_x, canvas_y, width / map_zoom, height / map_zoom);
1722 
1723   mapview_layer_iterate(layer) {
1724     if (layer == LAYER_TILELABEL) {
1725       show_tile_labels(canvas_x, canvas_y, width, height);
1726     }
1727     if (layer == LAYER_CITYBAR) {
1728       show_city_descriptions(canvas_x, canvas_y, width, height);
1729       continue;
1730     }
1731     gui_rect_iterate_coord(gui_x0, gui_y0, width,
1732 			   height + (tileset_is_isometric(tileset)
1733 				     ? (tileset_tile_height(tileset) / 2 * map_zoom) : 0),
1734 			   ptile, pedge, pcorner, gui_x, gui_y, map_zoom) {
1735       const int cx = gui_x - mapview.gui_x0, cy = gui_y - mapview.gui_y0;
1736 
1737       if (ptile) {
1738 	put_one_tile(mapview.store, layer, ptile, cx, cy, NULL);
1739       } else if (pedge) {
1740         put_one_element(mapview.store, map_zoom, layer, NULL, pedge, NULL,
1741                         NULL, NULL, cx, cy, NULL, NULL);
1742       } else if (pcorner) {
1743         put_one_element(mapview.store, map_zoom, layer, NULL, NULL, pcorner,
1744                         NULL, NULL, cx, cy, NULL, NULL);
1745       } else {
1746 	/* This can happen, for instance for unreal tiles. */
1747       }
1748     } gui_rect_iterate_coord_end;
1749   } mapview_layer_iterate_end;
1750 
1751   draw_trade_routes();
1752   link_marks_draw_all();
1753 
1754   /* Draw the goto lines on top of the whole thing. This is done last as
1755    * we want it completely on top.
1756    *
1757    * Note that a pixel right on the border of a tile may actually contain a
1758    * goto line from an adjacent tile.  Thus we draw any extra goto lines
1759    * from adjacent tiles (if they're close enough). */
1760   gui_rect_iterate(gui_x0 - GOTO_WIDTH, gui_y0 - GOTO_WIDTH,
1761 		   width + 2 * GOTO_WIDTH, height + 2 * GOTO_WIDTH,
1762 		   ptile, pedge, pcorner, map_zoom) {
1763     if (!ptile) {
1764       continue;
1765     }
1766     adjc_dir_base_iterate(ptile, dir) {
1767       if (mapdeco_is_gotoline_set(ptile, dir)) {
1768         draw_segment(ptile, dir);
1769       }
1770     } adjc_dir_base_iterate_end;
1771   } gui_rect_iterate_end;
1772 
1773   if (!full) {
1774     /* Swap store and tmp_store back. */
1775     tmp = mapview.store;
1776     mapview.store = mapview.tmp_store;
1777     mapview.tmp_store = tmp;
1778 
1779     /* And copy store to tmp_store. */
1780     canvas_copy(mapview.store, mapview.tmp_store,
1781 		canvas_x, canvas_y, canvas_x, canvas_y, width, height);
1782   }
1783 
1784   dirty_rect(canvas_x, canvas_y, width, height);
1785 }
1786 
1787 /**************************************************************************
1788  Update (only) the visible part of the map
1789 **************************************************************************/
update_map_canvas_visible(void)1790 void update_map_canvas_visible(void)
1791 {
1792   queue_mapview_update(UPDATE_MAP_CANVAS_VISIBLE);
1793 }
1794 
1795 /* The maximum city description width and height.  This gives the dimensions
1796  * of a rectangle centered directly beneath the tile a city is on, that
1797  * contains the city description.
1798  *
1799  * These values are increased when drawing is done.  This may mean that
1800  * the change (from increasing the value) won't take place until the
1801  * next redraw. */
1802 static int max_desc_width = 0, max_desc_height = 0;
1803 
1804 /* Same for tile labels */
1805 static int max_label_width = 0, max_label_height = 0 ;
1806 
1807 /**************************************************************************
1808   Update the city description for the given city.
1809 **************************************************************************/
update_city_description(struct city * pcity)1810 void update_city_description(struct city *pcity)
1811 {
1812   queue_mapview_tile_update(pcity->tile, TILE_UPDATE_CITY_DESC);
1813 }
1814 
1815 /**************************************************************************
1816   Update the label for the given tile
1817 **************************************************************************/
update_tile_label(struct tile * ptile)1818 void update_tile_label(struct tile *ptile)
1819 {
1820   queue_mapview_tile_update(ptile, TILE_UPDATE_TILE_LABEL);
1821 }
1822 
1823 /****************************************************************************
1824   Draw a "full" city bar for the city.  This is a subcase of show_city_desc
1825   (see that function for more info) for tilesets that have a full city bar.
1826 ****************************************************************************/
show_full_citybar(struct canvas * pcanvas,const int canvas_x0,const int canvas_y0,struct city * pcity,int * width,int * height)1827 static void show_full_citybar(struct canvas *pcanvas,
1828 			      const int canvas_x0, const int canvas_y0,
1829 			      struct city *pcity, int *width, int *height)
1830 {
1831   const struct citybar_sprites *citybar = get_citybar_sprites(tileset);
1832   static char name[512], growth[32], prod[512], size[32], trade_routes[32];
1833   enum color_std growth_color;
1834   enum color_std production_color;
1835   /* trade_routes_color initialized just to get rid of gcc warning
1836    * on optimization level 3 when it misdiagnoses that it would be used
1837    * uninitialized otherwise. Funny thing here is that warning would
1838    * go away also by *not* setting it to values other than
1839    * COLOR_MAPVIEW_CITYTEXT in get_city_mapview_trade_routes() */
1840   enum color_std trade_routes_color = COLOR_MAPVIEW_CITYTEXT;
1841   struct color *owner_color;
1842   struct {
1843     int x, y, w, h;
1844   } name_rect = {0, 0, 0, 0}, growth_rect = {0, 0, 0, 0},
1845     prod_rect = {0, 0, 0, 0}, size_rect = {0, 0, 0, 0},
1846     flag_rect = {0, 0, 0, 0}, occupy_rect = {0, 0, 0, 0},
1847     food_rect = {0, 0, 0, 0}, shield_rect = {0, 0, 0, 0},
1848     trade_routes_rect = {0,}, trade_rect = {0,};
1849   int width1 = 0, width2 = 0, height1 = 0, height2 = 0;
1850   struct sprite *bg = citybar->background;
1851   struct sprite *flag = get_city_flag_sprite(tileset, pcity);
1852   struct sprite *occupy = NULL;
1853   int bg_w, bg_h, x, y;
1854   const int canvas_x = canvas_x0 + tileset_tile_width(tileset) / 2 * map_zoom;
1855   const int canvas_y = canvas_y0 + tileset_citybar_offset_y(tileset) * map_zoom;
1856   const int border = 6;
1857   const enum client_font FONT_CITY_SIZE = FONT_CITY_NAME; /* TODO: new font */
1858 
1859   /* We can see the city's production or growth values if
1860    * we are observing or playing as the owner of the city. */
1861   const bool can_see_inside
1862     = (client_is_global_observer() || city_owner(pcity) == client_player());
1863   const bool should_draw_productions
1864     = can_see_inside && gui_options.draw_city_productions;
1865   const bool should_draw_growth = can_see_inside && gui_options.draw_city_growth;
1866   const bool should_draw_trade_routes = can_see_inside
1867     && gui_options.draw_city_trade_routes;
1868   const bool should_draw_lower_bar
1869     = should_draw_productions || should_draw_growth
1870     || should_draw_trade_routes;
1871 
1872 
1873   if (width != NULL) {
1874     *width = 0;
1875   }
1876   if (height != NULL) {
1877     *height = 0;
1878   }
1879 
1880   if (!gui_options.draw_city_names && !should_draw_lower_bar) {
1881     return;
1882   }
1883 
1884 
1885   /* First: calculate rect dimensions (but not positioning). */
1886 
1887   get_sprite_dimensions(bg, &bg_w, &bg_h);
1888   bg_w *= map_zoom; bg_h *= map_zoom;
1889   get_city_mapview_name_and_growth(pcity, name, sizeof(name),
1890 				   growth, sizeof(growth), &growth_color, &production_color);
1891 
1892   if (gui_options.draw_city_names) {
1893     fc_snprintf(size, sizeof(size), "%d", city_size_get(pcity));
1894 
1895     get_text_size(&size_rect.w, &size_rect.h, FONT_CITY_SIZE, size);
1896     get_text_size(&name_rect.w, &name_rect.h, FONT_CITY_NAME, name);
1897 
1898     if (can_player_see_units_in_city(client.conn.playing, pcity)) {
1899       int count = unit_list_size(pcity->tile->units);
1900 
1901       count = CLIP(0, count, citybar->occupancy.size - 1);
1902       occupy = citybar->occupancy.p[count];
1903     } else {
1904       if (pcity->client.occupied) {
1905 	occupy = citybar->occupied;
1906       } else {
1907 	occupy = citybar->occupancy.p[0];
1908       }
1909     }
1910 
1911     get_sprite_dimensions(flag, &flag_rect.w, &flag_rect.h);
1912     flag_rect.w *= map_zoom; flag_rect.h *= map_zoom;
1913     get_sprite_dimensions(occupy, &occupy_rect.w, &occupy_rect.h);
1914     occupy_rect.w *= map_zoom; occupy_rect.h *= map_zoom;
1915 
1916     width1 = (flag_rect.w + occupy_rect.w + name_rect.w
1917 	      + 2 * border + size_rect.w);
1918     height1 = MAX(flag_rect.h,
1919 		  MAX(occupy_rect.h,
1920 		      MAX(name_rect.h + border,
1921 			  size_rect.h + border)));
1922   }
1923 
1924   if (should_draw_lower_bar) {
1925     width2 = 0;
1926     height2 = 0;
1927 
1928     if (should_draw_productions) {
1929       get_city_mapview_production(pcity, prod, sizeof(prod));
1930       get_text_size(&prod_rect.w, &prod_rect.h, FONT_CITY_PROD, prod);
1931 
1932       get_sprite_dimensions(citybar->shields, &shield_rect.w, &shield_rect.h);
1933       shield_rect.w *= map_zoom; shield_rect.h *= map_zoom;
1934       width2 += shield_rect.w + prod_rect.w + border;
1935       height2 = MAX(height2, shield_rect.h);
1936       height2 = MAX(height2, prod_rect.h + border);
1937     }
1938 
1939     if (should_draw_growth) {
1940       get_text_size(&growth_rect.w, &growth_rect.h, FONT_CITY_PROD, growth);
1941       get_sprite_dimensions(citybar->food, &food_rect.w, &food_rect.h);
1942       food_rect.w *= map_zoom; food_rect.h *= map_zoom;
1943       width2 += food_rect.w + growth_rect.w + border;
1944       height2 = MAX(height2, food_rect.h);
1945       height2 = MAX(height2, growth_rect.h + border);
1946     }
1947 
1948     if (should_draw_trade_routes) {
1949       get_city_mapview_trade_routes(pcity, trade_routes,
1950                                     sizeof(trade_routes),
1951                                     &trade_routes_color);
1952       get_text_size(&trade_routes_rect.w, &trade_routes_rect.h,
1953                     FONT_CITY_PROD, trade_routes);
1954       get_sprite_dimensions(citybar->trade, &trade_rect.w, &trade_rect.h);
1955       trade_rect.w *= map_zoom; trade_rect.h *= map_zoom;
1956       width2 += trade_rect.w + trade_routes_rect.w + border;
1957       height2 = MAX(height2, trade_rect.h);
1958       height2 = MAX(height2, trade_routes_rect.h + border);
1959     }
1960   }
1961 
1962   *width = MAX(width1, width2);
1963   *height = height1 + height2;
1964 
1965 
1966   /* Next fill in X and Y locations. */
1967 
1968   if (gui_options.draw_city_names) {
1969     flag_rect.x = canvas_x - *width / 2;
1970     flag_rect.y = canvas_y + (height1 - flag_rect.h) / 2;
1971 
1972     occupy_rect.x = flag_rect.x + flag_rect.w;
1973     occupy_rect.y = canvas_y + (height1 - occupy_rect.h) / 2;
1974 
1975     name_rect.x = canvas_x + (flag_rect.w + occupy_rect.w
1976 			      - name_rect.w - size_rect.w - border) / 2;
1977     name_rect.y = canvas_y + (height1 - name_rect.h) / 2;
1978 
1979     size_rect.x = canvas_x + (*width + 1) / 2 - size_rect.w - border / 2;
1980     size_rect.y = canvas_y + (height1 - size_rect.h) / 2;
1981   }
1982 
1983   if (should_draw_lower_bar) {
1984     if (should_draw_productions) {
1985       shield_rect.x = canvas_x - *width / 2;
1986       shield_rect.y = canvas_y + height1 + (height2 - shield_rect.h) / 2;
1987 
1988       prod_rect.x = shield_rect.x + shield_rect.w + border / 2;
1989       prod_rect.y = canvas_y + height1 + (height2 - prod_rect.h) / 2;
1990     }
1991 
1992     if (should_draw_trade_routes) {
1993       trade_routes_rect.x = canvas_x + (*width + 1) / 2
1994         - trade_routes_rect.w - border / 2;
1995       trade_routes_rect.y = canvas_y + height1
1996         + (height2 - trade_routes_rect.h) / 2;
1997 
1998       trade_rect.x = trade_routes_rect.x - border / 2 - trade_rect.w;
1999       trade_rect.y = canvas_y + height1 + (height2 - trade_rect.h) / 2;
2000     }
2001 
2002     if (should_draw_growth) {
2003       growth_rect.x = canvas_x + (*width + 1) / 2
2004         - growth_rect.w - border / 2;
2005       if (trade_routes_rect.w > 0) {
2006         growth_rect.x = growth_rect.x
2007           - trade_routes_rect.w - border / 2 - trade_rect.w - border / 2;
2008       }
2009       growth_rect.y = canvas_y + height1 + (height2 - growth_rect.h) / 2;
2010 
2011       food_rect.x = growth_rect.x - border / 2 - food_rect.w;
2012       food_rect.y = canvas_y + height1 + (height2 - food_rect.h) / 2;
2013     }
2014   }
2015 
2016 
2017   /* Now draw. */
2018 
2019   /* Draw the city bar's background. */
2020   for (x = 0; x < *width; x += bg_w) {
2021     for (y = 0; y < *height; y += bg_h) {
2022       canvas_put_sprite(pcanvas, (canvas_x - *width / 2 + x) / map_zoom,
2023                         (canvas_y + y) / map_zoom,
2024 			bg, 0, 0,
2025                         (*width - x) / map_zoom, (*height - y) / map_zoom);
2026     }
2027   }
2028 
2029   owner_color = get_player_color(tileset, city_owner(pcity));
2030 
2031   if (gui_options.draw_city_names) {
2032     canvas_put_sprite_full(pcanvas,
2033                            flag_rect.x / map_zoom, flag_rect.y / map_zoom,
2034                            flag);
2035     /* XXX: canvas_put_line() doesn't currently take map_zoom into account.
2036      * Should it?
2037      * In the meantime, don't compensate with '/ map_zoom' here, unlike
2038      * for canvas_put_sprite/text/rectangle */
2039     canvas_put_line(pcanvas, owner_color, LINE_NORMAL,
2040 		    (flag_rect.x + flag_rect.w) /* / map_zoom */ - 1,
2041                     canvas_y /* / map_zoom */,
2042 		    0, height1 /* / map_zoom */);
2043     canvas_put_sprite_full(pcanvas,
2044                            occupy_rect.x / map_zoom, occupy_rect.y / map_zoom,
2045                            occupy);
2046     canvas_put_text(pcanvas, name_rect.x / map_zoom, name_rect.y / map_zoom,
2047                     FONT_CITY_NAME,
2048                     get_color(tileset, COLOR_MAPVIEW_CITYTEXT), name);
2049     canvas_put_rectangle(pcanvas, owner_color,
2050 			 (size_rect.x - border / 2) / map_zoom,
2051                          canvas_y / map_zoom,
2052 			 (size_rect.w + border) / map_zoom,
2053                          height1 / map_zoom);
2054     {
2055       /* Try to pick a color for city size text that contrasts with
2056        * player color */
2057       struct color *textcolors[2] = {
2058         get_color(tileset, COLOR_MAPVIEW_CITYTEXT),
2059         get_color(tileset, COLOR_MAPVIEW_CITYTEXT_DARK)
2060       };
2061 
2062       canvas_put_text(pcanvas, size_rect.x / map_zoom, size_rect.y / map_zoom,
2063                       FONT_CITY_NAME,
2064                       color_best_contrast(owner_color, textcolors,
2065                                           ARRAY_SIZE(textcolors)), size);
2066     }
2067   }
2068 
2069   if (should_draw_lower_bar) {
2070 
2071     if (should_draw_productions) {
2072       canvas_put_sprite_full(pcanvas,
2073                              shield_rect.x / map_zoom, shield_rect.y / map_zoom,
2074                              citybar->shields);
2075       canvas_put_text(pcanvas, prod_rect.x / map_zoom, prod_rect.y / map_zoom,
2076                       FONT_CITY_PROD,
2077                       get_color(tileset, production_color), prod);
2078     }
2079 
2080     if (should_draw_trade_routes) {
2081       canvas_put_sprite_full(pcanvas,
2082                              trade_rect.x / map_zoom, trade_rect.y / map_zoom,
2083                              citybar->trade);
2084       canvas_put_text(pcanvas,
2085                       trade_routes_rect.x / map_zoom, trade_routes_rect.y / map_zoom,
2086                       FONT_CITY_PROD,
2087                       get_color(tileset, trade_routes_color), trade_routes);
2088     }
2089 
2090     if (should_draw_growth) {
2091       canvas_put_sprite_full(pcanvas,
2092                              food_rect.x / map_zoom, food_rect.y / map_zoom,
2093                              citybar->food);
2094       canvas_put_text(pcanvas, growth_rect.x / map_zoom, growth_rect.y / map_zoom,
2095                       FONT_CITY_PROD,
2096                       get_color(tileset, growth_color), growth);
2097     }
2098   }
2099 
2100   /* Draw the city bar's outline. */
2101   /* XXX not scaling by map_zoom, see above */
2102   canvas_put_line(pcanvas, owner_color, LINE_NORMAL,
2103 		  (canvas_x - *width / 2) /* / map_zoom */,
2104                   canvas_y /* / map_zoom */,
2105 		  *width /* / map_zoom */, 0);
2106   canvas_put_line(pcanvas, owner_color, LINE_NORMAL,
2107 		  (canvas_x - *width / 2) /* / map_zoom */,
2108                   canvas_y /* / map_zoom */,
2109 		  0, *height /* / map_zoom */);
2110   canvas_put_line(pcanvas, owner_color, LINE_NORMAL,
2111 		  (canvas_x - *width / 2) /* / map_zoom */,
2112                   (canvas_y + *height) /* / map_zoom */ - 1,
2113 		  *width /* / map_zoom */, 0);
2114   canvas_put_line(pcanvas, owner_color, LINE_NORMAL,
2115 		  (canvas_x - *width / 2 + *width) /* / map_zoom */,
2116                   canvas_y /* / map_zoom */,
2117 		  0, *height /* / map_zoom */);
2118 
2119   /* Draw the dividing line if we drew both the
2120    * upper and lower parts. */
2121   if (gui_options.draw_city_names && should_draw_lower_bar) {
2122     canvas_put_line(pcanvas, owner_color, LINE_NORMAL,
2123 		    (canvas_x - *width / 2) /* / map_zoom */,
2124                     (canvas_y + height1) /* / map_zoom */ - 1,
2125 		    *width /* / map_zoom */, 0);
2126   }
2127 }
2128 
2129 /****************************************************************************
2130   Draw a "small" city bar for the city.  This is a subcase of show_city_desc
2131   (see that function for more info) for tilesets that do not have a full
2132   city bar.
2133 ****************************************************************************/
show_small_citybar(struct canvas * pcanvas,int canvas_x,int canvas_y,struct city * pcity,int * width,int * height)2134 static void show_small_citybar(struct canvas *pcanvas,
2135 			   int canvas_x, int canvas_y,
2136 			   struct city *pcity, int *width, int *height)
2137 {
2138   static char name[512], growth[32], prod[512], trade_routes[32];
2139   enum color_std growth_color;
2140   enum color_std production_color;
2141  /* trade_routes_color initialized just to get rid off gcc warning
2142    * on optimization level 3 when it misdiagnoses that it would be used
2143    * uninitialized otherwise. Funny thing here is that warning would
2144    * go away also by *not* setting it to values other than
2145    * COLOR_MAPVIEW_CITYTEXT in get_city_mapview_trade_routes() */
2146   enum color_std trade_routes_color = COLOR_MAPVIEW_CITYTEXT;
2147   struct {
2148     int x, y, w, h;
2149   } name_rect = {0, 0, 0, 0}, growth_rect = {0, 0, 0, 0},
2150     prod_rect = {0, 0, 0, 0}, trade_routes_rect = {0,};
2151   int total_width, total_height;
2152   int spacer_width = 0;
2153   const bool can_see_inside = (client_is_global_observer()
2154                                || city_owner(pcity) == client_player());
2155 
2156   *width = *height = 0;
2157 
2158   canvas_x += tileset_tile_width(tileset) / 2 * map_zoom;
2159   canvas_y += tileset_citybar_offset_y(tileset) * map_zoom;
2160 
2161   get_city_mapview_name_and_growth(pcity, name, sizeof(name),
2162                                    growth, sizeof(growth), &growth_color,
2163                                    &production_color);
2164 
2165   if (gui_options.draw_city_names) {
2166     int drawposx;
2167 
2168     /* HACK: put a character's worth of space between the two
2169      * strings if needed. */
2170     get_text_size(&spacer_width, NULL, FONT_CITY_NAME, "M");
2171 
2172     total_width = 0;
2173     total_height = 0;
2174 
2175     get_text_size(&name_rect.w, &name_rect.h, FONT_CITY_NAME, name);
2176     total_width += name_rect.w;
2177     total_height = MAX(total_height, name_rect.h);
2178 
2179     if (gui_options.draw_city_growth && can_see_inside) {
2180       get_text_size(&growth_rect.w, &growth_rect.h, FONT_CITY_PROD, growth);
2181       total_width += spacer_width + growth_rect.w;
2182       total_height = MAX(total_height, growth_rect.h);
2183     }
2184 
2185     if (gui_options.draw_city_trade_routes && can_see_inside) {
2186       get_city_mapview_trade_routes(pcity, trade_routes,
2187                                     sizeof(trade_routes),
2188                                     &trade_routes_color);
2189       get_text_size(&trade_routes_rect.w, &trade_routes_rect.h,
2190                     FONT_CITY_PROD, trade_routes);
2191       total_width += spacer_width + trade_routes_rect.w;
2192       total_height = MAX(total_height, trade_routes_rect.h);
2193     }
2194 
2195     drawposx = canvas_x;
2196     drawposx -= total_width / 2;
2197     canvas_put_text(pcanvas, drawposx / map_zoom, canvas_y / map_zoom,
2198 		    FONT_CITY_NAME,
2199 		    get_color(tileset, COLOR_MAPVIEW_CITYTEXT), name);
2200     drawposx += name_rect.w;
2201 
2202     if (gui_options.draw_city_growth && can_see_inside) {
2203       drawposx += spacer_width;
2204       canvas_put_text(pcanvas, drawposx / map_zoom,
2205 		      (canvas_y + total_height - growth_rect.h) / map_zoom,
2206 		      FONT_CITY_PROD,
2207 		      get_color(tileset, growth_color), growth);
2208       drawposx += growth_rect.w;
2209     }
2210 
2211     if (gui_options.draw_city_trade_routes && can_see_inside) {
2212       drawposx += spacer_width;
2213       canvas_put_text(pcanvas, drawposx / map_zoom,
2214 		      (canvas_y + total_height - trade_routes_rect.h) / map_zoom,
2215 		      FONT_CITY_PROD,
2216 		      get_color(tileset, trade_routes_color), trade_routes);
2217       drawposx += trade_routes_rect.w;
2218     }
2219 
2220     canvas_y += total_height + 3;
2221 
2222     *width = MAX(*width, total_width);
2223     *height += total_height + 3;
2224   }
2225   if (gui_options.draw_city_productions && can_see_inside) {
2226     get_city_mapview_production(pcity, prod, sizeof(prod));
2227     get_text_size(&prod_rect.w, &prod_rect.h, FONT_CITY_PROD, prod);
2228 
2229     total_width = prod_rect.w;
2230     total_height = prod_rect.h;
2231 
2232     canvas_put_text(pcanvas, (canvas_x - total_width / 2) / map_zoom,
2233                     canvas_y / map_zoom,
2234                     FONT_CITY_PROD,
2235                     get_color(tileset, production_color), prod);
2236 
2237     canvas_y += total_height;
2238     *width = MAX(*width, total_width);
2239     *height += total_height;
2240   }
2241 }
2242 
2243 /****************************************************************************
2244   Draw a description for the given city.  This description may include the
2245   name, turns-to-grow, production, and city turns-to-build (depending on
2246   client options).
2247 
2248   (canvas_x, canvas_y) gives the location on the given canvas at which to
2249   draw the description.  This is the location of the city itself so the
2250   text must be drawn underneath it.  pcity gives the city to be drawn,
2251   while (*width, *height) should be set by show_city_desc to contain the
2252   width and height of the text block (centered directly underneath the
2253   city's tile).
2254 ****************************************************************************/
show_city_desc(struct canvas * pcanvas,int canvas_x,int canvas_y,struct city * pcity,int * width,int * height)2255 static void show_city_desc(struct canvas *pcanvas,
2256 			   int canvas_x, int canvas_y,
2257 			   struct city *pcity, int *width, int *height)
2258 {
2259   if (gui_options.draw_full_citybar) {
2260     show_full_citybar(pcanvas, canvas_x, canvas_y, pcity, width, height);
2261   } else {
2262     show_small_citybar(pcanvas, canvas_x, canvas_y, pcity, width, height);
2263   }
2264 }
2265 
2266 /****************************************************************************
2267   Draw a label for the given tile.
2268 
2269   (canvas_x, canvas_y) gives the location on the given canvas at which to
2270   draw the label.  This is the location of the tile itself so the
2271   text must be drawn underneath it.  pcity gives the city to be drawn,
2272   while (*width, *height) should be set by show_tile_label to contain the
2273   width and height of the text block (centered directly underneath the
2274   city's tile).
2275 ****************************************************************************/
show_tile_label(struct canvas * pcanvas,int canvas_x,int canvas_y,struct tile * ptile,int * width,int * height)2276 static void show_tile_label(struct canvas *pcanvas,
2277 			   int canvas_x, int canvas_y,
2278 			   struct tile *ptile, int *width, int *height)
2279 {
2280   const enum client_font FONT_TILE_LABEL = FONT_CITY_NAME; /* TODO: new font */
2281 #define COLOR_MAPVIEW_TILELABEL COLOR_MAPVIEW_CITYTEXT
2282 
2283   canvas_x += tileset_tile_width(tileset) / 2 * map_zoom;
2284   canvas_y += tileset_tilelabel_offset_y(tileset) * map_zoom;
2285 
2286   get_text_size(width, height, FONT_TILE_LABEL, ptile->label);
2287 
2288   canvas_put_text(pcanvas, (canvas_x - * width / 2) / map_zoom, canvas_y / map_zoom,
2289                   FONT_TILE_LABEL,
2290                   get_color(tileset, COLOR_MAPVIEW_TILELABEL), ptile->label);
2291 #undef COLOR_MAPVIEW_TILELABEL
2292 }
2293 
2294 /**************************************************************************
2295   Show descriptions for all cities visible on the map canvas.
2296 **************************************************************************/
show_city_descriptions(int canvas_base_x,int canvas_base_y,int width_base,int height_base)2297 void show_city_descriptions(int canvas_base_x, int canvas_base_y,
2298 			    int width_base, int height_base)
2299 {
2300   const int dx = max_desc_width - tileset_tile_width(tileset) * map_zoom;
2301   const int dy = max_desc_height;
2302   const int offset_y = tileset_citybar_offset_y(tileset) * map_zoom;
2303   int new_max_width = max_desc_width, new_max_height = max_desc_height;
2304 
2305   if (gui_options.draw_full_citybar && !(gui_options.draw_city_names
2306                                          || gui_options.draw_city_productions
2307                                          || gui_options.draw_city_growth)) {
2308     return;
2309   }
2310 
2311   if (!gui_options.draw_full_citybar && !(gui_options.draw_city_names
2312                                           || gui_options.draw_city_productions)) {
2313     return;
2314   }
2315 
2316   /* A city description is shown below the city.  It has a specified
2317    * maximum width and height (although these are only estimates).  Thus
2318    * we need to update some tiles above the mapview and some to the left
2319    * and right.
2320    *
2321    *                    /--W1--\   (W1 = tileset_tile_width(tileset))
2322    *                    -------- \
2323    *                    | CITY | H1 (H1 = tileset_tile_height(tileset))
2324    *                    |      | /
2325    *               ------------------ \
2326    *               |  DESCRIPTION   | H2  (H2 = MAX_CITY_DESC_HEIGHT)
2327    *               |                | /
2328    *               ------------------
2329    *               \-------W2-------/    (W2 = MAX_CITY_DESC_WIDTH)
2330    *
2331    * We must draw H2 extra pixels above and (W2 - W1) / 2 extra pixels
2332    * to each side of the mapview.
2333    */
2334   gui_rect_iterate_coord(mapview.gui_x0 + canvas_base_x - dx / 2,
2335 			 mapview.gui_y0 + canvas_base_y - dy,
2336 			 width_base + dx, height_base + dy - offset_y,
2337 			 ptile, pedge, pcorner, gui_x, gui_y, map_zoom) {
2338     const int canvas_x = gui_x - mapview.gui_x0;
2339     const int canvas_y = gui_y - mapview.gui_y0;
2340 
2341     if (ptile && tile_city(ptile)) {
2342       int width = 0, height = 0;
2343       struct city *pcity = tile_city(ptile);
2344 
2345       show_city_desc(mapview.store, canvas_x, canvas_y,
2346                      pcity, &width, &height);
2347       log_debug("Drawing %s.", city_name_get(pcity));
2348 
2349       if (width > max_desc_width || height > max_desc_height) {
2350         /* The update was incomplete! We queue a new update. Note that
2351          * this is recursively queueing an update within a dequeuing of an
2352          * update. This is allowed specifically because of the code in
2353          * unqueue_mapview_updates. See that function for more. */
2354         log_debug("Re-queuing %s.", city_name_get(pcity));
2355         update_city_description(pcity);
2356       }
2357       new_max_width = MAX(width, new_max_width);
2358       new_max_height = MAX(height, new_max_height);
2359     }
2360   } gui_rect_iterate_coord_end;
2361 
2362   /* We don't update the new max values until the end, so that the
2363    * check above to see what cities need redrawing will be complete. */
2364   max_desc_width = MAX(max_desc_width, new_max_width);
2365   max_desc_height = MAX(max_desc_height, new_max_height);
2366 }
2367 
2368 /**************************************************************************
2369   Show labels for all tiles visible on the map canvas.
2370 **************************************************************************/
show_tile_labels(int canvas_base_x,int canvas_base_y,int width_base,int height_base)2371 void show_tile_labels(int canvas_base_x, int canvas_base_y,
2372                       int width_base, int height_base)
2373 {
2374   const int dx = max_label_width - tileset_tile_width(tileset) * map_zoom;
2375   const int dy = max_label_height;
2376   int new_max_width = max_label_width, new_max_height = max_label_height;
2377 
2378   gui_rect_iterate_coord(mapview.gui_x0 + canvas_base_x - dx / 2,
2379 			 mapview.gui_y0 + canvas_base_y - dy,
2380 			 width_base + dx, height_base + dy,
2381 			 ptile, pedge, pcorner, gui_x, gui_y, map_zoom) {
2382     const int canvas_x = gui_x - mapview.gui_x0;
2383     const int canvas_y = gui_y - mapview.gui_y0;
2384 
2385     if (ptile && ptile->label != NULL) {
2386       int width = 0, height = 0;
2387 
2388       show_tile_label(mapview.store, canvas_x, canvas_y,
2389                       ptile, &width, &height);
2390       log_debug("Drawing label %s.", ptile->label);
2391 
2392       if (width > max_label_width || height > max_label_height) {
2393         /* The update was incomplete! We queue a new update. Note that
2394          * this is recursively queueing an update within a dequeuing of an
2395          * update. This is allowed specifically because of the code in
2396          * unqueue_mapview_updates. See that function for more. */
2397         log_debug("Re-queuing tile label %s drawing.", ptile->label);
2398         update_tile_label(ptile);
2399       }
2400       new_max_width = MAX(width, new_max_width);
2401       new_max_height = MAX(height, new_max_height);
2402     }
2403   } gui_rect_iterate_coord_end;
2404 
2405   /* We don't update the new max values until the end, so that the
2406    * check above to see what cities need redrawing will be complete. */
2407   max_label_width = MAX(max_label_width, new_max_width);
2408   max_label_height = MAX(max_label_height, new_max_height);
2409 }
2410 
2411 /****************************************************************************
2412   Draw the goto route for the unit.  Return TRUE if anything is drawn.
2413 
2414   This duplicates drawing code that is run during the hover state.
2415 ****************************************************************************/
show_unit_orders(struct unit * punit)2416 bool show_unit_orders(struct unit *punit)
2417 {
2418   if (punit && unit_has_orders(punit)) {
2419     struct tile *ptile = unit_tile(punit);
2420     int i;
2421 
2422     for (i = 0; i < punit->orders.length; i++) {
2423       int idx = (punit->orders.index + i) % punit->orders.length;
2424       struct unit_order *order;
2425 
2426       if (punit->orders.index + i >= punit->orders.length
2427 	  && !punit->orders.repeat) {
2428 	break;
2429       }
2430 
2431       order = &punit->orders.list[idx];
2432 
2433       switch (order->order) {
2434       case ORDER_MOVE:
2435 	draw_segment(ptile, order->dir);
2436 	ptile = mapstep(ptile, order->dir);
2437 	if (!ptile) {
2438 	  /* This shouldn't happen unless the server gives us invalid
2439 	   * data.  To avoid disaster we need to break out of the
2440 	   * switch and the enclosing for loop. */
2441           fc_assert(NULL != ptile);
2442 	  i = punit->orders.length;
2443 	}
2444 	break;
2445       default:
2446 	/* TODO: graphics for other orders. */
2447 	break;
2448       }
2449     }
2450     return TRUE;
2451   } else {
2452     return FALSE;
2453   }
2454 }
2455 
2456 /****************************************************************************
2457   Draw a goto line at the given location and direction.  The line goes from
2458   the source tile to the adjacent tile in the given direction.
2459 ****************************************************************************/
draw_segment(struct tile * src_tile,enum direction8 dir)2460 void draw_segment(struct tile *src_tile, enum direction8 dir)
2461 {
2462   float canvas_x, canvas_y, canvas_dx, canvas_dy;
2463 
2464   /* Determine the source position of the segment. */
2465   (void) tile_to_canvas_pos(&canvas_x, &canvas_y, src_tile);
2466   canvas_x += tileset_tile_width(tileset) / 2 * map_zoom;
2467   canvas_y += tileset_tile_height(tileset) / 2 * map_zoom;
2468 
2469   /* Determine the vector of the segment. */
2470   map_to_gui_vector(tileset, map_zoom, &canvas_dx, &canvas_dy,
2471                     DIR_DX[dir], DIR_DY[dir]);
2472 
2473   /* Draw the segment. */
2474   /* XXX: canvas_put_line doesn't currently take map_zoom into account
2475    * itself, but it probably should? If so this will need adjusting */
2476   canvas_put_line(mapview.store,
2477 		  get_color(tileset, COLOR_MAPVIEW_GOTO), LINE_GOTO,
2478 		  canvas_x, canvas_y, canvas_dx, canvas_dy);
2479 
2480   /* The actual area drawn will extend beyond the base rectangle, since
2481    * the goto lines have width. */
2482   dirty_rect(MIN(canvas_x, canvas_x + canvas_dx) - GOTO_WIDTH,
2483 	     MIN(canvas_y, canvas_y + canvas_dy) - GOTO_WIDTH,
2484 	     ABS(canvas_dx) + 2 * GOTO_WIDTH,
2485 	     ABS(canvas_dy) + 2 * GOTO_WIDTH);
2486 
2487   /* It is possible that the mapview wraps between the source and dest
2488    * tiles.  In this case they will not be next to each other; they'll be
2489    * on the opposite sides of the screen.  If this happens then the dest
2490    * tile will not be updated.  This is consistent with the mapview design
2491    * which fails when the size of the mapview approaches that of the map. */
2492 }
2493 
2494 /****************************************************************************
2495   This function is called to decrease a unit's HP smoothly in battle
2496   when combat_animation is turned on.
2497 ****************************************************************************/
decrease_unit_hp_smooth(struct unit * punit0,int hp0,struct unit * punit1,int hp1)2498 void decrease_unit_hp_smooth(struct unit *punit0, int hp0,
2499 			     struct unit *punit1, int hp1)
2500 {
2501   struct unit *losing_unit = (hp0 == 0 ? punit0 : punit1);
2502   float canvas_x, canvas_y;
2503   int i;
2504 
2505   set_units_in_combat(punit0, punit1);
2506 
2507   /* Make sure we don't start out with fewer HP than we're supposed to
2508    * end up with (which would cause the following loop to break). */
2509   punit0->hp = MAX(punit0->hp, hp0);
2510   punit1->hp = MAX(punit1->hp, hp1);
2511 
2512   unqueue_mapview_updates(TRUE);
2513 
2514   if (frame_by_frame_animation) {
2515     struct animation *anim = fc_malloc(sizeof(struct animation));
2516     struct unit *winning_unit;
2517     int winner_end_hp;
2518 
2519     if (losing_unit == punit1) {
2520       winning_unit = punit0;
2521       winner_end_hp = hp0;
2522     } else {
2523       winning_unit = punit1;
2524       winner_end_hp = hp1;
2525     }
2526 
2527     anim->type = ANIM_BATTLE;
2528     anim->id = -1;
2529     anim->battle.virt_loser = unit_virtual_create(unit_owner(losing_unit),
2530                                                   NULL, unit_type_get(losing_unit),
2531                                                   losing_unit->veteran);
2532     anim->battle.loser_tile = unit_tile(losing_unit);
2533     anim->battle.virt_loser->facing = losing_unit->facing;
2534     anim->battle.loser_hp_start = losing_unit->hp;
2535     anim->battle.virt_winner = unit_virtual_create(unit_owner(winning_unit),
2536                                                    NULL, unit_type_get(winning_unit),
2537                                                    winning_unit->veteran);
2538     anim->battle.winner_tile = unit_tile(winning_unit);
2539     anim->battle.virt_winner->facing = winning_unit->facing;
2540     anim->battle.winner_hp_start = MAX(winning_unit->hp, winner_end_hp);
2541     anim->battle.winner_hp_end = winner_end_hp;
2542     anim->battle.steps = MAX(losing_unit->hp,
2543                              anim->battle.winner_hp_start - winner_end_hp);
2544     animation_add(anim);
2545 
2546     anim = fc_malloc(sizeof(struct animation));
2547     anim->type = ANIM_EXPL;
2548     anim->id = winning_unit->id;
2549     anim->expl.tile = losing_unit->tile;
2550     anim->expl.sprites = get_unit_explode_animation(tileset);
2551     anim->expl.sprite_count = sprite_vector_size(anim->expl.sprites);
2552     anim->width = tileset_tile_width(tileset) * map_zoom;
2553     anim->height = tileset_tile_height(tileset) * map_zoom;
2554     animation_add(anim);
2555   } else {
2556     const struct sprite_vector *anim = get_unit_explode_animation(tileset);
2557     const int num_tiles_explode_unit = sprite_vector_size(anim);
2558 
2559     while (punit0->hp > hp0 || punit1->hp > hp1) {
2560       const int diff0 = punit0->hp - hp0, diff1 = punit1->hp - hp1;
2561 
2562       anim_timer = timer_renew(anim_timer, TIMER_USER, TIMER_ACTIVE);
2563       timer_start(anim_timer);
2564 
2565       if (fc_rand(diff0 + diff1) < diff0) {
2566         punit0->hp--;
2567         refresh_unit_mapcanvas(punit0, unit_tile(punit0), FALSE, FALSE);
2568       } else {
2569         punit1->hp--;
2570         refresh_unit_mapcanvas(punit1, unit_tile(punit1), FALSE, FALSE);
2571       }
2572 
2573       unqueue_mapview_updates(TRUE);
2574       gui_flush();
2575 
2576       timer_usleep_since_start(anim_timer,
2577                                gui_options.smooth_combat_step_msec * 1000ul);
2578     }
2579 
2580     if (num_tiles_explode_unit > 0
2581         && tile_to_canvas_pos(&canvas_x, &canvas_y,
2582                               unit_tile(losing_unit))) {
2583       refresh_unit_mapcanvas(losing_unit, unit_tile(losing_unit), FALSE, FALSE);
2584       unqueue_mapview_updates(FALSE);
2585       canvas_copy(mapview.tmp_store, mapview.store,
2586                   canvas_x, canvas_y, canvas_x, canvas_y,
2587                   tileset_tile_width(tileset) * map_zoom,
2588                   tileset_tile_height(tileset) * map_zoom);
2589 
2590       for (i = 0; i < num_tiles_explode_unit; i++) {
2591         int w, h;
2592         struct sprite *sprite = *sprite_vector_get(anim, i);
2593 
2594         get_sprite_dimensions(sprite, &w, &h);
2595         anim_timer = timer_renew(anim_timer, TIMER_USER, TIMER_ACTIVE);
2596         timer_start(anim_timer);
2597 
2598         /* We first draw the explosion onto the unit and draw draw the
2599          * complete thing onto the map canvas window. This avoids
2600          * flickering. */
2601         canvas_copy(mapview.store, mapview.tmp_store,
2602                     canvas_x, canvas_y, canvas_x, canvas_y,
2603                     tileset_tile_width(tileset) * map_zoom,
2604                     tileset_tile_height(tileset) * map_zoom);
2605         canvas_put_sprite_full(mapview.store,
2606                                canvas_x + tileset_tile_width(tileset) / 2 * map_zoom
2607                                - w / 2,
2608                                canvas_y + tileset_tile_height(tileset) / 2 * map_zoom
2609                                - h / 2,
2610                                sprite);
2611         dirty_rect(canvas_x, canvas_y, tileset_tile_width(tileset) * map_zoom,
2612                    tileset_tile_height(tileset) * map_zoom);
2613 
2614         flush_dirty();
2615         gui_flush();
2616 
2617         timer_usleep_since_start(anim_timer,
2618                                  gui_options.smooth_combat_step_msec * 2 * 1000ul);
2619       }
2620     }
2621   }
2622 
2623   set_units_in_combat(NULL, NULL);
2624   refresh_unit_mapcanvas(punit0, unit_tile(punit0), TRUE, FALSE);
2625   refresh_unit_mapcanvas(punit1, unit_tile(punit1), TRUE, FALSE);
2626 }
2627 
2628 /**************************************************************************
2629   Animates punit's "smooth" move from (x0, y0) to (x0+dx, y0+dy).
2630   Note: Works only for adjacent-tile moves.
2631 **************************************************************************/
move_unit_map_canvas(struct unit * punit,struct tile * src_tile,int dx,int dy)2632 void move_unit_map_canvas(struct unit *punit,
2633                           struct tile *src_tile, int dx, int dy)
2634 {
2635   struct tile *dest_tile;
2636   int dest_x, dest_y, src_x, src_y;
2637   int prev_x = -1;
2638   int prev_y = -1;
2639   int tuw;
2640   int tuh;
2641 
2642   /* only works for adjacent-square moves */
2643   if (dx < -1 || dx > 1 || dy < -1 || dy > 1 || (dx == 0 && dy == 0)) {
2644     return;
2645   }
2646 
2647   index_to_map_pos(&src_x, &src_y, tile_index(src_tile));
2648   dest_x = src_x + dx;
2649   dest_y = src_y + dy;
2650   dest_tile = map_pos_to_tile(dest_x, dest_y);
2651   if (!dest_tile) {
2652     return;
2653   }
2654 
2655   if (tile_visible_mapcanvas(src_tile)
2656       || tile_visible_mapcanvas(dest_tile)) {
2657     float start_x, start_y;
2658     float canvas_dx, canvas_dy;
2659     double timing_sec = (double)gui_options.smooth_move_unit_msec / 1000.0;
2660     double mytime;
2661 
2662     fc_assert(gui_options.smooth_move_unit_msec > 0);
2663 
2664     map_to_gui_vector(tileset, map_zoom, &canvas_dx, &canvas_dy, dx, dy);
2665 
2666     tile_to_canvas_pos(&start_x, &start_y, src_tile);
2667     if (tileset_is_isometric(tileset) && tileset_hex_height(tileset) == 0) {
2668       start_y -= tileset_tile_height(tileset) / 2 * map_zoom;
2669       start_y -= (tileset_unit_height(tileset) - tileset_full_tile_height(tileset)) * map_zoom;
2670     }
2671 
2672     /* Bring the backing store up to date, but don't flush. */
2673     unqueue_mapview_updates(FALSE);
2674 
2675     tuw = tileset_unit_width(tileset) * map_zoom;
2676     tuh = tileset_unit_height(tileset) * map_zoom;
2677 
2678     if (frame_by_frame_animation) {
2679       struct animation *anim = fc_malloc(sizeof(struct animation));
2680 
2681       anim->type = ANIM_MOVEMENT;
2682       anim->id = punit->id;
2683       punit->refcount++;
2684       anim->movement.mover = punit;
2685       anim->movement.src = src_tile;
2686       anim->movement.dest = dest_tile;
2687       anim->movement.canvas_dx = canvas_dx;
2688       anim->movement.canvas_dy = canvas_dy;
2689       anim->width = tuw;
2690       anim->height = tuh;
2691       animation_add(anim);
2692     } else {
2693 
2694       /* Start the timer (AFTER the unqueue above). */
2695       anim_timer = timer_renew(anim_timer, TIMER_USER, TIMER_ACTIVE);
2696       timer_start(anim_timer);
2697 
2698       do {
2699         int new_x, new_y;
2700 
2701         mytime = MIN(timer_read_seconds(anim_timer), timing_sec);
2702 
2703         new_x = start_x + canvas_dx * (mytime / timing_sec);
2704         new_y = start_y + canvas_dy * (mytime / timing_sec);
2705 
2706         if (new_x != prev_x || new_y != prev_y) {
2707           /* Backup the canvas store to the temp store. */
2708           canvas_copy(mapview.tmp_store, mapview.store,
2709                       new_x, new_y, new_x, new_y,
2710                       tuw, tuh);
2711 
2712           /* Draw */
2713           put_unit(punit, mapview.store, map_zoom, new_x, new_y);
2714           dirty_rect(new_x, new_y, tuw, tuh);
2715 
2716           /* Flush. */
2717           flush_dirty();
2718           gui_flush();
2719 
2720           /* Restore the backup.  It won't take effect until the next flush. */
2721           canvas_copy(mapview.store, mapview.tmp_store,
2722                       new_x, new_y, new_x, new_y,
2723                       tuw, tuh);
2724           dirty_rect(new_x, new_y, tuw, tuh);
2725 
2726           prev_x = new_x;
2727           prev_y = new_y;
2728         } else {
2729           fc_usleep(500);
2730         }
2731       } while (mytime < timing_sec);
2732     }
2733   }
2734 }
2735 
2736 /**************************************************************************
2737   Find the "best" city/settlers to associate with the selected tile.
2738     a.  If a visible city is working the tile, return that city.
2739     b.  If another player's city is working the tile, return NULL.
2740     c.  If any selected cities are within range, return the closest one.
2741     d.  If any cities are within range, return the closest one.
2742     e.  If any active (with color) settler could work it if they founded a
2743         city, choose the closest one (only if punit != NULL).
2744     f.  If any settler could work it if they founded a city, choose the
2745         closest one (only if punit != NULL).
2746     g.  If nobody can work it, return NULL.
2747 **************************************************************************/
find_city_or_settler_near_tile(const struct tile * ptile,struct unit ** punit)2748 struct city *find_city_or_settler_near_tile(const struct tile *ptile,
2749 					    struct unit **punit)
2750 {
2751   struct city *closest_city;
2752   struct city *pcity;
2753   struct unit *closest_settler = NULL, *best_settler = NULL;
2754   int max_rad = rs_max_city_radius_sq();
2755 
2756   if (punit) {
2757     *punit = NULL;
2758   }
2759 
2760   /* Check if there is visible city working that tile */
2761   pcity = tile_worked(ptile);
2762   if (pcity && pcity->tile) {
2763     if (NULL == client.conn.playing
2764         || city_owner(pcity) == client.conn.playing) {
2765       /* rule a */
2766       return pcity;
2767     } else {
2768       /* rule b */
2769       return NULL;
2770     }
2771   }
2772 
2773   /* rule e */
2774   closest_city = NULL;
2775 
2776   /* check within maximum (squared) city radius */
2777   city_tile_iterate(max_rad, ptile, tile1) {
2778     pcity = tile_city(tile1);
2779     if (pcity
2780 	&& (NULL == client.conn.playing
2781 	    || city_owner(pcity) == client.conn.playing)
2782 	&& client_city_can_work_tile(pcity, tile1)) {
2783       /*
2784        * Note, we must explicitly check if the tile is workable (with
2785        * city_can_work_tile() above), since it is possible that another
2786        * city (perhaps an UNSEEN city) may be working it!
2787        */
2788 
2789       if (mapdeco_is_highlight_set(city_tile(pcity))) {
2790 	/* rule c */
2791 	return pcity;
2792       }
2793       if (!closest_city) {
2794 	closest_city = pcity;
2795       }
2796     }
2797   } city_tile_iterate_end;
2798 
2799   /* rule d */
2800   if (closest_city || !punit) {
2801     return closest_city;
2802   }
2803 
2804   if (!game.scenario.prevent_new_cities) {
2805     /* check within maximum (squared) city radius */
2806     city_tile_iterate(max_rad, ptile, tile1) {
2807       unit_list_iterate(tile1->units, psettler) {
2808         if ((NULL == client.conn.playing
2809              || unit_owner(psettler) == client.conn.playing)
2810             && unit_has_type_flag(psettler, UTYF_CITIES)
2811             && city_can_be_built_here(unit_tile(psettler), psettler)) {
2812           if (!closest_settler) {
2813             closest_settler = psettler;
2814           }
2815           if (!best_settler && psettler->client.colored) {
2816             best_settler = psettler;
2817           }
2818         }
2819       } unit_list_iterate_end;
2820     } city_tile_iterate_end;
2821 
2822     if (best_settler) {
2823       /* Rule e */
2824       *punit = best_settler;
2825     } else if (closest_settler) {
2826       /* Rule f */
2827       *punit = closest_settler;
2828     }
2829   }
2830 
2831   /* rule g */
2832   return NULL;
2833 }
2834 
2835 /**************************************************************************
2836   Find the nearest/best city that owns the tile.
2837 **************************************************************************/
find_city_near_tile(const struct tile * ptile)2838 struct city *find_city_near_tile(const struct tile *ptile)
2839 {
2840   return find_city_or_settler_near_tile(ptile, NULL);
2841 }
2842 
2843 /**************************************************************************
2844   Append the buy cost of the current production of the given city to the
2845   already NULL-terminated buffer. Does nothing if draw_city_buycost is
2846   set to FALSE, or if it does not make sense to buy the current production
2847   (e.g. coinage).
2848 **************************************************************************/
append_city_buycost_string(const struct city * pcity,char * buffer,int buffer_len)2849 static void append_city_buycost_string(const struct city *pcity,
2850                                        char *buffer, int buffer_len)
2851 {
2852   if (!pcity || !buffer || buffer_len < 1) {
2853     return;
2854   }
2855 
2856   if (!gui_options.draw_city_buycost || !city_can_buy(pcity)) {
2857     return;
2858   }
2859 
2860   cat_snprintf(buffer, buffer_len, "/%d",
2861                city_production_buy_gold_cost(pcity));
2862 }
2863 
2864 /**************************************************************************
2865   Find the mapview city production text for the given city, and place it
2866   into the buffer.
2867 **************************************************************************/
get_city_mapview_production(struct city * pcity,char * buffer,size_t buffer_len)2868 void get_city_mapview_production(struct city *pcity,
2869                                  char *buffer, size_t buffer_len)
2870 {
2871   int turns;
2872 
2873   universal_name_translation(&pcity->production, buffer, buffer_len);
2874 
2875   if (city_production_has_flag(pcity, IF_GOLD)) {
2876     return;
2877   }
2878   turns = city_production_turns_to_build(pcity, TRUE);
2879 
2880   if (999 < turns) {
2881     cat_snprintf(buffer, buffer_len, " -");
2882   } else {
2883     cat_snprintf(buffer, buffer_len, " %d", turns);
2884   }
2885 
2886   append_city_buycost_string(pcity, buffer, buffer_len);
2887 }
2888 
2889 /**************************************************************************
2890   Find the mapview city trade routes text for the given city, and place it
2891   into the buffer. Sets 'pcolor' to the preferred color the text should
2892   be drawn in if it is non-NULL.
2893 **************************************************************************/
get_city_mapview_trade_routes(struct city * pcity,char * trade_routes_buffer,size_t trade_routes_buffer_len,enum color_std * pcolor)2894 void get_city_mapview_trade_routes(struct city *pcity,
2895                                    char *trade_routes_buffer,
2896                                    size_t trade_routes_buffer_len,
2897                                    enum color_std *pcolor)
2898 {
2899   int num_trade_routes = 0, i;
2900   int max_routes;
2901 
2902   if (!trade_routes_buffer || trade_routes_buffer_len <= 0) {
2903     return;
2904   }
2905 
2906   if (!pcity) {
2907     trade_routes_buffer[0] = '\0';
2908     if (pcolor) {
2909       *pcolor = COLOR_MAPVIEW_CITYTEXT;
2910     }
2911     return;
2912   }
2913 
2914   for (i = 0; i < MAX_TRADE_ROUTES; i++) {
2915     if (pcity->trade[i] <= 0) {
2916       /* NB: pcity->trade_value[i] == 0 is a valid case. */
2917       continue;
2918     }
2919     num_trade_routes++;
2920   }
2921 
2922   max_routes = max_trade_routes(pcity);
2923 
2924   fc_snprintf(trade_routes_buffer, trade_routes_buffer_len,
2925               "%d/%d", num_trade_routes, max_routes);
2926 
2927   if (pcolor) {
2928     if (num_trade_routes == max_routes) {
2929       *pcolor = COLOR_MAPVIEW_TRADE_ROUTES_ALL_BUILT;
2930     } else if (num_trade_routes == 0) {
2931       *pcolor = COLOR_MAPVIEW_TRADE_ROUTES_NO_BUILT;
2932     } else {
2933       *pcolor = COLOR_MAPVIEW_TRADE_ROUTES_SOME_BUILT;
2934     }
2935   }
2936 }
2937 
2938 /***************************************************************************/
2939 static enum update_type needed_updates = UPDATE_NONE;
2940 static bool callback_queued = FALSE;
2941 
2942 /* These values hold the tiles that need city, unit, or tile updates.
2943  * These different types of updates just tell what area need to be updated,
2944  * not necessarily what's sitting on the tile.  A city update covers the
2945  * whole citymap area.  A unit update covers just the "full" unit tile
2946  * area.  A tile update covers the base tile plus half a tile in each
2947  * direction. */
2948 struct tile_list *tile_updates[TILE_UPDATE_COUNT];
2949 
2950 /****************************************************************************
2951   This callback is called during an idle moment to unqueue any pending
2952   mapview updates.
2953 ****************************************************************************/
queue_callback(void * data)2954 static void queue_callback(void *data)
2955 {
2956   callback_queued = FALSE;
2957   unqueue_mapview_updates(TRUE);
2958 }
2959 
2960 /****************************************************************************
2961   When a mapview update is queued this function should be called to prepare
2962   an idle-time callback to unqueue the updates.
2963 ****************************************************************************/
queue_add_callback(void)2964 static void queue_add_callback(void)
2965 {
2966   if (!callback_queued) {
2967     callback_queued = TRUE;
2968     add_idle_callback(queue_callback, NULL);
2969   }
2970 }
2971 
2972 /**************************************************************************
2973   This function, along with unqueue_mapview_update(), helps in updating
2974   the mapview when a packet is received.  Previously, we just called
2975   update_map_canvas when (for instance) a city update was received.
2976   Not only would this often end up with a lot of duplicated work, but it
2977   would also draw over the city descriptions, which would then just
2978   "disappear" from the mapview.  The hack is to instead call
2979   queue_mapview_update in place of this update, and later (after all
2980   packets have been read) call unqueue_mapview_update.  The functions
2981   don't track which areas of the screen need updating, rather when the
2982   unqueue is done we just update the whole visible mapqueue, and redraw
2983   the city descriptions.
2984 
2985   Using these functions, updates are done correctly, and are probably
2986   faster too.  But it's a bit of a hack to insert this code into the
2987   packet-handling code.
2988 **************************************************************************/
queue_mapview_update(enum update_type update)2989 void queue_mapview_update(enum update_type update)
2990 {
2991   if (can_client_change_view()) {
2992     needed_updates |= update;
2993     queue_add_callback();
2994   }
2995 }
2996 
2997 /**************************************************************************
2998   Queue this tile to be refreshed.  The refresh will be done some time
2999   soon thereafter, and grouped with other needed refreshes.
3000 
3001   Note this should only be called for tiles.  For cities or units use
3002   queue_mapview_xxx_update instead.
3003 **************************************************************************/
queue_mapview_tile_update(struct tile * ptile,enum tile_update_type type)3004 void queue_mapview_tile_update(struct tile *ptile,
3005 			       enum tile_update_type type)
3006 {
3007   if (can_client_change_view()) {
3008     if (!tile_updates[type]) {
3009       tile_updates[type] = tile_list_new();
3010     }
3011     tile_list_append(tile_updates[type], ptile);
3012     queue_add_callback();
3013   }
3014 }
3015 
3016 /**************************************************************************
3017   See comment for queue_mapview_update().
3018 **************************************************************************/
unqueue_mapview_updates(bool write_to_screen)3019 void unqueue_mapview_updates(bool write_to_screen)
3020 {
3021   /* Calculate the area covered by each update type.  The area array gives
3022    * the offset from the tile origin as well as the width and height of the
3023    * area to be updated.  This is initialized each time when entering the
3024    * function from the existing tileset variables.
3025    *
3026    * A TILE update covers the base tile (W x H) plus a half-tile in each
3027    * direction (for edge/corner graphics), making its area 2W x 2H.
3028    *
3029    * A UNIT update covers a UW x UH area.  This is centered horizontally
3030    * over the tile but extends up above the tile (e.g., units in iso-view).
3031    *
3032    * A CITYMAP update covers the whole citymap of a tile.  This includes
3033    * the citymap area itself plus an extra half-tile in each direction (for
3034    * edge/corner graphics).
3035    */
3036   const float W = tileset_tile_width(tileset) * map_zoom;
3037   const float H = tileset_tile_height(tileset) * map_zoom;
3038   const float UW = tileset_unit_width(tileset) * map_zoom;
3039   const float UH = tileset_unit_height(tileset) * map_zoom;
3040   const float city_width = get_citydlg_canvas_width() * map_zoom + W;
3041   const float city_height = get_citydlg_canvas_height() * map_zoom + H;
3042   const struct {
3043     float dx, dy, w, h;
3044   } area[TILE_UPDATE_COUNT] = {
3045     {0, 0, W, H},
3046     {-W / 2, -H / 2, 2 * W, 2 * H},
3047     {(W - UW) / 2, H - UH, UW, UH},
3048     {-(max_desc_width - W) / 2, H, max_desc_width, max_desc_height},
3049     {-(city_width - W) / 2, -(city_height - H) / 2, city_width, city_height},
3050     {-(max_label_width - W) / 2, H, max_label_width, max_label_height}
3051   };
3052   struct tile_list *my_tile_updates[TILE_UPDATE_COUNT];
3053 
3054   int i;
3055 
3056   if (!can_client_change_view()) {
3057     /* Double sanity check: make sure we don't unqueue an invalid update
3058      * after we've already detached. */
3059     return;
3060   }
3061 
3062   log_debug("unqueue_mapview_update: needed_updates=%d",
3063             needed_updates);
3064 
3065   /* This code "pops" the lists of tile updates off of the static array and
3066    * stores them locally.  This allows further updates to be queued within
3067    * the function itself (namely, within update_map_canvas). */
3068   for (i = 0; i < TILE_UPDATE_COUNT; i++) {
3069     my_tile_updates[i] = tile_updates[i];
3070     tile_updates[i] = NULL;
3071   }
3072 
3073   if (map_exists()) {
3074     if ((needed_updates & UPDATE_MAP_CANVAS_VISIBLE)
3075 	|| (needed_updates & UPDATE_CITY_DESCRIPTIONS)
3076         || (needed_updates & UPDATE_TILE_LABELS)) {
3077       dirty_all();
3078       update_map_canvas(0, 0, mapview.store_width,
3079 			mapview.store_height);
3080       /* Have to update the overview too, since some tiles may have changed. */
3081       refresh_overview_canvas();
3082     } else {
3083       int min_x = mapview.width, min_y = mapview.height;
3084       int max_x = 0, max_y = 0;
3085 
3086       for (i = 0; i < TILE_UPDATE_COUNT; i++) {
3087         if (my_tile_updates[i]) {
3088           tile_list_iterate(my_tile_updates[i], ptile) {
3089             float xl, yt;
3090             int xr, yb;
3091 
3092 	    (void) tile_to_canvas_pos(&xl, &yt, ptile);
3093 
3094 	    xl += area[i].dx;
3095 	    yt += area[i].dy;
3096 	    xr = xl + area[i].w;
3097 	    yb = yt + area[i].h;
3098 
3099 	    if (xr > 0 && xl < mapview.width
3100 		&& yb > 0 && yt < mapview.height) {
3101 	      min_x = MIN(min_x, xl);
3102 	      min_y = MIN(min_y, yt);
3103 	      max_x = MAX(max_x, xr);
3104 	      max_y = MAX(max_y, yb);
3105 	    }
3106 
3107 	    /* FIXME: These overview updates should be batched as well.
3108 	     * Right now they account for as much as 90% of the runtime of
3109 	     * the unqueue. */
3110 	    overview_update_tile(ptile);
3111 	  } tile_list_iterate_end;
3112 	}
3113       }
3114 
3115       if (min_x < max_x && min_y < max_y) {
3116 	update_map_canvas(min_x, min_y, max_x - min_x, max_y - min_y);
3117       }
3118     }
3119   }
3120 
3121   for (i = 0; i < TILE_UPDATE_COUNT; i++) {
3122     if (my_tile_updates[i]) {
3123       tile_list_destroy(my_tile_updates[i]);
3124     }
3125   }
3126   needed_updates = UPDATE_NONE;
3127 
3128   if (write_to_screen) {
3129     flush_dirty();
3130     flush_dirty_overview();
3131   }
3132 }
3133 
3134 /**************************************************************************
3135   Fill the two buffers which information about the city which is shown
3136   below it. It does not take draw_city_names/draw_city_growth into account.
3137 **************************************************************************/
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)3138 void get_city_mapview_name_and_growth(struct city *pcity,
3139                                       char *name_buffer,
3140                                       size_t name_buffer_len,
3141                                       char *growth_buffer,
3142                                       size_t growth_buffer_len,
3143                                       enum color_std *growth_color,
3144                                       enum color_std *production_color)
3145 {
3146   fc_strlcpy(name_buffer, city_name_get(pcity), name_buffer_len);
3147 
3148   *production_color = COLOR_MAPVIEW_CITYTEXT;
3149   if (NULL == client.conn.playing
3150       || city_owner(pcity) == client.conn.playing) {
3151     int turns = city_turns_to_grow(pcity);
3152 
3153     if (turns == 0) {
3154       fc_snprintf(growth_buffer, growth_buffer_len, "X");
3155     } else if (turns == FC_INFINITY) {
3156       fc_snprintf(growth_buffer, growth_buffer_len, "-");
3157     } else {
3158       /* Negative turns means we're shrinking, but that's handled
3159          down below. */
3160       fc_snprintf(growth_buffer, growth_buffer_len, "%d", abs(turns));
3161     }
3162 
3163     if (turns <= 0) {
3164       /* A blocked or shrinking city has its growth status shown in red. */
3165       *growth_color = COLOR_MAPVIEW_CITYGROWTH_BLOCKED;
3166     } else {
3167       *growth_color = COLOR_MAPVIEW_CITYTEXT;
3168     }
3169 
3170     if (pcity->surplus[O_SHIELD] < 0) {
3171       *production_color = COLOR_MAPVIEW_CITYGROWTH_BLOCKED;
3172     }
3173   } else {
3174     growth_buffer[0] = '\0';
3175     *growth_color = COLOR_MAPVIEW_CITYTEXT;
3176   }
3177 }
3178 
3179 /**************************************************************************
3180   Returns TRUE if cached drawing is possible.  If the mapview is too large
3181   we have to turn it off.
3182 **************************************************************************/
can_do_cached_drawing(void)3183 static bool can_do_cached_drawing(void)
3184 {
3185   const int W = tileset_tile_width(tileset) * map_zoom;
3186   const int H = tileset_tile_height(tileset) * map_zoom;
3187   int w = mapview.store_width, h = mapview.store_height;
3188 
3189   /* If the mapview window is too large, cached drawing is not possible.
3190    *
3191    * BACKGROUND: cached drawing occurrs when the mapview is scrolled just
3192    * a short distance.  The majority of the mapview window can simply be
3193    * copied while the newly visible areas must be drawn from scratch.  This
3194    * speeds up drawing significantly, especially when using the scrollbars
3195    * or mapview sliding.
3196    *
3197    * When the mapview is larger than the map, however, some tiles may become
3198    * visible twice.  In this case one instance of the tile will be drawn
3199    * while all others are drawn black.  When this happens the cached drawing
3200    * system breaks since it assumes the mapview canvas is an "ideal" window
3201    * over the map.  So black tiles may be scrolled from the edges of the
3202    * mapview into the center, while drawn tiles may be scrolled from the
3203    * center of the mapview out to the edges.  The result is very bad.
3204    *
3205    * There are a few different ways this could be solved.  One way is simply
3206    * to turn off cached drawing, which is what we do now.  If the mapview
3207    * window gets to be too large, the caching is disabled.  Another would
3208    * be to prevent the window from getting too large in the first place -
3209    * but because the window boundaries aren't at an even tile this would
3210    * mean the entire map could never be shown.  Yet another way would be
3211    * to draw tiles more than once if they are visible in multiple locations
3212    * on the mapview.
3213    *
3214    * The logic below is complicated and determined in part by
3215    * trial-and-error. */
3216   if (!current_topo_has_flag(TF_WRAPX) && !current_topo_has_flag(TF_WRAPY)) {
3217     /* An unwrapping map: no limitation.  On an unwrapping map no tile can
3218      * be visible twice so there's no problem. */
3219     return TRUE;
3220   }
3221   if (XOR(current_topo_has_flag(TF_ISO) || current_topo_has_flag(TF_HEX),
3222 	  tileset_is_isometric(tileset))) {
3223     /* Non-matching.  In this case the mapview does not line up with the
3224      * map's axis of wrapping.  This will give very bad results for the
3225      * player!
3226      * We can never show more than half of the map.
3227      *
3228      * We divide by 4 below because we have to divide by 2 twice.  The
3229      * first division by 2 is because the square must be half the size
3230      * of the (width+height).  The second division by two is because for
3231      * an iso-map, NATURAL_XXX has a scale of 2, whereas for iso-view
3232      * NORMAL_TILE_XXX has a scale of 2. */
3233     return (w <= (NATURAL_WIDTH + NATURAL_HEIGHT) * W / 4
3234 	    && h <= (NATURAL_WIDTH + NATURAL_HEIGHT) * H / 4);
3235   } else {
3236     /* Matching. */
3237     const int isofactor = (tileset_is_isometric(tileset) ? 2 : 1);
3238     const int isodiff = (tileset_is_isometric(tileset) ? 6 : 2);
3239 
3240     /* Now we can use the full width and height, with the exception of a small
3241      * area on each side. */
3242     if (current_topo_has_flag(TF_WRAPX)
3243 	&& w > (NATURAL_WIDTH - isodiff) * W / isofactor) {
3244       return FALSE;
3245     }
3246     if (current_topo_has_flag(TF_WRAPY)
3247 	&& h > (NATURAL_HEIGHT - isodiff) * H / isofactor) {
3248       return FALSE;
3249     }
3250     return TRUE;
3251   }
3252 }
3253 
3254 /**************************************************************************
3255   Called when we receive map dimensions.  It initialized the mapview
3256   decorations.
3257 **************************************************************************/
mapdeco_init(void)3258 void mapdeco_init(void)
3259 {
3260   /* HACK: this must be called on a map_info packet. */
3261   mapview.can_do_cached_drawing = can_do_cached_drawing();
3262 
3263   mapdeco_free();
3264   mapdeco_highlight_table = tile_hash_new();
3265   mapdeco_crosshair_table = tile_hash_new();
3266   mapdeco_gotoline_table = gotoline_hash_new();
3267 }
3268 
3269 /**************************************************************************
3270   Free all memory used for map decorations.
3271 **************************************************************************/
mapdeco_free(void)3272 void mapdeco_free(void)
3273 {
3274   if (mapdeco_highlight_table) {
3275     tile_hash_destroy(mapdeco_highlight_table);
3276     mapdeco_highlight_table = NULL;
3277   }
3278   if (mapdeco_crosshair_table) {
3279     tile_hash_destroy(mapdeco_crosshair_table);
3280     mapdeco_crosshair_table = NULL;
3281   }
3282   if (mapdeco_gotoline_table) {
3283     gotoline_hash_destroy(mapdeco_gotoline_table);
3284     mapdeco_gotoline_table = NULL;
3285   }
3286 }
3287 
3288 /**************************************************************************
3289   Set the given tile's map decoration as either highlighted or not,
3290   depending on the value of 'highlight'.
3291 **************************************************************************/
mapdeco_set_highlight(const struct tile * ptile,bool highlight)3292 void mapdeco_set_highlight(const struct tile *ptile, bool highlight)
3293 {
3294   bool changed = FALSE;
3295   if (!ptile || !mapdeco_highlight_table) {
3296     return;
3297   }
3298 
3299   if (highlight) {
3300     changed = tile_hash_insert(mapdeco_highlight_table, ptile, NULL);
3301   } else {
3302     changed = tile_hash_remove(mapdeco_highlight_table, ptile);
3303   }
3304 
3305   if (changed) {
3306     /* FIXME: Remove the cast. */
3307     refresh_tile_mapcanvas((struct tile *) ptile, TRUE, FALSE);
3308   }
3309 }
3310 
3311 /**************************************************************************
3312   Return TRUE if the given tile is highlighted.
3313 **************************************************************************/
mapdeco_is_highlight_set(const struct tile * ptile)3314 bool mapdeco_is_highlight_set(const struct tile *ptile)
3315 {
3316   if (!ptile || !mapdeco_highlight_table) {
3317     return FALSE;
3318   }
3319   return tile_hash_lookup(mapdeco_highlight_table, ptile, NULL);
3320 }
3321 
3322 /**************************************************************************
3323   Clears all highlighting. Marks the previously highlighted tiles as
3324   needing a mapview update.
3325 **************************************************************************/
mapdeco_clear_highlights(void)3326 void mapdeco_clear_highlights(void)
3327 {
3328   if (!mapdeco_highlight_table) {
3329     return;
3330   }
3331 
3332   tile_hash_iterate(mapdeco_highlight_table, ptile) {
3333     refresh_tile_mapcanvas(ptile, TRUE, FALSE);
3334   } tile_hash_iterate_end;
3335 
3336   tile_hash_clear(mapdeco_highlight_table);
3337 }
3338 
3339 /**************************************************************************
3340   Marks the given tile as having a "crosshair" map decoration.
3341 **************************************************************************/
mapdeco_set_crosshair(const struct tile * ptile,bool crosshair)3342 void mapdeco_set_crosshair(const struct tile *ptile, bool crosshair)
3343 {
3344   bool changed;
3345 
3346   if (!mapdeco_crosshair_table || !ptile) {
3347     return;
3348   }
3349 
3350   if (crosshair) {
3351     changed = tile_hash_insert(mapdeco_crosshair_table, ptile, NULL);
3352   } else {
3353     changed = tile_hash_remove(mapdeco_crosshair_table, ptile);
3354   }
3355 
3356   if (changed) {
3357     /* FIXME: Remove the cast. */
3358     refresh_tile_mapcanvas((struct tile *) ptile, FALSE, FALSE);
3359   }
3360 }
3361 
3362 /**************************************************************************
3363   Returns TRUE if there is a "crosshair" decoration set at the given tile.
3364 **************************************************************************/
mapdeco_is_crosshair_set(const struct tile * ptile)3365 bool mapdeco_is_crosshair_set(const struct tile *ptile)
3366 {
3367   if (!mapdeco_crosshair_table || !ptile) {
3368     return FALSE;
3369   }
3370   return tile_hash_lookup(mapdeco_crosshair_table, ptile, NULL);
3371 }
3372 
3373 /**************************************************************************
3374   Clears all previous set tile crosshair decorations. Marks the affected
3375   tiles as needing a mapview update.
3376 **************************************************************************/
mapdeco_clear_crosshairs(void)3377 void mapdeco_clear_crosshairs(void)
3378 {
3379   if (!mapdeco_crosshair_table) {
3380     return;
3381   }
3382 
3383   tile_hash_iterate(mapdeco_crosshair_table, ptile) {
3384     refresh_tile_mapcanvas(ptile, FALSE, FALSE);
3385   } tile_hash_iterate_end;
3386 
3387   tile_hash_clear(mapdeco_crosshair_table);
3388 }
3389 
3390 /**************************************************************************
3391   Add a goto line from the given tile 'ptile' in the direction 'dir'. If
3392   there was no previously drawn line there, a mapview update is queued
3393   for the source and destination tiles.
3394 **************************************************************************/
mapdeco_add_gotoline(const struct tile * ptile,enum direction8 dir)3395 void mapdeco_add_gotoline(const struct tile *ptile, enum direction8 dir)
3396 {
3397   struct gotoline_counter *pglc;
3398   const struct tile *ptile_dest;
3399   bool changed;
3400 
3401   if (!mapdeco_gotoline_table || !ptile
3402       || !(0 <= dir && dir <= direction8_max())) {
3403     return;
3404   }
3405   ptile_dest = mapstep(ptile, dir);
3406   if (!ptile_dest) {
3407     return;
3408   }
3409 
3410   if (!gotoline_hash_lookup(mapdeco_gotoline_table, ptile, &pglc)) {
3411     pglc = gotoline_counter_new();
3412     gotoline_hash_insert(mapdeco_gotoline_table, ptile, pglc);
3413   }
3414   changed = (pglc->line_count[dir] < 1);
3415   pglc->line_count[dir]++;
3416 
3417   if (changed) {
3418     /* FIXME: Remove cast. */
3419     refresh_tile_mapcanvas((struct tile *) ptile, FALSE, FALSE);
3420     refresh_tile_mapcanvas((struct tile *) ptile_dest, FALSE, FALSE);
3421   }
3422 }
3423 
3424 /**************************************************************************
3425   Removes a goto line from the given tile 'ptile' going in the direction
3426   'dir'. If this was the last line there, a mapview update is queued to
3427   erase the drawn line.
3428 **************************************************************************/
mapdeco_remove_gotoline(const struct tile * ptile,enum direction8 dir)3429 void mapdeco_remove_gotoline(const struct tile *ptile,
3430                              enum direction8 dir)
3431 {
3432   struct gotoline_counter *pglc;
3433   bool changed = FALSE;
3434 
3435   if (!mapdeco_gotoline_table || !ptile
3436       || !(0 <= dir && dir <= direction8_max())) {
3437     return;
3438   }
3439 
3440   if (!gotoline_hash_lookup(mapdeco_gotoline_table, ptile, &pglc)) {
3441     return;
3442   }
3443 
3444   pglc->line_count[dir]--;
3445   if (pglc->line_count[dir] <= 0) {
3446     pglc->line_count[dir] = 0;
3447     changed = TRUE;
3448   }
3449 
3450   if (changed) {
3451     /* FIXME: Remove the casts. */
3452     refresh_tile_mapcanvas((struct tile *) ptile, FALSE, FALSE);
3453     ptile = mapstep(ptile, dir);
3454     if (ptile != NULL) {
3455       refresh_tile_mapcanvas((struct tile *) ptile, FALSE, FALSE);
3456     }
3457   }
3458 }
3459 
3460 /**************************************************************************
3461   Set the map decorations for the given unit's goto route. A goto route
3462   consists of one or more goto lines, with each line being from the center
3463   of one tile to the center of another tile.
3464 **************************************************************************/
mapdeco_set_gotoroute(const struct unit * punit)3465 void mapdeco_set_gotoroute(const struct unit *punit)
3466 {
3467   const struct unit_order *porder;
3468   const struct tile *ptile;
3469   int i, ind;
3470 
3471   if (!punit || !unit_tile(punit) || !unit_has_orders(punit)
3472       || punit->orders.length < 1) {
3473     return;
3474   }
3475 
3476   ptile = unit_tile(punit);
3477 
3478   for (i = 0; ptile != NULL && i < punit->orders.length; i++) {
3479     if (punit->orders.index + i >= punit->orders.length
3480         && !punit->orders.repeat) {
3481       break;
3482     }
3483 
3484     ind = (punit->orders.index + i) % punit->orders.length;
3485     porder = &punit->orders.list[ind];
3486     if (porder->order != ORDER_MOVE) {
3487       /* FIXME: should display some indication of non-move orders here. */
3488       continue;
3489     }
3490 
3491     mapdeco_add_gotoline(ptile, porder->dir);
3492     ptile = mapstep(ptile, porder->dir);
3493   }
3494 }
3495 
3496 /**************************************************************************
3497   Returns TRUE if a goto line should be drawn from the given tile in the
3498   given direction.
3499 **************************************************************************/
mapdeco_is_gotoline_set(const struct tile * ptile,enum direction8 dir)3500 bool mapdeco_is_gotoline_set(const struct tile *ptile,
3501                              enum direction8 dir)
3502 {
3503   struct gotoline_counter *pglc;
3504 
3505   if (!ptile || !(0 <= dir && dir <= direction8_max())
3506       || !mapdeco_gotoline_table) {
3507     return FALSE;
3508   }
3509 
3510   if (!gotoline_hash_lookup(mapdeco_gotoline_table, ptile, &pglc)) {
3511     return FALSE;
3512   }
3513 
3514   return pglc->line_count[dir] > 0;
3515 }
3516 
3517 /**************************************************************************
3518   Clear all goto line map decorations and queues mapview updates for the
3519   affected tiles.
3520 **************************************************************************/
mapdeco_clear_gotoroutes(void)3521 void mapdeco_clear_gotoroutes(void)
3522 {
3523   if (!mapdeco_gotoline_table) {
3524     return;
3525   }
3526 
3527   gotoline_hash_iterate(mapdeco_gotoline_table, ptile, pglc) {
3528     refresh_tile_mapcanvas(ptile, FALSE, FALSE);
3529     adjc_dir_iterate(ptile, ptile_dest, dir) {
3530       if (pglc->line_count[dir] > 0) {
3531         refresh_tile_mapcanvas(ptile_dest, FALSE, FALSE);
3532       }
3533     } adjc_dir_iterate_end;
3534   } gotoline_hash_iterate_end;
3535   gotoline_hash_clear(mapdeco_gotoline_table);
3536 }
3537 
3538 /**************************************************************************
3539   Called if the map in the GUI is resized.
3540 
3541   Returns TRUE iff the canvas was redrawn.
3542 **************************************************************************/
map_canvas_resized(int width,int height)3543 bool map_canvas_resized(int width, int height)
3544 {
3545   int old_tile_width = mapview.tile_width;
3546   int old_tile_height = mapview.tile_height;
3547   int old_width = mapview.width, old_height = mapview.height;
3548   int tile_width = (width + tileset_tile_width(tileset) * map_zoom - 1) /
3549     (tileset_tile_width(tileset) * map_zoom);
3550   int tile_height = (height + tileset_tile_height(tileset) * map_zoom - 1) /
3551     (tileset_tile_height(tileset) * map_zoom);
3552   int full_width = tile_width * tileset_tile_width(tileset) * map_zoom;
3553   int full_height = tile_height * tileset_tile_height(tileset) * map_zoom;
3554   bool tile_size_changed, size_changed, redrawn = FALSE;
3555 
3556   /* Resized */
3557 
3558   /* Since a resize is only triggered when the tile_*** changes, the canvas
3559    * width and height must include the entire backing store - otherwise
3560    * small resizings may lead to undrawn tiles. */
3561   mapview.tile_width = tile_width;
3562   mapview.tile_height = tile_height;
3563   mapview.width = width;
3564   mapview.height = height;
3565   mapview.store_width = full_width;
3566   mapview.store_height = full_height;
3567 
3568   /* Check for what's changed. */
3569   tile_size_changed = (tile_width != old_tile_width
3570  		       || tile_height != old_tile_height);
3571   size_changed = (width != old_width || height != old_height);
3572 
3573   /* If the tile size has changed, resize the canvas. */
3574   if (tile_size_changed) {
3575     if (mapview.store) {
3576       canvas_free(mapview.store);
3577       canvas_free(mapview.tmp_store);
3578     }
3579     mapview.store = canvas_create(full_width, full_height);
3580     canvas_set_zoom(mapview.store, map_zoom);
3581     canvas_mapview_init(mapview.store);
3582     canvas_put_rectangle(mapview.store,
3583                          get_color(tileset, COLOR_MAPVIEW_UNKNOWN),
3584                          0, 0, full_width / map_zoom, full_height / map_zoom);
3585 
3586     mapview.tmp_store = canvas_create(full_width, full_height);
3587     canvas_set_zoom(mapview.tmp_store, map_zoom);
3588     canvas_mapview_init(mapview.tmp_store);
3589   }
3590 
3591   if (map_exists() && can_client_change_view()) {
3592     if (tile_size_changed) {
3593       if (center_tile != NULL) {
3594         int x_left, y_top;
3595         float gui_x, gui_y;
3596 
3597         index_to_map_pos(&x_left, &y_top, tile_index(center_tile));
3598         map_to_gui_pos(tileset, &gui_x, &gui_y, x_left, y_top);
3599 
3600         /* Put the center pixel of the tile at the exact center of the mapview. */
3601         gui_x -= (mapview.width - tileset_tile_width(tileset) * map_zoom) / 2;
3602         gui_y -= (mapview.height - tileset_tile_height(tileset) * map_zoom) / 2;
3603 
3604         calc_mapview_origin(&gui_x, &gui_y);
3605         mapview.gui_x0 = gui_x;
3606         mapview.gui_y0 = gui_y;
3607       }
3608       update_map_canvas_visible();
3609       center_tile_overviewcanvas();
3610 
3611       /* Do not draw to the screen here as that could cause problems
3612        * when we are only initially setting up the view and some widgets
3613        * are not yet ready. */
3614       unqueue_mapview_updates(FALSE);
3615       redrawn = TRUE;
3616     }
3617 
3618     /* If the width/height has changed, update the scrollbars even if
3619      * the backing store is not resized. */
3620     if (size_changed) {
3621       update_map_canvas_scrollbars_size();
3622       update_map_canvas_scrollbars();
3623     }
3624   }
3625 
3626   mapview.can_do_cached_drawing = can_do_cached_drawing();
3627 
3628   return redrawn;
3629 }
3630 
3631 /**************************************************************************
3632   Sets up data for the mapview and overview.
3633 **************************************************************************/
init_mapcanvas_and_overview(void)3634 void init_mapcanvas_and_overview(void)
3635 {
3636   /* Create a dummy map to make sure mapview.store is never NULL. */
3637   map_canvas_resized(1, 1);
3638 }
3639 
3640 /**************************************************************************
3641   Frees resources allocated for mapview and overview
3642 **************************************************************************/
free_mapcanvas_and_overview(void)3643 void free_mapcanvas_and_overview(void)
3644 {
3645   canvas_free(mapview.store);
3646   canvas_free(mapview.tmp_store);
3647 }
3648 
3649 /****************************************************************************
3650   Return the desired width of the spaceship canvas.
3651 ****************************************************************************/
get_spaceship_dimensions(int * width,int * height)3652 void get_spaceship_dimensions(int *width, int *height)
3653 {
3654   struct sprite *sprite
3655     = get_spaceship_sprite(tileset, SPACESHIP_HABITATION);
3656 
3657   get_sprite_dimensions(sprite, width, height);
3658   *width *= 7;
3659   *height *= 7;
3660 }
3661 
3662 /****************************************************************************
3663   Draw the spaceship onto the canvas.
3664 ****************************************************************************/
put_spaceship(struct canvas * pcanvas,int canvas_x,int canvas_y,const struct player * pplayer)3665 void put_spaceship(struct canvas *pcanvas, int canvas_x, int canvas_y,
3666 		   const struct player *pplayer)
3667 {
3668   int i, x, y;
3669   const struct player_spaceship *ship = &pplayer->spaceship;
3670   int w, h;
3671   struct sprite *spr;
3672   struct tileset *t = tileset;
3673 
3674   spr = get_spaceship_sprite(t, SPACESHIP_HABITATION);
3675   get_sprite_dimensions(spr, &w, &h);
3676 
3677   canvas_put_rectangle(pcanvas,
3678                        get_color(tileset, COLOR_SPACESHIP_BACKGROUND),
3679                        0, 0, w * 7, h * 7);
3680 
3681   for (i = 0; i < NUM_SS_MODULES; i++) {
3682     const int j = i / 3;
3683     const int k = i % 3;
3684 
3685     if ((k == 0 && j >= ship->habitation)
3686 	|| (k == 1 && j >= ship->life_support)
3687 	|| (k == 2 && j >= ship->solar_panels)) {
3688       continue;
3689     }
3690     x = modules_info[i].x * w / 4 - w / 2;
3691     y = modules_info[i].y * h / 4 - h / 2;
3692 
3693     spr = (k == 0 ? get_spaceship_sprite(t, SPACESHIP_HABITATION)
3694            : k == 1 ? get_spaceship_sprite(t, SPACESHIP_LIFE_SUPPORT)
3695            : get_spaceship_sprite(t, SPACESHIP_SOLAR_PANEL));
3696     canvas_put_sprite_full(pcanvas, x, y, spr);
3697   }
3698 
3699   for (i = 0; i < NUM_SS_COMPONENTS; i++) {
3700     const int j = i / 2;
3701     const int k = i % 2;
3702 
3703     if ((k == 0 && j >= ship->fuel)
3704 	|| (k == 1 && j >= ship->propulsion)) {
3705       continue;
3706     }
3707     x = components_info[i].x * w / 4 - w / 2;
3708     y = components_info[i].y * h / 4 - h / 2;
3709 
3710     spr = ((k == 0) ? get_spaceship_sprite(t, SPACESHIP_FUEL)
3711            : get_spaceship_sprite(t, SPACESHIP_PROPULSION));
3712 
3713     canvas_put_sprite_full(pcanvas, x, y, spr);
3714 
3715     if (k && ship->state == SSHIP_LAUNCHED) {
3716       spr = get_spaceship_sprite(t, SPACESHIP_EXHAUST);
3717       canvas_put_sprite_full(pcanvas, x + w, y, spr);
3718     }
3719   }
3720 
3721   for (i = 0; i < NUM_SS_STRUCTURALS; i++) {
3722     if (!BV_ISSET(ship->structure, i)) {
3723       continue;
3724     }
3725     x = structurals_info[i].x * w / 4 - w / 2;
3726     y = structurals_info[i].y * h / 4 - h / 2;
3727 
3728     spr = get_spaceship_sprite(t, SPACESHIP_STRUCTURAL);
3729     canvas_put_sprite_full(pcanvas, x, y, spr);
3730   }
3731 }
3732 
3733 /****************************************************************************
3734   Map link mark module: it makes link marks when a link is sent by chating,
3735   or restore a mark with clicking a link on the chatline.
3736 ****************************************************************************/
3737 struct link_mark {
3738   enum text_link_type type;     /* The target type. */
3739   int id;                       /* The city or unit id, or tile index. */
3740   int turn_counter;             /* The turn counter before it disappears. */
3741 };
3742 
3743 #define SPECLIST_TAG link_mark
3744 #define SPECLIST_TYPE struct link_mark
3745 #include "speclist.h"
3746 #define link_marks_iterate(pmark) \
3747   TYPED_LIST_ITERATE(struct link_mark, link_marks, pmark)
3748 #define link_marks_iterate_end LIST_ITERATE_END
3749 
3750 static struct link_mark_list *link_marks = NULL;
3751 
3752 /**********************************************************************
3753   Find a link mark in the list.
3754 ***********************************************************************/
link_mark_find(enum text_link_type type,int id)3755 static struct link_mark *link_mark_find(enum text_link_type type, int id)
3756 {
3757   link_marks_iterate(pmark) {
3758     if (pmark->type == type && pmark->id == id) {
3759       return pmark;
3760     }
3761   } link_marks_iterate_end;
3762 
3763   return NULL;
3764 }
3765 
3766 /**********************************************************************
3767   Create a new link mark.
3768 ***********************************************************************/
link_mark_new(enum text_link_type type,int id,int turns)3769 static struct link_mark *link_mark_new(enum text_link_type type,
3770                                        int id, int turns)
3771 {
3772   struct link_mark *pmark = fc_malloc(sizeof(struct link_mark));
3773 
3774   pmark->type = type;
3775   pmark->id = id;
3776   pmark->turn_counter = turns;
3777 
3778   return pmark;
3779 }
3780 
3781 /**********************************************************************
3782   Remove a link mark.
3783 ***********************************************************************/
link_mark_destroy(struct link_mark * pmark)3784 static void link_mark_destroy(struct link_mark *pmark)
3785 {
3786   free(pmark);
3787 }
3788 
3789 /**********************************************************************
3790   Returns the location of the pointed mark.
3791 ***********************************************************************/
link_mark_tile(const struct link_mark * pmark)3792 static struct tile *link_mark_tile(const struct link_mark *pmark)
3793 {
3794   switch (pmark->type) {
3795   case TLT_CITY:
3796     {
3797       struct city *pcity = game_city_by_number(pmark->id);
3798       return pcity ? pcity->tile : NULL;
3799     }
3800   case TLT_TILE:
3801     return index_to_tile(pmark->id);
3802   case TLT_UNIT:
3803     {
3804       struct unit *punit = game_unit_by_number(pmark->id);
3805       return punit ? unit_tile(punit) : NULL;
3806     }
3807   }
3808   return NULL;
3809 }
3810 
3811 /**********************************************************************
3812   Returns the color of the pointed mark.
3813 ***********************************************************************/
link_mark_color(const struct link_mark * pmark)3814 static struct color *link_mark_color(const struct link_mark *pmark)
3815 {
3816   switch (pmark->type) {
3817   case TLT_CITY:
3818     return get_color(tileset, COLOR_MAPVIEW_CITY_LINK);
3819   case TLT_TILE:
3820     return get_color(tileset, COLOR_MAPVIEW_TILE_LINK);
3821   case TLT_UNIT:
3822     return get_color(tileset, COLOR_MAPVIEW_UNIT_LINK);
3823   }
3824   return NULL;
3825 }
3826 
3827 /**********************************************************************
3828   Print a link mark.
3829 ***********************************************************************/
link_mark_draw(const struct link_mark * pmark)3830 static void link_mark_draw(const struct link_mark *pmark)
3831 {
3832   int width = tileset_tile_width(tileset) * map_zoom;
3833   int height = tileset_tile_height(tileset) * map_zoom;
3834   int xd = width / 20, yd = height / 20;
3835   int xlen = width / 3, ylen = height / 3;
3836   float canvas_x, canvas_y;
3837   int x_left, x_right, y_top, y_bottom;
3838   struct tile *ptile = link_mark_tile(pmark);
3839   struct color *pcolor = link_mark_color(pmark);
3840 
3841   if (!ptile || !tile_to_canvas_pos(&canvas_x, &canvas_y, ptile)) {
3842     return;
3843   }
3844 
3845   x_left = canvas_x + xd;
3846   x_right = canvas_x + width - xd;
3847   y_top = canvas_y + yd;
3848   y_bottom = canvas_y + height - yd;
3849 
3850   /* XXX: canvas_put_line doesn't currently take map_zoom into account
3851    * itself, but it probably should? If so these will need adjusting */
3852   canvas_put_line(mapview.store, pcolor, LINE_TILE_FRAME, x_left, y_top, xlen, 0);
3853   canvas_put_line(mapview.store, pcolor, LINE_TILE_FRAME, x_left, y_top, 0, ylen);
3854 
3855   canvas_put_line(mapview.store, pcolor, LINE_TILE_FRAME, x_right, y_top, -xlen, 0);
3856   canvas_put_line(mapview.store, pcolor, LINE_TILE_FRAME, x_right, y_top, 0, ylen);
3857 
3858   canvas_put_line(mapview.store, pcolor, LINE_TILE_FRAME, x_left, y_bottom, xlen, 0);
3859   canvas_put_line(mapview.store, pcolor, LINE_TILE_FRAME, x_left, y_bottom, 0, -ylen);
3860 
3861   canvas_put_line(mapview.store, pcolor, LINE_TILE_FRAME, x_right, y_bottom, -xlen, 0);
3862   canvas_put_line(mapview.store, pcolor, LINE_TILE_FRAME, x_right, y_bottom, 0, -ylen);
3863 }
3864 
3865 /**********************************************************************
3866   Initialize the link marks.
3867 ***********************************************************************/
link_marks_init(void)3868 void link_marks_init(void)
3869 {
3870   if (link_marks) {
3871     link_marks_free();
3872   }
3873 
3874   link_marks = link_mark_list_new_full(link_mark_destroy);
3875 }
3876 
3877 /**********************************************************************
3878   Free the link marks.
3879 ***********************************************************************/
link_marks_free(void)3880 void link_marks_free(void)
3881 {
3882   if (!link_marks) {
3883     return;
3884   }
3885 
3886   link_mark_list_destroy(link_marks);
3887   link_marks = NULL;
3888 }
3889 
3890 /**********************************************************************
3891   Draw all link marks.
3892 ***********************************************************************/
link_marks_draw_all(void)3893 void link_marks_draw_all(void)
3894 {
3895   link_marks_iterate(pmark) {
3896     link_mark_draw(pmark);
3897   } link_marks_iterate_end;
3898 }
3899 
3900 /**********************************************************************
3901   Clear all visible links.
3902 ***********************************************************************/
link_marks_clear_all(void)3903 void link_marks_clear_all(void)
3904 {
3905   link_mark_list_clear(link_marks);
3906   update_map_canvas_visible();
3907 }
3908 
3909 /**********************************************************************
3910   Clear all visible links.
3911 ***********************************************************************/
link_marks_decrease_turn_counters(void)3912 void link_marks_decrease_turn_counters(void)
3913 {
3914   link_marks_iterate(pmark) {
3915     if (--pmark->turn_counter <= 0) {
3916       link_mark_list_remove(link_marks, pmark);
3917     }
3918   } link_marks_iterate_end;
3919 
3920   /* update_map_canvas_visible(); not needed here. */
3921 }
3922 
3923 /**********************************************************************
3924   Add a visible link for 2 turns.
3925 ***********************************************************************/
link_mark_add_new(enum text_link_type type,int id)3926 void link_mark_add_new(enum text_link_type type, int id)
3927 {
3928   struct link_mark *pmark = link_mark_find(type, id);
3929   struct tile *ptile;
3930 
3931   if (pmark) {
3932     /* Already displayed, but maybe increase the turn counter. */
3933     pmark->turn_counter = MAX(pmark->turn_counter, 2);
3934     return;
3935   }
3936 
3937   pmark = link_mark_new(type, id, 2);
3938   link_mark_list_append(link_marks, pmark);
3939   ptile = link_mark_tile(pmark);
3940   if (ptile && tile_visible_mapcanvas(ptile)) {
3941     refresh_tile_mapcanvas(ptile, FALSE, FALSE);
3942   }
3943 }
3944 
3945 /**********************************************************************
3946   Add a visible link for 1 turn.
3947 ***********************************************************************/
link_mark_restore(enum text_link_type type,int id)3948 void link_mark_restore(enum text_link_type type, int id)
3949 {
3950   struct link_mark *pmark;
3951   struct tile *ptile;
3952 
3953   if (link_mark_find(type, id)) {
3954     return;
3955   }
3956 
3957   pmark = link_mark_new(type, id, 1);
3958   link_mark_list_append(link_marks, pmark);
3959   ptile = link_mark_tile(pmark);
3960   if (ptile && tile_visible_mapcanvas(ptile)) {
3961     refresh_tile_mapcanvas(ptile, FALSE, FALSE);
3962   }
3963 }
3964 
3965 /**********************************************************************
3966   Are the topology and tileset compatible?
3967   Tileset topology written to tset_topo, if not NULL.
3968 ***********************************************************************/
tileset_map_topo_compatible(int topology_id,struct tileset * tset,int * tset_topo)3969 enum topo_comp_lvl tileset_map_topo_compatible(int topology_id,
3970                                                struct tileset *tset,
3971                                                int *tset_topo)
3972 {
3973   int tileset_topology;
3974 
3975   if (tileset_hex_width(tset) > 0) {
3976     fc_assert(tileset_is_isometric(tset));
3977     tileset_topology = TF_HEX | TF_ISO;
3978   } else if (tileset_hex_height(tset) > 0) {
3979     fc_assert(tileset_is_isometric(tset));
3980     tileset_topology = TF_HEX;
3981   } else if (tileset_is_isometric(tset)) {
3982     tileset_topology = TF_ISO;
3983   } else {
3984     tileset_topology = 0;
3985   }
3986 
3987   if (tset_topo != NULL) {
3988     *tset_topo = tileset_topology;
3989   }
3990 
3991   if (tileset_topology & TF_HEX) {
3992     if ((topology_id & (TF_HEX | TF_ISO)) == tileset_topology) {
3993       return TOPO_COMPATIBLE;
3994     }
3995 
3996     /* Hex topology must match for both hexness and iso/non-iso */
3997     return TOPO_INCOMP_HARD;
3998   }
3999 
4000   if (topology_id & TF_HEX) {
4001     return TOPO_INCOMP_HARD;
4002   }
4003 
4004   if ((topology_id & TF_ISO) != (tileset_topology & TF_ISO)) {
4005     /* Non-hex iso/non-iso incompatibility is a soft one */
4006     return TOPO_INCOMP_SOFT;
4007   }
4008 
4009   return TOPO_COMPATIBLE;
4010 }
4011 
4012 /**********************************************************************
4013   Return string describing topology id.
4014 ***********************************************************************/
describe_topology(int topo)4015 const char *describe_topology(int topo)
4016 {
4017   if (topo & TF_ISO) {
4018     if (topo & TF_HEX) {
4019       return _("ISO|Hex");
4020     }
4021     return _("ISO");
4022   }
4023   if (topo & TF_HEX) {
4024     return _("Hex");
4025   }
4026 
4027   return _("Overhead");
4028 }
4029 
4030 /**********************************************************************
4031   Set frame by frame animation mode on.
4032 ***********************************************************************/
set_frame_by_frame_animation(void)4033 void set_frame_by_frame_animation(void)
4034 {
4035   frame_by_frame_animation = TRUE;
4036 }
4037