Replaced typo that caused unnecessary regeneration of fc_gitrev_gen.h
[freeciv.git] / client / mapctrl_common.c
blobf28b058e56bf28cfc33614d7d2b32ab9285b29ed
1 /***********************************************************************
2 Freeciv - Copyright (C) 2002 - The Freeciv Project
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.
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 ***********************************************************************/
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
18 #include <stdlib.h> /* qsort */
20 /* utility */
21 #include "fcintl.h"
22 #include "log.h"
23 #include "support.h"
25 /* common */
26 #include "combat.h"
27 #include "game.h"
28 #include "unitlist.h"
30 /* client */
31 #include "agents.h"
32 #include "chatline_common.h"
33 #include "cityrep_g.h"
34 #include "client_main.h"
35 #include "climisc.h"
36 #include "cma_core.h"
37 #include "control.h"
38 #include "editor.h"
39 #include "fcintl.h"
40 #include "goto.h"
41 #include "mapctrl_common.h"
42 #include "mapctrl_g.h"
43 #include "mapview_g.h"
44 #include "options.h"
45 #include "overview_common.h"
46 #include "tilespec.h"
48 /* Selection Rectangle */
49 static float rec_anchor_x, rec_anchor_y; /* canvas coordinates for anchor */
50 static struct tile *rec_canvas_center_tile;
51 static int rec_corner_x, rec_corner_y; /* corner to iterate from */
52 static int rec_w, rec_h; /* width, heigth in pixels */
54 bool rbutton_down = FALSE;
55 bool rectangle_active = FALSE;
57 /* This changes the behaviour of left mouse
58 button in Area Selection mode. */
59 bool tiles_hilited_cities = FALSE;
61 /* The mapcanvas clipboard */
62 struct universal clipboard =
63 { .kind = VUT_NONE,
64 .value = {.building = NULL}
67 /* Goto with drag and drop. */
68 bool keyboardless_goto_button_down = FALSE;
69 bool keyboardless_goto_active = FALSE;
70 struct tile *keyboardless_goto_start_tile;
72 /* Update the workers for a city on the map, when the update is received */
73 struct city *city_workers_display = NULL;
75 /*************************************************************************/
77 static void clipboard_send_production_packet(struct city *pcity);
78 static void define_tiles_within_rectangle(bool append);
80 /**************************************************************************
81 Called when Right Mouse Button is depressed. Record the canvas
82 coordinates of the center of the tile, which may be unreal. This
83 anchor is not the drawing start point, but is used to calculate
84 width, height. Also record the current mapview centering.
85 **************************************************************************/
86 void anchor_selection_rectangle(int canvas_x, int canvas_y)
88 struct tile *ptile = canvas_pos_to_nearest_tile(canvas_x, canvas_y);
90 tile_to_canvas_pos(&rec_anchor_x, &rec_anchor_y, ptile);
91 rec_anchor_x += tileset_tile_width(tileset) / 2;
92 rec_anchor_y += tileset_tile_height(tileset) / 2;
93 /* FIXME: This may be off-by-one. */
94 rec_canvas_center_tile = get_center_tile_mapcanvas();
95 rec_w = rec_h = 0;
98 /**************************************************************************
99 Iterate over the pixel boundaries of the rectangle and pick the tiles
100 whose center falls within. Axis pixel incrementation is half tile size to
101 accomodate tilesets with varying tile shapes and proportions of X/Y.
103 These operations are performed on the tiles:
104 - Make tiles that contain owned cities hilited
105 on the map and hilited in the City List Window.
107 Later, I'll want to add unit hiliting for mass orders. -ali
109 NB: At the end of this function the current selection rectangle will be
110 erased (by being redrawn).
111 **************************************************************************/
112 static void define_tiles_within_rectangle(bool append)
114 const int W = tileset_tile_width(tileset), half_W = W / 2;
115 const int H = tileset_tile_height(tileset), half_H = H / 2;
116 const int segments_x = abs(rec_w / half_W);
117 const int segments_y = abs(rec_h / half_H);
119 /* Iteration direction */
120 const int inc_x = (rec_w > 0 ? half_W : -half_W);
121 const int inc_y = (rec_h > 0 ? half_H : -half_H);
122 int x, y, xx, yy;
123 float x2, y2;
124 struct unit_list *units = unit_list_new();
125 const struct city *pcity;
126 bool found_any_cities = FALSE;
128 if (!append) {
129 cancel_tile_hiliting();
132 y = rec_corner_y;
133 for (yy = 0; yy <= segments_y; yy++, y += inc_y) {
134 x = rec_corner_x;
135 for (xx = 0; xx <= segments_x; xx++, x += inc_x) {
136 struct tile *ptile;
138 /* For diamond shaped tiles, every other row is indented.
140 if ((yy % 2 ^ xx % 2) != 0) {
141 continue;
144 ptile = canvas_pos_to_tile(x, y);
145 if (!ptile) {
146 continue;
149 /* "Half-tile" indentation must match, or we'll process
150 * some tiles twice in the case of rectangular shape tiles.
152 tile_to_canvas_pos(&x2, &y2, ptile);
154 if ((yy % 2) != 0 && ((rec_corner_x % W) ^ abs((int)x2 % W)) != 0) {
155 continue;
158 /* Tile passed all tests; process it.
160 pcity = tile_city(ptile);
161 if (pcity != NULL && city_owner(pcity) == client_player()) {
162 mapdeco_set_highlight(ptile, TRUE);
163 found_any_cities = tiles_hilited_cities = TRUE;
165 unit_list_iterate(ptile->units, punit) {
166 if (unit_owner(punit) == client.conn.playing) {
167 unit_list_append(units, punit);
169 } unit_list_iterate_end;
173 if (!(gui_options.separate_unit_selection && found_any_cities)
174 && unit_list_size(units) > 0) {
175 if (!append) {
176 struct unit *punit = unit_list_get(units, 0);
178 unit_focus_set(punit);
179 unit_list_remove(units, punit);
181 unit_list_iterate(units, punit) {
182 unit_focus_add(punit);
183 } unit_list_iterate_end;
185 unit_list_destroy(units);
187 /* Clear previous rectangle. */
188 draw_selection_rectangle(rec_corner_x, rec_corner_y, rec_w, rec_h);
190 /* Hilite in City List Window */
191 if (tiles_hilited_cities) {
192 hilite_cities_from_canvas(); /* cityrep.c */
196 /**************************************************************************
197 Called when mouse pointer moves and rectangle is active.
198 **************************************************************************/
199 void update_selection_rectangle(float canvas_x, float canvas_y)
201 const int W = tileset_tile_width(tileset), half_W = W / 2;
202 const int H = tileset_tile_height(tileset), half_H = H / 2;
203 static struct tile *rec_tile = NULL;
204 int diff_x, diff_y;
205 struct tile *center_tile;
206 struct tile *ptile;
208 ptile = canvas_pos_to_nearest_tile(canvas_x, canvas_y);
210 /* Did mouse pointer move beyond the current tile's
211 * boundaries? Avoid macros; tile may be unreal!
213 if (ptile == rec_tile) {
214 return;
216 rec_tile = ptile;
218 /* Clear previous rectangle. */
219 draw_selection_rectangle(rec_corner_x, rec_corner_y, rec_w, rec_h);
221 /* Fix canvas coords to the center of the tile.
223 tile_to_canvas_pos(&canvas_x, &canvas_y, ptile);
224 canvas_x += half_W;
225 canvas_y += half_H;
227 rec_w = rec_anchor_x - canvas_x; /* width */
228 rec_h = rec_anchor_y - canvas_y; /* height */
230 /* FIXME: This may be off-by-one. */
231 center_tile = get_center_tile_mapcanvas();
232 map_distance_vector(&diff_x, &diff_y, center_tile, rec_canvas_center_tile);
234 /* Adjust width, height if mapview has recentered.
236 if (diff_x != 0 || diff_y != 0) {
238 if (tileset_is_isometric(tileset)) {
239 rec_w += (diff_x - diff_y) * half_W;
240 rec_h += (diff_x + diff_y) * half_H;
242 /* Iso wrapping */
243 if (abs(rec_w) > game.map.xsize * half_W / 2) {
244 int wx = game.map.xsize * half_W, wy = game.map.xsize * half_H;
246 rec_w > 0 ? (rec_w -= wx, rec_h -= wy) : (rec_w += wx, rec_h += wy);
249 } else {
250 rec_w += diff_x * W;
251 rec_h += diff_y * H;
253 /* X wrapping */
254 if (abs(rec_w) > game.map.xsize * half_W) {
255 int wx = game.map.xsize * W;
257 rec_w > 0 ? (rec_w -= wx) : (rec_w += wx);
262 if (rec_w == 0 && rec_h == 0) {
263 rectangle_active = FALSE;
264 return;
267 /* It is currently drawn only to the screen, not backing store */
268 rectangle_active = TRUE;
269 draw_selection_rectangle(canvas_x, canvas_y, rec_w, rec_h);
270 rec_corner_x = canvas_x;
271 rec_corner_y = canvas_y;
274 /**************************************************************************
275 Redraws the selection rectangle after a map flush.
276 **************************************************************************/
277 void redraw_selection_rectangle(void)
279 if (rectangle_active) {
280 draw_selection_rectangle(rec_corner_x, rec_corner_y, rec_w, rec_h);
284 /**************************************************************************
285 Redraws the selection rectangle after a map flush.
286 **************************************************************************/
287 void cancel_selection_rectangle(void)
289 if (rectangle_active) {
290 rectangle_active = FALSE;
291 rbutton_down = FALSE;
293 /* Erase the previously drawn selection rectangle. */
294 draw_selection_rectangle(rec_corner_x, rec_corner_y, rec_w, rec_h);
298 /**************************************************************************
299 Is city highlighted
300 **************************************************************************/
301 bool is_city_hilited(struct city *pcity)
303 return pcity && mapdeco_is_highlight_set(city_tile(pcity));
306 /**************************************************************************
307 Remove hiliting from all tiles, but not from rows in the City List window.
308 **************************************************************************/
309 void cancel_tile_hiliting(void)
311 if (tiles_hilited_cities) {
312 tiles_hilited_cities = FALSE;
313 mapdeco_clear_highlights();
317 /**************************************************************************
318 Action depends on whether the mouse pointer moved
319 a tile between press and release.
320 **************************************************************************/
321 void release_right_button(int canvas_x, int canvas_y, bool shift)
323 if (rectangle_active) {
324 define_tiles_within_rectangle(shift);
325 } else {
326 cancel_tile_hiliting();
327 recenter_button_pressed(canvas_x, canvas_y);
329 rectangle_active = FALSE;
330 rbutton_down = FALSE;
333 /**************************************************************************
334 Left Mouse Button in Area Selection mode.
335 **************************************************************************/
336 void toggle_tile_hilite(struct tile *ptile)
338 struct city *pcity = tile_city(ptile);
340 if (mapdeco_is_highlight_set(ptile)) {
341 mapdeco_set_highlight(ptile, FALSE);
342 if (pcity) {
343 toggle_city_hilite(pcity, FALSE); /* cityrep.c */
346 else if (NULL != pcity && city_owner(pcity) == client.conn.playing) {
347 mapdeco_set_highlight(ptile, TRUE);
348 tiles_hilited_cities = TRUE;
349 toggle_city_hilite(pcity, TRUE);
351 else {
352 return;
356 /**************************************************************************
357 The user pressed the overlay-city button (t) while the mouse was at the
358 given canvas position.
359 **************************************************************************/
360 void key_city_overlay(int canvas_x, int canvas_y)
362 struct tile *ptile = canvas_pos_to_tile(canvas_x, canvas_y);
364 if (can_client_change_view() && ptile) {
365 struct unit *punit;
366 struct city *pcity = find_city_or_settler_near_tile(ptile, &punit);
368 if (pcity) {
369 toggle_city_color(pcity);
370 } else if (punit) {
371 toggle_unit_color(punit);
376 /**************************************************************************
377 Shift-Left-Click on owned city or any visible unit to copy.
378 Returns whether it found anything to try to copy.
379 **************************************************************************/
380 bool clipboard_copy_production(struct tile *ptile)
382 char buffer[256];
383 struct city *pcity = tile_city(ptile);
385 if (!can_client_issue_orders()) {
386 return FALSE;
389 if (pcity) {
390 if (city_owner(pcity) != client.conn.playing) {
391 return FALSE;
393 clipboard = pcity->production;
394 } else {
395 struct unit *punit = find_visible_unit(ptile);
396 if (!punit) {
397 return FALSE;
399 if (!can_player_build_unit_direct(client.conn.playing,
400 unit_type_get(punit))) {
401 create_event(ptile, E_BAD_COMMAND, ftc_client,
402 _("You don't know how to build %s!"),
403 unit_name_translation(punit));
404 return TRUE;
406 clipboard.kind = VUT_UTYPE;
407 clipboard.value.utype = unit_type_get(punit);
409 upgrade_canvas_clipboard();
411 create_event(ptile, E_CITY_PRODUCTION_CHANGED, /* ? */
412 ftc_client, _("Copy %s to clipboard."),
413 universal_name_translation(&clipboard, buffer, sizeof(buffer)));
414 return TRUE;
417 /**************************************************************************
418 If City tiles are hilited, paste into all those cities.
419 Otherwise paste into the one city under the mouse pointer.
420 **************************************************************************/
421 void clipboard_paste_production(struct city *pcity)
423 if (!can_client_issue_orders()) {
424 return;
426 if (NULL == clipboard.value.building) {
427 create_event(city_tile(pcity), E_BAD_COMMAND, ftc_client,
428 _("Clipboard is empty."));
429 return;
431 if (!tiles_hilited_cities) {
432 if (NULL != pcity && city_owner(pcity) == client.conn.playing) {
433 clipboard_send_production_packet(pcity);
435 return;
437 else {
438 connection_do_buffer(&client.conn);
439 city_list_iterate(client.conn.playing->cities, hilicity) {
440 if (is_city_hilited(hilicity)) {
441 clipboard_send_production_packet(hilicity);
443 } city_list_iterate_end;
444 connection_do_unbuffer(&client.conn);
448 /**************************************************************************
449 Send request to build production in clipboard to server.
450 **************************************************************************/
451 static void clipboard_send_production_packet(struct city *pcity)
453 if (are_universals_equal(&pcity->production, &clipboard)
454 || !can_city_build_now(pcity, &clipboard)) {
455 return;
458 dsend_packet_city_change(&client.conn, pcity->id,
459 clipboard.kind,
460 universal_number(&clipboard));
463 /**************************************************************************
464 A newer technology may be available for units.
465 Also called from packhand.c.
466 **************************************************************************/
467 void upgrade_canvas_clipboard(void)
469 if (!can_client_issue_orders()) {
470 return;
472 if (VUT_UTYPE == clipboard.kind) {
473 struct unit_type *u =
474 can_upgrade_unittype(client.conn.playing, clipboard.value.utype);
476 if (u) {
477 clipboard.value.utype = u;
482 /**************************************************************************
483 Goto button has been released. Finish goto.
484 **************************************************************************/
485 void release_goto_button(int canvas_x, int canvas_y)
487 struct tile *ptile = canvas_pos_to_tile(canvas_x, canvas_y);
489 if (keyboardless_goto_active && hover_state == HOVER_GOTO && ptile) {
490 do_unit_goto(ptile);
491 set_hover_state(NULL, HOVER_NONE, ACTIVITY_LAST, NULL, ORDER_LAST);
492 update_unit_info_label(get_units_in_focus());
494 keyboardless_goto_active = FALSE;
495 keyboardless_goto_button_down = FALSE;
496 keyboardless_goto_start_tile = NULL;
499 /**************************************************************************
500 The goto hover state is only activated when the mouse pointer moves
501 beyond the tile where the button was depressed, to avoid mouse typos.
502 **************************************************************************/
503 void maybe_activate_keyboardless_goto(int canvas_x, int canvas_y)
505 struct tile *ptile = canvas_pos_to_tile(canvas_x, canvas_y);
507 if (ptile && get_num_units_in_focus() > 0
508 && !same_pos(keyboardless_goto_start_tile, ptile)
509 && can_client_issue_orders()) {
510 keyboardless_goto_active = TRUE;
511 request_unit_goto(ORDER_LAST);
515 /**************************************************************************
516 Return TRUE iff the turn done button should be enabled.
517 **************************************************************************/
518 bool get_turn_done_button_state(void)
520 return (can_client_issue_orders()
521 && !client.conn.playing->ai_controlled
522 && client.conn.playing->is_alive
523 && !client.conn.playing->phase_done
524 && !is_server_busy()
525 && is_player_phase(client.conn.playing, game.info.phase)
526 && !agents_busy());
529 /**************************************************************************
530 Scroll the mapview half a screen in the given direction. This is a GUI
531 direction; i.e., DIR8_NORTH is "up" on the mapview.
532 **************************************************************************/
533 void scroll_mapview(enum direction8 gui_dir)
535 int gui_x = mapview.gui_x0, gui_y = mapview.gui_y0;
537 if (!can_client_change_view()) {
538 return;
541 gui_x += DIR_DX[gui_dir] * mapview.width / 2;
542 gui_y += DIR_DY[gui_dir] * mapview.height / 2;
543 set_mapview_origin(gui_x, gui_y);
546 /**************************************************************************
547 Do some appropriate action when the "main" mouse button (usually
548 left-click) is pressed. For more sophisticated user control use (or
549 write) a different xxx_button_pressed function.
550 **************************************************************************/
551 void action_button_pressed(int canvas_x, int canvas_y,
552 enum quickselect_type qtype)
554 struct tile *ptile = canvas_pos_to_tile(canvas_x, canvas_y);
556 if (can_client_change_view() && ptile) {
557 /* FIXME: Some actions here will need to check can_client_issue_orders.
558 * But all we can check is the lowest common requirement. */
559 do_map_click(ptile, qtype);
563 /**************************************************************************
564 Wakeup sentried units on the tile of the specified location.
565 **************************************************************************/
566 void wakeup_button_pressed(int canvas_x, int canvas_y)
568 struct tile *ptile = canvas_pos_to_tile(canvas_x, canvas_y);
570 if (can_client_issue_orders() && ptile) {
571 wakeup_sentried_units(ptile);
575 /**************************************************************************
576 Adjust the position of city workers from the mapview.
577 **************************************************************************/
578 void adjust_workers_button_pressed(int canvas_x, int canvas_y)
580 struct tile *ptile = canvas_pos_to_tile(canvas_x, canvas_y);
582 if (NULL != ptile && can_client_issue_orders()) {
583 struct city *pcity = find_city_near_tile(ptile);
585 if (pcity && !cma_is_city_under_agent(pcity, NULL)) {
586 int city_x, city_y;
588 fc_assert_ret(city_base_to_city_map(&city_x, &city_y, pcity, ptile));
590 if (NULL != tile_worked(ptile) && tile_worked(ptile) == pcity) {
591 dsend_packet_city_make_specialist(&client.conn, pcity->id,
592 city_x, city_y);
593 } else if (city_can_work_tile(pcity, ptile)) {
594 dsend_packet_city_make_worker(&client.conn, pcity->id,
595 city_x, city_y);
596 } else {
597 return;
600 /* When the city info packet is received, update the workers on the
601 * map. This is a bad hack used to selectively update the mapview
602 * when we receive the corresponding city packet. */
603 city_workers_display = pcity;
608 /**************************************************************************
609 Recenter the map on the canvas location, on user request. Usually this
610 is done with a right-click.
611 **************************************************************************/
612 void recenter_button_pressed(int canvas_x, int canvas_y)
614 /* We use the "nearest" tile here so off-map clicks will still work. */
615 struct tile *ptile = canvas_pos_to_nearest_tile(canvas_x, canvas_y);
617 if (can_client_change_view() && ptile) {
618 center_tile_mapcanvas(ptile);
622 /**************************************************************************
623 Update the turn done button state.
624 **************************************************************************/
625 void update_turn_done_button_state(void)
627 bool turn_done_state = get_turn_done_button_state();
629 set_turn_done_button_state(turn_done_state);
631 if (turn_done_state) {
632 if (waiting_for_end_turn
633 || (NULL != client.conn.playing
634 && client.conn.playing->ai_controlled
635 && !gui_options.ai_manual_turn_done)) {
636 send_turn_done();
637 } else {
638 update_turn_done_button(TRUE);
643 /**************************************************************************
644 Update the goto/patrol line to the given map canvas location.
645 **************************************************************************/
646 void update_line(int canvas_x, int canvas_y)
648 struct tile *ptile;
650 switch (hover_state) {
651 case HOVER_GOTO:
652 case HOVER_PATROL:
653 case HOVER_CONNECT:
654 case HOVER_NUKE:
655 ptile = canvas_pos_to_tile(canvas_x, canvas_y);
657 is_valid_goto_draw_line(ptile);
658 case HOVER_NONE:
659 case HOVER_PARADROP:
660 case HOVER_ACT_SEL_TGT:
661 break;
665 /****************************************************************************
666 Update the goto/patrol line to the given overview canvas location.
667 ****************************************************************************/
668 void overview_update_line(int overview_x, int overview_y)
670 struct tile *ptile;
671 int x, y;
673 switch (hover_state) {
674 case HOVER_GOTO:
675 case HOVER_PATROL:
676 case HOVER_CONNECT:
677 case HOVER_NUKE:
678 overview_to_map_pos(&x, &y, overview_x, overview_y);
679 ptile = map_pos_to_tile(x, y);
681 is_valid_goto_draw_line(ptile);
682 case HOVER_NONE:
683 case HOVER_PARADROP:
684 case HOVER_ACT_SEL_TGT:
685 break;
689 /****************************************************************************
690 We sort according to the following logic:
692 - Transported units should immediately follow their transporter (note that
693 transporting may be recursive).
694 - Otherwise we sort by ID (which is what the list is originally sorted by).
695 ****************************************************************************/
696 static int unit_list_compare(const void *a, const void *b)
698 const struct unit *punit1 = *(struct unit **)a;
699 const struct unit *punit2 = *(struct unit **)b;
701 if (unit_transport_get(punit1) == unit_transport_get(punit2)) {
702 /* For units with the same transporter or no transporter: sort by id. */
703 /* Perhaps we should sort by name instead? */
704 return punit1->id - punit2->id;
705 } else if (unit_transport_get(punit1) == punit2) {
706 return 1;
707 } else if (unit_transport_get(punit2) == punit1) {
708 return -1;
709 } else {
710 /* If the transporters aren't the same, put in order by the
711 * transporters. */
712 const struct unit *ptrans1 = unit_transport_get(punit1);
713 const struct unit *ptrans2 = unit_transport_get(punit2);
715 if (!ptrans1) {
716 ptrans1 = punit1;
718 if (!ptrans2) {
719 ptrans2 = punit2;
722 return unit_list_compare(&ptrans1, &ptrans2);
726 /****************************************************************************
727 Fill and sort the list of units on the tile.
728 ****************************************************************************/
729 void fill_tile_unit_list(const struct tile *ptile, struct unit **unit_list)
731 int i = 0;
733 /* First populate the unit list. */
734 unit_list_iterate(ptile->units, punit) {
735 unit_list[i] = punit;
736 i++;
737 } unit_list_iterate_end;
739 /* Then sort it. */
740 qsort(unit_list, i, sizeof(*unit_list), unit_list_compare);