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(¢er_map_x, ¢er_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