webperimental: killstack decides stack protects.
[freeciv.git] / client / mapctrl_common.c
bloba9f319d3f3c26e115bcbbdc1a0a544eb0e0ad66f
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_end_turn()
523 && (is_human(client.conn.playing) || gui_options.ai_manual_turn_done);
526 /**************************************************************************
527 Return TRUE iff client can end turn.
528 **************************************************************************/
529 bool can_end_turn(void)
531 return (can_client_issue_orders()
532 && client.conn.playing->is_alive
533 && !client.conn.playing->phase_done
534 && !is_server_busy()
535 && is_player_phase(client.conn.playing, game.info.phase)
536 && !agents_busy());
539 /**************************************************************************
540 Scroll the mapview half a screen in the given direction. This is a GUI
541 direction; i.e., DIR8_NORTH is "up" on the mapview.
542 **************************************************************************/
543 void scroll_mapview(enum direction8 gui_dir)
545 int gui_x = mapview.gui_x0, gui_y = mapview.gui_y0;
547 if (!can_client_change_view()) {
548 return;
551 gui_x += DIR_DX[gui_dir] * mapview.width / 2;
552 gui_y += DIR_DY[gui_dir] * mapview.height / 2;
553 set_mapview_origin(gui_x, gui_y);
556 /**************************************************************************
557 Do some appropriate action when the "main" mouse button (usually
558 left-click) is pressed. For more sophisticated user control use (or
559 write) a different xxx_button_pressed function.
560 **************************************************************************/
561 void action_button_pressed(int canvas_x, int canvas_y,
562 enum quickselect_type qtype)
564 struct tile *ptile = canvas_pos_to_tile(canvas_x, canvas_y);
566 if (can_client_change_view() && ptile) {
567 /* FIXME: Some actions here will need to check can_client_issue_orders.
568 * But all we can check is the lowest common requirement. */
569 do_map_click(ptile, qtype);
573 /**************************************************************************
574 Wakeup sentried units on the tile of the specified location.
575 **************************************************************************/
576 void wakeup_button_pressed(int canvas_x, int canvas_y)
578 struct tile *ptile = canvas_pos_to_tile(canvas_x, canvas_y);
580 if (can_client_issue_orders() && ptile) {
581 wakeup_sentried_units(ptile);
585 /**************************************************************************
586 Adjust the position of city workers from the mapview.
587 **************************************************************************/
588 void adjust_workers_button_pressed(int canvas_x, int canvas_y)
590 struct tile *ptile = canvas_pos_to_tile(canvas_x, canvas_y);
592 if (NULL != ptile && can_client_issue_orders()) {
593 struct city *pcity = find_city_near_tile(ptile);
595 if (pcity && !cma_is_city_under_agent(pcity, NULL)) {
596 int city_x, city_y;
598 fc_assert_ret(city_base_to_city_map(&city_x, &city_y, pcity, ptile));
600 if (NULL != tile_worked(ptile) && tile_worked(ptile) == pcity) {
601 dsend_packet_city_make_specialist(&client.conn, pcity->id,
602 city_x, city_y);
603 } else if (city_can_work_tile(pcity, ptile)) {
604 dsend_packet_city_make_worker(&client.conn, pcity->id,
605 city_x, city_y);
606 } else {
607 return;
610 /* When the city info packet is received, update the workers on the
611 * map. This is a bad hack used to selectively update the mapview
612 * when we receive the corresponding city packet. */
613 city_workers_display = pcity;
618 /**************************************************************************
619 Recenter the map on the canvas location, on user request. Usually this
620 is done with a right-click.
621 **************************************************************************/
622 void recenter_button_pressed(int canvas_x, int canvas_y)
624 /* We use the "nearest" tile here so off-map clicks will still work. */
625 struct tile *ptile = canvas_pos_to_nearest_tile(canvas_x, canvas_y);
627 if (can_client_change_view() && ptile) {
628 center_tile_mapcanvas(ptile);
632 /**************************************************************************
633 Update the turn done button state.
634 **************************************************************************/
635 void update_turn_done_button_state(void)
637 bool turn_done_state = get_turn_done_button_state();
639 set_turn_done_button_state(turn_done_state);
641 if (can_end_turn()) {
642 if (waiting_for_end_turn) {
643 send_turn_done();
644 } else {
645 update_turn_done_button(TRUE);
650 /**************************************************************************
651 Update the goto/patrol line to the given map canvas location.
652 **************************************************************************/
653 void update_line(int canvas_x, int canvas_y)
655 struct tile *ptile;
657 switch (hover_state) {
658 case HOVER_GOTO:
659 case HOVER_PATROL:
660 case HOVER_CONNECT:
661 ptile = canvas_pos_to_tile(canvas_x, canvas_y);
663 is_valid_goto_draw_line(ptile);
664 case HOVER_NONE:
665 case HOVER_PARADROP:
666 case HOVER_ACT_SEL_TGT:
667 break;
671 /****************************************************************************
672 Update the goto/patrol line to the given overview canvas location.
673 ****************************************************************************/
674 void overview_update_line(int overview_x, int overview_y)
676 struct tile *ptile;
677 int x, y;
679 switch (hover_state) {
680 case HOVER_GOTO:
681 case HOVER_PATROL:
682 case HOVER_CONNECT:
683 overview_to_map_pos(&x, &y, overview_x, overview_y);
684 ptile = map_pos_to_tile(&(wld.map), x, y);
686 is_valid_goto_draw_line(ptile);
687 case HOVER_NONE:
688 case HOVER_PARADROP:
689 case HOVER_ACT_SEL_TGT:
690 break;
694 /****************************************************************************
695 We sort according to the following logic:
697 - Transported units should immediately follow their transporter (note that
698 transporting may be recursive).
699 - Otherwise we sort by ID (which is what the list is originally sorted by).
700 ****************************************************************************/
701 static int unit_list_compare(const void *a, const void *b)
703 const struct unit *punit1 = *(struct unit **)a;
704 const struct unit *punit2 = *(struct unit **)b;
706 if (unit_transport_get(punit1) == unit_transport_get(punit2)) {
707 /* For units with the same transporter or no transporter: sort by id. */
708 /* Perhaps we should sort by name instead? */
709 return punit1->id - punit2->id;
710 } else if (unit_transport_get(punit1) == punit2) {
711 return 1;
712 } else if (unit_transport_get(punit2) == punit1) {
713 return -1;
714 } else {
715 /* If the transporters aren't the same, put in order by the
716 * transporters. */
717 const struct unit *ptrans1 = unit_transport_get(punit1);
718 const struct unit *ptrans2 = unit_transport_get(punit2);
720 if (!ptrans1) {
721 ptrans1 = punit1;
723 if (!ptrans2) {
724 ptrans2 = punit2;
727 return unit_list_compare(&ptrans1, &ptrans2);
731 /****************************************************************************
732 Fill and sort the list of units on the tile.
733 ****************************************************************************/
734 void fill_tile_unit_list(const struct tile *ptile, struct unit **unit_list)
736 int i = 0;
738 /* First populate the unit list. */
739 unit_list_iterate(ptile->units, punit) {
740 unit_list[i] = punit;
741 i++;
742 } unit_list_iterate_end;
744 /* Then sort it. */
745 qsort(unit_list, i, sizeof(*unit_list), unit_list_compare);