Stop sharing requirement_unit_state_ereq().
[freeciv.git] / client / mapctrl_common.c
blob1a1c8a12183da1c8ee5eb7deea61bd5cd0fbccd9
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) > wld.map.xsize * half_W / 2) {
244 int wx = wld.map.xsize * half_W, wy = wld.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) > wld.map.xsize * half_W) {
255 int wx = wld.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,
492 ACTIVITY_LAST, NULL,
493 EXTRA_NONE, ACTION_NONE, ORDER_LAST);
494 update_unit_info_label(get_units_in_focus());
496 keyboardless_goto_active = FALSE;
497 keyboardless_goto_button_down = FALSE;
498 keyboardless_goto_start_tile = NULL;
501 /**************************************************************************
502 The goto hover state is only activated when the mouse pointer moves
503 beyond the tile where the button was depressed, to avoid mouse typos.
504 **************************************************************************/
505 void maybe_activate_keyboardless_goto(int canvas_x, int canvas_y)
507 struct tile *ptile = canvas_pos_to_tile(canvas_x, canvas_y);
509 if (ptile && get_num_units_in_focus() > 0
510 && !same_pos(keyboardless_goto_start_tile, ptile)
511 && can_client_issue_orders()) {
512 keyboardless_goto_active = TRUE;
513 request_unit_goto(ORDER_LAST, ACTION_NONE, EXTRA_NONE);
517 /**************************************************************************
518 Return TRUE iff the turn done button should be enabled.
519 **************************************************************************/
520 bool get_turn_done_button_state(void)
522 return (can_client_issue_orders()
523 && is_human(client.conn.playing)
524 && client.conn.playing->is_alive
525 && !client.conn.playing->phase_done
526 && !is_server_busy()
527 && is_player_phase(client.conn.playing, game.info.phase)
528 && !agents_busy());
531 /**************************************************************************
532 Scroll the mapview half a screen in the given direction. This is a GUI
533 direction; i.e., DIR8_NORTH is "up" on the mapview.
534 **************************************************************************/
535 void scroll_mapview(enum direction8 gui_dir)
537 int gui_x = mapview.gui_x0, gui_y = mapview.gui_y0;
539 if (!can_client_change_view()) {
540 return;
543 gui_x += DIR_DX[gui_dir] * mapview.width / 2;
544 gui_y += DIR_DY[gui_dir] * mapview.height / 2;
545 set_mapview_origin(gui_x, gui_y);
548 /**************************************************************************
549 Do some appropriate action when the "main" mouse button (usually
550 left-click) is pressed. For more sophisticated user control use (or
551 write) a different xxx_button_pressed function.
552 **************************************************************************/
553 void action_button_pressed(int canvas_x, int canvas_y,
554 enum quickselect_type qtype)
556 struct tile *ptile = canvas_pos_to_tile(canvas_x, canvas_y);
558 if (can_client_change_view() && ptile) {
559 /* FIXME: Some actions here will need to check can_client_issue_orders.
560 * But all we can check is the lowest common requirement. */
561 do_map_click(ptile, qtype);
565 /**************************************************************************
566 Wakeup sentried units on the tile of the specified location.
567 **************************************************************************/
568 void wakeup_button_pressed(int canvas_x, int canvas_y)
570 struct tile *ptile = canvas_pos_to_tile(canvas_x, canvas_y);
572 if (can_client_issue_orders() && ptile) {
573 wakeup_sentried_units(ptile);
577 /**************************************************************************
578 Adjust the position of city workers from the mapview.
579 **************************************************************************/
580 void adjust_workers_button_pressed(int canvas_x, int canvas_y)
582 struct tile *ptile = canvas_pos_to_tile(canvas_x, canvas_y);
584 if (NULL != ptile && can_client_issue_orders()) {
585 struct city *pcity = find_city_near_tile(ptile);
587 if (pcity && !cma_is_city_under_agent(pcity, NULL)) {
588 int city_x, city_y;
590 fc_assert_ret(city_base_to_city_map(&city_x, &city_y, pcity, ptile));
592 if (NULL != tile_worked(ptile) && tile_worked(ptile) == pcity) {
593 dsend_packet_city_make_specialist(&client.conn, pcity->id,
594 city_x, city_y);
595 } else if (city_can_work_tile(pcity, ptile)) {
596 dsend_packet_city_make_worker(&client.conn, pcity->id,
597 city_x, city_y);
598 } else {
599 return;
602 /* When the city info packet is received, update the workers on the
603 * map. This is a bad hack used to selectively update the mapview
604 * when we receive the corresponding city packet. */
605 city_workers_display = pcity;
610 /**************************************************************************
611 Recenter the map on the canvas location, on user request. Usually this
612 is done with a right-click.
613 **************************************************************************/
614 void recenter_button_pressed(int canvas_x, int canvas_y)
616 /* We use the "nearest" tile here so off-map clicks will still work. */
617 struct tile *ptile = canvas_pos_to_nearest_tile(canvas_x, canvas_y);
619 if (can_client_change_view() && ptile) {
620 center_tile_mapcanvas(ptile);
624 /**************************************************************************
625 Update the turn done button state.
626 **************************************************************************/
627 void update_turn_done_button_state(void)
629 bool turn_done_state = get_turn_done_button_state();
631 set_turn_done_button_state(turn_done_state);
633 if (turn_done_state) {
634 if (waiting_for_end_turn
635 || (NULL != client.conn.playing
636 && is_ai(client.conn.playing)
637 && !gui_options.ai_manual_turn_done)) {
638 send_turn_done();
639 } else {
640 update_turn_done_button(TRUE);
645 /**************************************************************************
646 Update the goto/patrol line to the given map canvas location.
647 **************************************************************************/
648 void update_line(int canvas_x, int canvas_y)
650 struct tile *ptile;
652 switch (hover_state) {
653 case HOVER_GOTO:
654 case HOVER_PATROL:
655 case HOVER_CONNECT:
656 ptile = canvas_pos_to_tile(canvas_x, canvas_y);
658 is_valid_goto_draw_line(ptile);
659 case HOVER_NONE:
660 case HOVER_PARADROP:
661 case HOVER_ACT_SEL_TGT:
662 break;
666 /****************************************************************************
667 Update the goto/patrol line to the given overview canvas location.
668 ****************************************************************************/
669 void overview_update_line(int overview_x, int overview_y)
671 struct tile *ptile;
672 int x, y;
674 switch (hover_state) {
675 case HOVER_GOTO:
676 case HOVER_PATROL:
677 case HOVER_CONNECT:
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);