The Battle for Wesnoth  1.17.0-dev
mouse_events.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2006 - 2021
3  by Joerg Hinrichs <joerg.hinrichs@alice-dsl.de>
4  Copyright (C) 2003 by David White <dave@whitevine.net>
5  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
6 
7  This program is free software; you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation; either version 2 of the License, or
10  (at your option) any later version.
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY.
13 
14  See the COPYING file for more details.
15 */
16 
17 #include "mouse_events.hpp"
18 
19 #include "actions/attack.hpp" // for battle_context, etc
20 #include "actions/move.hpp" // for move_and_record
21 #include "actions/undo.hpp" // for undo_list
22 #include "config.hpp" // for config
23 #include "cursor.hpp" // for set, CURSOR_TYPE::NORMAL, etc
24 #include "game_board.hpp" // for game_board, etc
25 #include "game_events/pump.hpp" // for fire
26 #include "gettext.hpp" // for _
27 #include "gui/dialogs/transient_message.hpp" // for show_transient_message
28 #include "gui/dialogs/unit_attack.hpp" // for unit_attack
29 #include "gui/widgets/settings.hpp" // for new_widgets
30 #include "language.hpp" // for string_table, symbol_table
31 #include "log.hpp" // for LOG_STREAM, logger, etc
32 #include "map/map.hpp" // for gamemap
33 #include "pathfind/teleport.hpp" // for get_teleport_locations, etc
34 #include "play_controller.hpp" // for playing_side, set_button_state
35 #include "replay_helper.hpp"
37 #include "sound.hpp"
38 #include "synced_context.hpp"
39 #include "team.hpp" // for team
40 #include "tod_manager.hpp"
42 #include "units/ptr.hpp" // for unit_const_ptr
43 #include "units/unit.hpp" // for unit
44 #include "whiteboard/manager.hpp" // for manager, etc
45 #include "whiteboard/typedefs.hpp" // for whiteboard_lock
46 
47 #include <SDL2/SDL_mouse.h> // for SDL_GetMouseState
48 #include <cassert> // for assert
49 #include <new> // for bad_alloc
50 #include <ostream> // for operator<<, basic_ostream, etc
51 #include <string> // for string, operator<<, etc
52 
53 static lg::log_domain log_engine("engine");
54 #define ERR_NG LOG_STREAM(err, log_engine)
55 #define LOG_NG LOG_STREAM(info, log_engine)
56 
57 static lg::log_domain log_wml("wml");
58 #define ERR_WML LOG_STREAM(err, log_wml)
59 
60 namespace events
61 {
64  , gui_(gui)
65  , pc_(pc)
66  , previous_hex_()
67  , previous_free_hex_()
68  , selected_hex_()
69  , next_unit_()
70  , current_route_()
71  , current_paths_()
72  , unselected_paths_(false)
73  , unselected_reach_(false)
74  , path_turns_(0)
75  , side_num_(1)
76  , over_route_(false)
77  , reachmap_invalid_(false)
78  , show_partial_move_(false)
79  , preventing_units_highlight_(false)
80 {
81  singleton_ = this;
82 }
83 
85 {
86  singleton_ = nullptr;
87 }
88 
90 {
92 }
93 
95 {
96  // TODO: Use physical screen size.
97  return 14;
98 }
99 
100 void mouse_handler::touch_motion(int x, int y, const bool browse, bool update, map_location new_hex)
101 {
102  // Frankensteining from mouse_motion(), as it has a lot in common, but a lot of differences too.
103  // Copy-pasted from everywhere. TODO: generalize the two.
104  SDL_GetMouseState(&x,&y);
105 
106  // This is from mouse_handler_base::mouse_motion_default()
107  tooltips::process(x, y);
108 
109  if(simple_warp_) {
110  return;
111  }
112 
113  if(minimap_scrolling_) {
114  const map_location& mini_loc = gui().minimap_location_on(x,y);
115  if(mini_loc.valid()) {
116  if(mini_loc != last_hex_) {
117  last_hex_ = mini_loc;
118  gui().scroll_to_tile(mini_loc,display::WARP,false);
119  }
120  return;
121  } else {
122  // clicking outside of the minimap will end minimap scrolling
123  minimap_scrolling_ = false;
124  }
125  }
126 
127  // Fire the drag & drop only after minimal drag distance
128  // While we check the mouse buttons state, we also grab fresh position data.
129  int mx = drag_from_x_; // some default value to prevent unlikely SDL bug
130  int my = drag_from_y_;
131  if(is_dragging() && !dragging_started_) {
132  if(dragging_touch_) {
133  SDL_GetMouseState(&mx, &my);
134  const double drag_distance = std::pow(static_cast<double>(drag_from_x_- mx), 2)
135  + std::pow(static_cast<double>(drag_from_y_- my), 2);
136  if(drag_distance > drag_threshold()*drag_threshold()) {
137  dragging_started_ = true;
138  }
139  }
140  }
141 
142  // Not-so-smooth panning
143  const auto found_unit = find_unit(selected_hex_);
144  bool selected_hex_has_my_unit = found_unit.valid() && found_unit.get_shared_ptr()->side() == side_num_;
145  if((browse || !found_unit.valid()) && is_dragging() && dragging_started_) {
146  SDL_GetMouseState(&mx, &my);
147 
148  if(sdl::point_in_rect(x, y, gui().map_area())) {
149  int dx = drag_from_x_ - mx;
150  int dy = drag_from_y_ - my;
151 
152  gui().scroll(dx, dy);
153  drag_from_x_ = mx;
154  drag_from_y_ = my;
155  }
156  return;
157  }
158 
159  // now copy-pasting mouse_handler::mouse_motion()
160 
161  game_board & board = pc_.gamestate().board_;
162 
163  if(new_hex == map_location::null_location())
164  new_hex = gui().hex_clicked_on(x,y);
165 
166  if(new_hex != last_hex_) {
167  update = true;
168  if( pc_.get_map().on_board(last_hex_) ) {
169  // we store the previous hexes used to propose attack direction
171  // the hex of the selected unit is also "free"
172  { // start planned unit map scope
176  }
177  } // end planned unit map scope
178  }
179  last_hex_ = new_hex;
180  }
181 
182  if(reachmap_invalid_) update = true;
183 
184  if(!update) return;
185 
186  if(reachmap_invalid_) {
187  reachmap_invalid_ = false;
189  { // start planned unit map scope
190  wb::future_map_if_active planned_unit_map;
191  selected_hex_has_my_unit = found_unit.valid();
192  } // end planned unit map scope
193  if(selected_hex_.valid() && selected_hex_has_my_unit) {
194  // FIXME: vic: why doesn't this trigger when touch-dragging an unselected unit?
195  // reselect the unit without firing events (updates current_paths_)
196  select_hex(selected_hex_, true);
197  }
198  // we do never deselect here, mainly because of canceled attack-move
199  }
200  }
201 
202  // reset current_route_ and current_paths if not valid anymore
203  // we do it before cursor selection, because it uses current_paths_
204  if( !pc_.get_map().on_board(new_hex) ) {
205  current_route_.steps.clear();
206  gui().set_route(nullptr);
207  pc_.get_whiteboard()->erase_temp_move();
208  }
209 
210  if(unselected_paths_) {
211  unselected_paths_ = false;
214  } else if(over_route_) {
215  over_route_ = false;
216  current_route_.steps.clear();
217  gui().set_route(nullptr);
218  pc_.get_whiteboard()->erase_temp_move();
219  }
220 
221  gui().highlight_hex(new_hex);
222  pc_.get_whiteboard()->on_mouseover_change(new_hex);
223 
225  unit_map::iterator mouseover_unit;
226  map_location attack_from;
227 
228  { // start planned unit map scope
229  wb::future_map_if_active planned_unit_map;
230  selected_unit = found_unit;
231  mouseover_unit = find_unit(new_hex);
232 
233  // we search if there is an attack possibility and where
234  attack_from = current_unit_attacks_from(new_hex);
235 
236  //see if we should show the normal cursor, the movement cursor, or
237  //the attack cursor
238  //If the cursor is on WAIT, we don't change it and let the setter
239  //of this state end it
240  if (cursor::get() != cursor::WAIT) {
241  if (selected_unit &&
242  selected_unit->side() == side_num_ &&
243  !selected_unit->incapacitated() && !browse)
244  {
245  if (attack_from.valid()) {
247  }
248  else if (!mouseover_unit &&
250  {
251  // Is this where left-drag cursor changes? Test.
253  } else {
254  // selected unit can't attack or move there
256  }
257  } else {
258  // no selected unit or we can't move it
259 
260  if ( selected_hex_.valid() && mouseover_unit
261  && mouseover_unit->side() == side_num_ ) {
262  // empty hex field selected and unit on our site under the cursor
264  } else {
266  }
267  }
268  }
269  } // end planned unit map scope
270 
271  // show (or cancel) the attack direction indicator
272  if(attack_from.valid() && (!browse || pc_.get_whiteboard()->is_active())) {
273  gui().set_attack_indicator(attack_from, new_hex);
274  } else {
276  }
277 
278  unit_ptr un; //will later point to unit at mouseover_hex_
279 
280  // the destination is the pointed hex or the adjacent hex
281  // used to attack it
282  map_location dest;
283  unit_map::const_iterator dest_un;
284  { // start planned unit map scope
286  if (attack_from.valid()) {
287  dest = attack_from;
288  dest_un = find_unit(dest);
289  } else {
290  dest = new_hex;
291  dest_un = find_unit(new_hex);
292  }
293 
294  if(dest == selected_hex_ || dest_un) {
295  current_route_.steps.clear();
296  gui().set_route(nullptr);
297  pc_.get_whiteboard()->erase_temp_move();
298  }
299  else if (!current_paths_.destinations.empty() &&
300  board.map().on_board(selected_hex_) && board.map().on_board(new_hex))
301  {
302  if (selected_unit && !selected_unit->incapacitated()) {
303  // Show the route from selected unit to mouseover hex
304  current_route_ = get_route(&*selected_unit, dest, viewing_team());
305 
306  pc_.get_whiteboard()->create_temp_move();
307 
308  if(!browse) {
310  }
311  }
312  }
313 
314  if(board.map().on_board(selected_hex_)
315  && !selected_unit
316  && mouseover_unit.valid()
317  && mouseover_unit) {
318  // Show the route from selected hex to mouseover unit
319  current_route_ = get_route(&*mouseover_unit, selected_hex_, viewing_team());
320 
321  pc_.get_whiteboard()->create_temp_move();
322 
323  if(!browse) {
325  }
326  } else if (!selected_unit) {
327  current_route_.steps.clear();
328  gui().set_route(nullptr);
329  pc_.get_whiteboard()->erase_temp_move();
330  }
331 
332  unit_map::iterator iter = mouseover_unit;
333  if (iter)
334  un = iter.get_shared_ptr();
335  else
336  un.reset();
337  } //end planned unit map scope
338 
339  if( (!selected_hex_.valid()) && un && current_paths_.destinations.empty() &&
340  !gui().fogged(un->get_location()))
341  {
342  if (un->side() == side_num_) {
343  //unit is on our team, show path if the unit has one
344  const map_location go_to = un->get_goto();
345  if(board.map().on_board(go_to)) {
347  { // start planned unit map scope
349  route = get_route(un.get(), go_to, current_team());
350  } // end planned unit map scope
351  gui().set_route(&route);
352  }
353  over_route_ = true;
354 
356  current_paths_ = pathfind::paths(*un, false, true,
358  } else {
359  //unit under cursor is not on our team
360  //Note: planned unit map must be activated after this is done,
361  //since the future state includes changes to units' movement.
362  unit_movement_resetter move_reset(*un);
363 
365  current_paths_ = pathfind::paths(*un, false, true,
367  }
368 
369  unselected_paths_ = true;
371  }
372 
373 }
374 
375 void mouse_handler::mouse_motion(int x, int y, const bool browse, bool update, map_location new_hex)
376 {
377  // we ignore the position coming from event handler
378  // because it's always a little obsolete and we don't need
379  // to highlight all the hexes where the mouse passed.
380  // Also, sometimes it seems to have one *very* obsolete
381  // and isolated mouse motion event when using drag&drop
382  SDL_GetMouseState(&x, &y); // <-- modify x and y
383 
384  if(mouse_handler_base::mouse_motion_default(x, y, update)) {
385  return;
386  }
387 
388  if(new_hex == map_location::null_location()) {
389  new_hex = gui().hex_clicked_on(x, y);
390  }
391 
392  if(new_hex != last_hex_) {
393  if(game_lua_kernel* lk = pc_.gamestate().lua_kernel_.get()) {
394  lk->mouse_over_hex_callback(new_hex);
395  }
396 
397  update = true;
398 
399  if(pc_.get_map().on_board(last_hex_)) {
400  // we store the previous hexes used to propose attack direction
402 
403  // the hex of the selected unit is also "free"
404  { // start planned unit map scope
408  }
409  } // end planned unit map scope
410  }
411 
412  last_hex_ = new_hex;
413  }
414 
415  if(reachmap_invalid_) {
416  update = true;
417  }
418 
419  if(!update) {
420  return;
421  }
422 
423  if(reachmap_invalid_) {
424  reachmap_invalid_ = false;
425 
427  bool selected_hex_has_unit;
428  { // start planned unit map scope
429  wb::future_map_if_active planned_unit_map;
430  selected_hex_has_unit = find_unit(selected_hex_).valid();
431  } // end planned unit map scope
432 
433  if(selected_hex_.valid() && selected_hex_has_unit) {
434  // reselect the unit without firing events (updates current_paths_)
435  select_hex(selected_hex_, true);
436  }
437 
438  // we do never deselect here, mainly because of canceled attack-move
439  }
440  }
441 
442  // reset current_route_ and current_paths if not valid anymore
443  // we do it before cursor selection, because it uses current_paths_
444  if(!pc_.get_map().on_board(new_hex)) {
445  current_route_.steps.clear();
446  gui().set_route(nullptr);
447  pc_.get_whiteboard()->erase_temp_move();
448  }
449 
450  if(unselected_paths_) {
451  unselected_paths_ = false;
454  } else if(over_route_) {
455  over_route_ = false;
456  current_route_.steps.clear();
457  gui().set_route(nullptr);
458  pc_.get_whiteboard()->erase_temp_move();
459  }
460 
461  gui().highlight_hex(new_hex);
462  pc_.get_whiteboard()->on_mouseover_change(new_hex);
463 
465  unit_map::iterator mouseover_unit;
466  map_location attack_from;
467 
468  { // start planned unit map scope
469  wb::future_map_if_active planned_unit_map;
470  selected_unit = find_unit(selected_hex_);
471  mouseover_unit = find_unit(new_hex);
472 
473  // we search if there is an attack possibility and where
474  attack_from = current_unit_attacks_from(new_hex);
475 
476  // see if we should show the normal cursor, the movement cursor, or
477  // the attack cursor
478  // If the cursor is on WAIT, we don't change it and let the setter
479  // of this state end it
480  if(cursor::get() != cursor::WAIT) {
481  if(selected_unit && selected_unit->side() == side_num_ && !selected_unit->incapacitated() && !browse) {
482  if(attack_from.valid()) {
484  } else if(!mouseover_unit && current_paths_.destinations.contains(new_hex)) {
486  } else {
487  // selected unit can't attack or move there
489  }
490  } else {
491  // no selected unit or we can't move it
492 
493  if(selected_hex_.valid() && mouseover_unit && mouseover_unit->side() == side_num_) {
494  // empty hex field selected and unit on our site under the cursor
496  } else {
498  }
499  }
500  }
501  } // end planned unit map scope
502 
503  // show (or cancel) the attack direction indicator
504  if(attack_from.valid() && (!browse || pc_.get_whiteboard()->is_active())) {
505  gui().set_attack_indicator(attack_from, new_hex);
506  } else {
508  }
509 
510  unit_ptr un; // will later point to unit at mouseover_hex_
511 
512  // the destination is the pointed hex or the adjacent hex
513  // used to attack it
514  map_location dest;
515  unit_map::const_iterator dest_un;
516  /* start planned unit map scope*/
517  {
519  if(attack_from.valid()) {
520  dest = attack_from;
521  dest_un = find_unit(dest);
522  } else {
523  dest = new_hex;
524  dest_un = find_unit(new_hex);
525  }
526 
527  if(dest == selected_hex_ || dest_un) {
528  current_route_.steps.clear();
529  gui().set_route(nullptr);
530  pc_.get_whiteboard()->erase_temp_move();
531  } else if(!current_paths_.destinations.empty() && pc_.get_map().on_board(selected_hex_) && pc_.get_map().on_board(new_hex)) {
532  if(selected_unit && !selected_unit->incapacitated()) {
533  // Show the route from selected unit to mouseover hex
534  current_route_ = get_route(&*selected_unit, dest, viewing_team());
535 
536  pc_.get_whiteboard()->create_temp_move();
537 
538  if(!browse) {
540  }
541  }
542  }
543 
544  if(pc_.get_map().on_board(selected_hex_) && !selected_unit && mouseover_unit.valid() && mouseover_unit) {
545  // Show the route from selected hex to mouseover unit
546  current_route_ = get_route(&*mouseover_unit, selected_hex_, viewing_team());
547 
548  pc_.get_whiteboard()->create_temp_move();
549 
550  if(!browse) {
552  }
553  } else if(!selected_unit) {
554  current_route_.steps.clear();
555  gui().set_route(nullptr);
556  pc_.get_whiteboard()->erase_temp_move();
557  }
558 
559  if(mouseover_unit) {
560  un = mouseover_unit.get_shared_ptr();
561  } else {
562  un.reset();
563  }
564  } /*end planned unit map scope*/
565 
566  if(!selected_hex_.valid() && un && current_paths_.destinations.empty() && !gui().fogged(un->get_location())) {
567  /*
568  * Only process unit if toggler not preventing normal unit
569  * processing. This can happen e.g. if, after activating 'show
570  * [best possible] enemy movements' through the UI menu, the
571  * mouse cursor lands on a hex with unit in it.
572  */
574  if(un->side() == side_num_) {
575  // unit is on our team, show path if the unit has one
576  const map_location go_to = un->get_goto();
577  if(pc_.get_map().on_board(go_to)) {
579  { // start planned unit map scope
581  route = get_route(un.get(), go_to, current_team());
582  } // end planned unit map scope
583  gui().set_route(&route);
584  }
585  over_route_ = true;
586 
588  current_paths_ = pathfind::paths(*un, false, true, viewing_team(), path_turns_);
589  } else {
590  // unit under cursor is not on our team
591  // Note: planned unit map must be activated after this is done,
592  // since the future state includes changes to units' movement.
593  unit_movement_resetter move_reset(*un);
594 
596  current_paths_ = pathfind::paths(*un, false, true, viewing_team(), path_turns_);
597  }
598 
599  unselected_paths_ = true;
601 
602  }
603  }
604 
605  if(!un && preventing_units_highlight_) {
606  // Cursor on empty hex, turn unit highlighting back on.
608  }
609 }
610 
612 {
614  if(res) {
615  return res;
616  }
617 
618  return find_unit(last_hex_);
619 }
620 
622 {
624  if(it.valid()) {
625  return it;
626  }
627 
628  return pc_.get_units().end();
629 }
630 
632 {
634 }
635 
637 {
639  return it.valid() ? &*it : nullptr;
640 }
641 
643 {
645  return it.valid() ? &*it : nullptr;
646 }
647 
649 {
650  int x = -1;
651  int y = -1;
652  SDL_GetMouseState(&x, &y);
653  return gui_->hex_clicked_on(x, y);
654 }
655 
657 {
658  return find_unit(hex).valid();
659 }
660 
662 {
663  if(loc == selected_hex_) {
664  return map_location();
665  }
666 
667  bool wb_active = pc_.get_whiteboard()->is_active();
668 
669  {
670  // Check the unit SOURCE of the attack
671 
672  // Check that there's a selected unit
673  const unit_map::const_iterator source_unit = find_unit(selected_hex_);
674 
675  bool source_eligible = source_unit.valid();
676  if(!source_eligible) {
677  return map_location();
678  }
679 
680  // The selected unit must at least belong to the player currently controlling this client.
681  source_eligible &= source_unit->side() == gui_->viewing_side();
682  if(!source_eligible) {
683  return map_location();
684  }
685 
686  // In addition:
687  // - If whiteboard is enabled, we allow planning attacks outside of player's turn
688  // - If whiteboard is disabled, it must be the turn of the player controlling this client
689  if(!wb_active) {
690  source_eligible &= gui_->viewing_side() == pc_.current_side();
691  if(!source_eligible) {
692  return map_location();
693  }
694  }
695 
696  // Unit must have attacks left
697  source_eligible &= source_unit->attacks_left() != 0;
698  if(!source_eligible) {
699  return map_location();
700  }
701 
702  // Check the unit TARGET of the attack
703 
704  const team& viewer = viewing_team();
705 
706  // Check that there's a unit at the target location
707  const unit_map::const_iterator target_unit = find_unit(loc);
708 
709  bool target_eligible = target_unit.valid();
710  if(!target_eligible) {
711  return map_location();
712  }
713 
714  // The player controlling this client must be an enemy of the target unit's side
715  target_eligible &= viewer.is_enemy(target_unit->side());
716  if(!target_eligible) {
717  return map_location();
718  }
719 
720  // Sanity check: source and target of the attack shouldn't be on the same team
721  assert(source_unit->side() != target_unit->side());
722 
723  target_eligible &= !target_unit->incapacitated();
724  if(!target_eligible) {
725  return map_location();
726  }
727  }
728 
730  const map_location::DIRECTION second_preferred = loc.get_relative_dir(previous_free_hex_);
731 
732  int best_rating = 100; // smaller is better
733 
734  map_location res;
735  const auto adj = get_adjacent_tiles(loc);
736 
737  for(std::size_t n = 0; n < adj.size(); ++n) {
738  if(pc_.get_map().on_board(adj[n]) == false) {
739  continue;
740  }
741 
742  if(adj[n] != selected_hex_ && find_unit(adj[n])) {
743  continue;
744  }
745 
746  if(current_paths_.destinations.contains(adj[n])) {
747  static const std::size_t NDIRECTIONS = map_location::NDIRECTIONS;
748 
749  unsigned int difference = std::abs(static_cast<int>(preferred - n));
750  if(difference > NDIRECTIONS / 2) {
751  difference = NDIRECTIONS - difference;
752  }
753 
754  unsigned int second_difference = std::abs(static_cast<int>(second_preferred - n));
755  if(second_difference > NDIRECTIONS / 2) {
756  second_difference = NDIRECTIONS - second_difference;
757  }
758 
759  const int rating = difference * 2 + (second_difference > difference);
760  if(rating < best_rating || res.valid() == false) {
761  best_rating = rating;
762  res = adj[n];
763  }
764  }
765  }
766 
767  return res;
768 }
769 
771 {
772  game_board& board = pc_.gamestate().board_;
773 
774  // The pathfinder will check unit visibility (fogged/stealthy).
775  const pathfind::shortest_path_calculator calc(*un, team, board.teams(), board.map());
776 
778 
779  pathfind::plain_route route;
780 
781  route = pathfind::a_star_search(
782  un->get_location(), go_to, 10000.0, calc, board.map().w(), board.map().h(), &allowed_teleports);
783 
784  return mark_route(route);
785 }
786 
787 bool mouse_handler::right_click_show_menu(int x, int y, const bool /*browse*/)
788 {
790  unselected_reach_ = false;
791  return false;
792  }
793 
794  return sdl::point_in_rect(x, y, gui().map_area());
795 }
796 
798 {
799  if(!pc_.get_map().on_board(last_hex_)) {
801  return;
802  }
803 
804  // Load whiteboard partial moves
805  wb::future_map_if_active planned_unit_map;
806 
807  if(game_lua_kernel* lk = pc_.gamestate().lua_kernel_.get()) {
808  lk->select_hex_callback(last_hex_);
809  }
810 
813 
814  if(clicked_u && (!selected_u || selected_u->side() != side_num_ ||
815  (clicked_u->side() == side_num_ && clicked_u->id() != selected_u->id()))
816  ) {
817  select_hex(last_hex_, false);
818  } else {
819  move_action(browse);
820  }
821 }
822 
823 void mouse_handler::move_action(bool browse)
824 {
825  // Lock whiteboard activation state to avoid problems due to
826  // its changing while an animation takes place.
827  wb::whiteboard_lock wb_lock = pc_.get_whiteboard()->get_activation_state_lock();
828 
829  // we use the last registered highlighted hex
830  // since it's what update our global state
831  map_location hex = last_hex_;
832 
833  // TODO
834  // // Clicks on border hexes mean to deselect.
835  // // (Check this before doing processing that might not be needed.)
836  // if ( !pc_.get_map().on_board(hex) ) {
837  // deselect_hex();
838  // return false;
839  // }
840 
841  unit* u = nullptr;
842  const unit* clicked_u = nullptr;
843 
844  map_location src;
845  pathfind::paths orig_paths;
846  map_location attack_from;
847 
848  { // start planned unit map scope
849  wb::future_map_if_active planned_unit_map;
851 
852  // if the unit is selected and then itself clicked on,
853  // any goto command is canceled
854  if(u && !browse && selected_hex_ == hex && u->side() == side_num_) {
855  u->set_goto(map_location());
856  }
857 
858  clicked_u = find_unit_nonowning(hex);
859 
860  src = selected_hex_;
861  orig_paths = current_paths_;
862  attack_from = current_unit_attacks_from(hex);
863  } // end planned unit map scope
864 
865  // see if we're trying to do a attack or move-and-attack
866  if((!browse || pc_.get_whiteboard()->is_active()) && attack_from.valid()) {
867  // Ignore this command if commands are disabled.
868  if(commands_disabled) {
869  return;
870  }
871 
872  if(((u != nullptr && u->side() == side_num_) || pc_.get_whiteboard()->is_active()) && clicked_u != nullptr) {
873  if(attack_from == selected_hex_) { // no move needed
874  int choice = -1;
875  {
876  wb::future_map_if_active planned_unit_map; // start planned unit map scope
877  choice = show_attack_dialog(attack_from, clicked_u->get_location());
878  } // end planned unit map scope
879 
880  if(choice >= 0) {
881  if(pc_.get_whiteboard()->is_active()) {
882  save_whiteboard_attack(attack_from, clicked_u->get_location(), choice);
883  } else {
884  // clear current unit selection so that any other unit selected
885  // triggers a new selection
887 
888  attack_enemy(u->get_location(), clicked_u->get_location(), choice);
889  }
890  }
891 
892  return;
893  } else {
894  int choice = -1; // for the attack dialog
895 
896  {
897  wb::future_map_if_active planned_unit_map; // start planned unit map scope
898  // we will now temporary move next to the enemy
899  pathfind::paths::dest_vect::const_iterator itor = current_paths_.destinations.find(attack_from);
900  if(itor == current_paths_.destinations.end()) {
901  // can't reach the attacking location
902  // not supposed to happen, so abort
903  return;
904  }
905 
906  // block where we temporary move the unit
907  {
908  temporary_unit_mover temp_mover(pc_.get_units(), src, attack_from, itor->move_left);
909  choice = show_attack_dialog(attack_from, clicked_u->get_location());
910  }
911 
912  if(choice < 0) {
913  // user hit cancel, don't start move+attack
914  return;
915  }
916  } // end planned unit map scope
917 
918  if(pc_.get_whiteboard()->is_active()) {
919  save_whiteboard_attack(attack_from, hex, choice);
920  } else {
921  bool not_interrupted = move_unit_along_current_route();
922  bool alt_unit_selected = (selected_hex_ != src);
923  src = selected_hex_;
924  // clear current unit selection so that any other unit selected
925  // triggers a new selection
927 
928  if(not_interrupted)
929  attack_enemy(attack_from, hex, choice); // Fight !!
930 
931  // TODO: Maybe store the attack choice so "press t to continue"
932  // can also continue the attack?
933 
934  if(alt_unit_selected && !selected_hex_.valid()) {
935  // reselect other unit if selected during movement animation
936  select_hex(src, browse);
937  }
938  }
939 
940  return;
941  }
942  }
943  }
944  // otherwise we're trying to move to a hex
945  else if(
946  // The old use case: move selected unit to mouse hex field.
947  (
948  (!browse || pc_.get_whiteboard()->is_active())
949  && selected_hex_.valid()
950  && selected_hex_ != hex
951  && u != nullptr
952  && (u->side() == side_num_ || pc_.get_whiteboard()->is_active())
953  && !clicked_u
954  && !current_route_.steps.empty()
955  && current_route_.steps.front() == selected_hex_
956  )
957  || // The new use case: move mouse unit to selected hex field.
958  (
959  (!browse || pc_.get_whiteboard()->is_active())
960  && selected_hex_.valid()
961  && selected_hex_ != hex
962  && clicked_u
963  && !current_route_.steps.empty()
964  && current_route_.steps.back() == selected_hex_
965  && !u
966  && clicked_u->side() == side_num_
967  )
968  ) {
969  // Ignore this command if commands are disabled.
970  if(commands_disabled) {
971  return;
972  }
973 
974  // If the whiteboard is active, it intercepts any unit movement.
975  if(pc_.get_whiteboard()->is_active()) {
976  // Deselect the current hex, and create planned move for whiteboard.
978 
981  gui().set_route(nullptr);
982 
983  show_partial_move_ = false;
984 
986 
988  current_route_.steps.clear();
989 
990  pc_.get_whiteboard()->save_temp_move();
991 
992  // Otherwise proceed to normal unit movement
993  } else {
994  // Don't move if the unit already has actions
995  // from the whiteboard.
996  if(pc_.get_whiteboard()->unit_has_actions(u ? u : clicked_u)) {
997  return;
998  }
999 
1001 
1002  // During the move, we may have selected another unit
1003  // (but without triggering a select event (command was disabled)
1004  // in that case reselect it now to fire the event (+ anim & sound)
1005  if(selected_hex_ != src) {
1006  select_hex(selected_hex_, browse);
1007  }
1008  }
1009 
1010  return;
1011  }
1012 }
1013 
1014 void mouse_handler::touch_action(const map_location touched_hex, bool browse)
1015 {
1016  unit_map::iterator unit = find_unit(touched_hex);
1017 
1018  if (touched_hex.valid() && unit.valid() && !unit->get_hidden()) {
1019  select_or_action(browse);
1020  } else {
1021  deselect_hex();
1022  }
1023 }
1024 
1025 void mouse_handler::select_hex(const map_location& hex, const bool browse, const bool highlight, const bool fire_event)
1026 {
1027  selected_hex_ = hex;
1028 
1031  gui().set_route(nullptr);
1032 
1033  show_partial_move_ = false;
1034 
1035  wb::future_map_if_active planned_unit_map; // lasts for whole method
1036 
1038 
1039  if(selected_hex_.valid() && unit.valid() && !unit->get_hidden()) {
1040  next_unit_ = unit->get_location();
1041 
1042  {
1043  current_paths_ = pathfind::paths(*unit, false, true, viewing_team(), path_turns_);
1044  }
1045 
1046  if(highlight) {
1047  show_attack_options(unit);
1049  }
1050 
1051  // The highlight now comes from selection
1052  // and not from the mouseover on an enemy
1053  unselected_paths_ = false;
1054  gui().set_route(nullptr);
1055 
1056  // Selection have impact only if we are not observing and it's our unit
1057  if((!commands_disabled || pc_.get_whiteboard()->is_active()) && unit->side() == gui().viewing_side()) {
1058  if(!(browse || pc_.get_whiteboard()->unit_has_actions(&*unit))) {
1059  sound::play_UI_sound("select-unit.wav");
1060 
1061  unit->anim_comp().set_selecting();
1062 
1063  if(fire_event) {
1064  // Ensure unit map is back to normal while event is fired
1065  wb::real_map srum;
1066  pc_.pump().fire("select", hex);
1067  // end forced real unit map
1068  }
1069  }
1070  }
1071 
1072  return;
1073  }
1074 
1075  if(selected_hex_.valid() && !unit) {
1076  // Compute unit in range of the empty selected_hex field
1077 
1079 
1080  pathfind::paths reaching_unit_locations;
1081 
1082  pathfind::paths clicked_location;
1083  clicked_location.destinations.insert(hex);
1084 
1085  for(unit_map::iterator u = pc_.get_units().begin(); u != pc_.get_units().end();
1086  ++u) {
1087  bool invisible = u->invisible(u->get_location());
1088 
1089  if(!gui_->fogged(u->get_location()) && !u->incapacitated() && !invisible) {
1090  const pathfind::paths& path =
1091  pathfind::paths(*u, false, true, viewing_team(), path_turns_, false, false);
1092 
1093  if(path.destinations.find(hex) != path.destinations.end()) {
1094  reaching_unit_locations.destinations.insert(u->get_location());
1095  gui_->highlight_another_reach(clicked_location);
1096  }
1097  }
1098  }
1099 
1100  gui_->highlight_another_reach(reaching_unit_locations);
1101  } else {
1102  if(!pc_.get_units().find(last_hex_)) {
1104  }
1105 
1107  current_route_.steps.clear();
1108 
1109  pc_.get_whiteboard()->on_deselect_hex();
1110  }
1111 }
1112 
1114 {
1115  select_hex(map_location(), true);
1116 }
1117 
1118 /**
1119  * Moves a unit along the currently cached route.
1120  *
1121  * @returns true if the end of the route was reached and no information was
1122  * uncovered that would warrant interrupting a chain of actions;
1123  * false otherwise.
1124  */
1126 {
1127  // Copy the current route to ensure it remains valid throughout the animation.
1128  const std::vector<map_location> steps = current_route_.steps;
1129 
1130  // do not show footsteps during movement
1131  gui().set_route(nullptr);
1132  gui().unhighlight_reach();
1133 
1134  // do not keep the hex highlighted that we started from
1137 
1138  bool interrupted = false;
1139  if(steps.size() > 1) {
1140  std::size_t num_moves = move_unit_along_route(steps, interrupted);
1141 
1142  interrupted = interrupted || num_moves + 1 < steps.size();
1143  next_unit_ = steps[num_moves];
1144  }
1145 
1146  // invalid after the move
1148  current_route_.steps.clear();
1149 
1150  return !interrupted;
1151 }
1152 
1153 /**
1154  * Moves a unit across the board for a player.
1155  * This is specifically for movement at the time it is initiated by a player,
1156  * whether via a mouse click or executing whiteboard actions. Continued moves
1157  * (including goto execution) can bypass this and call actions::move_unit() directly.
1158  * This function call may include time for an animation, so make sure the
1159  * provided route will remain unchanged (the caller should probably make a local
1160  * copy).
1161  *
1162  * @param[in] steps The route to be traveled. The unit to be moved is at the beginning of this route.
1163  * @param[out] interrupted This is set to true if information was uncovered that warrants interrupting a chain of
1164  * actions (and set to false otherwise).
1165  *
1166  * @returns The number of hexes entered. This can safely be used as an index
1167  * into steps to get the location where movement ended, provided
1168  * steps is not empty (the return value is guaranteed to be less
1169  * than steps.size() ).
1170  */
1171 std::size_t mouse_handler::move_unit_along_route(const std::vector<map_location>& steps, bool& interrupted)
1172 {
1173  if(steps.empty()) {
1174  interrupted = false;
1175  return 0;
1176  }
1177 
1178  // Default return value.
1179  interrupted = true;
1180 
1181  // If this is a leader on a keep, ask permission to the whiteboard to move it
1182  // since otherwise it may cause planned recruits to be erased.
1183  if(pc_.get_map().is_keep(steps.front())) {
1184  unit_map::const_iterator const u = pc_.get_units().find(steps.front());
1185 
1186  if(u && u->can_recruit() && u->side() == gui().viewing_side()
1187  && !pc_.get_whiteboard()->allow_leader_to_move(*u)) {
1189  _("You cannot move your leader away from the keep with some planned recruits or recalls left."));
1190  return 0;
1191  }
1192  }
1193 
1194  LOG_NG << "move unit along route from " << steps.front() << " to " << steps.back() << "\n";
1195  std::size_t moves = actions::move_unit_and_record(steps, &pc_.get_undo_stack(), false, true, &interrupted);
1196 
1199 
1200  if(moves == 0)
1201  return 0;
1202 
1203  if(interrupted && moves + 1 < steps.size()) {
1204  // reselect the unit (for "press t to continue")
1205  select_hex(steps[moves], false, false, false);
1206  // the new discovery is more important than the new movement range
1207  show_partial_move_ = true;
1208  }
1209 
1210  return moves;
1211 }
1212 
1214  const map_location& attacker_loc, const map_location& defender_loc, int weapon_choice)
1215 {
1216  {
1217  // @todo Fix flickering/reach highlight anomaly after the weapon choice dialog is closed
1218  // This method should do the cleanup of highlights and selection but it doesn't work properly
1219 
1220  // gui().highlight_hex(map_location());
1221 
1222  gui().unhighlight_reach();
1224 
1225  // remove footsteps if any - useless for whiteboard as of now
1226  gui().set_route(nullptr);
1227 
1228  // do not keep the hex that we started from highlighted
1231  show_partial_move_ = false;
1232 
1233  // invalid after saving the move
1235  current_route_.steps.clear();
1236  }
1237 
1238  // create planned attack for whiteboard
1239  pc_.get_whiteboard()->save_temp_attack(attacker_loc, defender_loc, weapon_choice);
1240 }
1241 
1243  std::vector<battle_context>& bc_vector, unit_map::iterator attacker, unit_map::iterator defender)
1244 {
1245  int best = 0;
1246  for(unsigned int i = 0; i < attacker->attacks().size(); i++) {
1247  // skip weapons with attack_weight=0
1248  if(attacker->attacks()[i].attack_weight() > 0) {
1249  battle_context bc(pc_.get_units(), attacker->get_location(), defender->get_location(), i);
1250 
1251  // Don't include if the attacker's weapon has at least one active "disable" special.
1252  if(bc.get_attacker_stats().disable) {
1253  continue;
1254  }
1255 
1256  if(!bc_vector.empty() && bc.better_attack(bc_vector[best], 0.5)) {
1257  // as some weapons can be hidden, i is not a valid index into the resulting vector
1258  best = bc_vector.size();
1259  }
1260 
1261  bc_vector.emplace_back(std::move(bc));
1262  }
1263  }
1264 
1265  return best;
1266 }
1267 
1268 int mouse_handler::show_attack_dialog(const map_location& attacker_loc, const map_location& defender_loc)
1269 {
1270  game_board& board = pc_.gamestate().board_;
1271 
1272  unit_map::iterator attacker = board.units().find(attacker_loc);
1273  unit_map::iterator defender = board.units().find(defender_loc);
1274 
1275  if(!attacker || !defender) {
1276  ERR_NG << "One fighter is missing, can't attack";
1277  return -1; // abort, click will do nothing
1278  }
1279 
1280  std::vector<battle_context> bc_vector;
1281  const int best = fill_weapon_choices(bc_vector, attacker, defender);
1282 
1283  if(bc_vector.empty()) {
1284  gui2::show_transient_message("No Attacks", _("This unit has no usable weapons."));
1285 
1286  return -1;
1287  }
1288 
1289  gui2::dialogs::unit_attack dlg(attacker, defender, std::move(bc_vector), best);
1290 
1291  if(dlg.show()) {
1292  return dlg.get_selected_weapon();
1293  }
1294 
1295  return -1;
1296 }
1297 
1298 void mouse_handler::attack_enemy(const map_location& attacker_loc, const map_location& defender_loc, int choice)
1299 {
1300  try {
1301  attack_enemy_(attacker_loc, defender_loc, choice);
1302  } catch(const std::bad_alloc&) {
1303  lg::log_to_chat() << "Memory exhausted a unit has either a lot hitpoints or a negative amount.\n";
1304  ERR_WML << "Memory exhausted a unit has either a lot hitpoints or a negative amount.";
1305  }
1306 }
1307 
1308 void mouse_handler::attack_enemy_(const map_location& att_loc, const map_location& def_loc, int choice)
1309 {
1310  // NOTE: copy the values because the const reference may change!
1311  // (WML events and mouse inputs during animations may modify
1312  // the data of the caller)
1313  const map_location attacker_loc = att_loc;
1314  const map_location defender_loc = def_loc;
1315 
1316  unit* attacker = nullptr;
1317  const unit* defender = nullptr;
1318  std::vector<battle_context> bc_vector;
1319 
1320  {
1321  unit_map::iterator attacker_it = find_unit(attacker_loc);
1322  if(!attacker_it || attacker_it->side() != side_num_ || attacker_it->incapacitated()) {
1323  return;
1324  }
1325 
1326  unit_map::iterator defender_it = find_unit(defender_loc);
1327  if(!defender_it || current_team().is_enemy(defender_it->side()) == false || defender_it->incapacitated()) {
1328  return;
1329  }
1330 
1331  fill_weapon_choices(bc_vector, attacker_it, defender_it);
1332 
1333  attacker = &*attacker_it;
1334  defender = &*defender_it;
1335  }
1336 
1337  if(std::size_t(choice) >= bc_vector.size()) {
1338  return;
1339  }
1340 
1341  events::command_disabler disabler;
1342  const battle_context_unit_stats& att = bc_vector[choice].get_attacker_stats();
1343  const battle_context_unit_stats& def = bc_vector[choice].get_defender_stats();
1344 
1345  attacker->set_goto(map_location());
1346 
1348 
1349  // make the attacker's stats appear during the attack
1350  gui().display_unit_hex(attacker_loc);
1351 
1352  // remove highlighted hexes etc..
1356  gui().unhighlight_reach();
1357 
1358  current_team().set_action_bonus_count(1 + current_team().action_bonus_count());
1359  // TODO: change ToD to be location specific for the defender
1360 
1361  const tod_manager& tod_man = pc_.get_tod_manager();
1362 
1365  attacker_loc,
1366  defender_loc,
1367  att.attack_num,
1368  def.attack_num,
1369  attacker->type_id(),
1370  defender->type_id(),
1371  att.level,
1372  def.level,
1373  tod_man.turn(),
1374  tod_man.get_time_of_day()
1375  )
1376  );
1377 }
1378 
1379 std::set<map_location> mouse_handler::get_adj_enemies(const map_location& loc, int side) const
1380 {
1381  std::set<map_location> res;
1382 
1383  const team& uteam = pc_.get_teams()[side - 1];
1384 
1385  for(const map_location& aloc : get_adjacent_tiles(loc)) {
1387 
1388  if(i && uteam.is_enemy(i->side())) {
1389  res.insert(aloc);
1390  }
1391  }
1392 
1393  return res;
1394 }
1395 
1396 /**
1397  * Causes attackable hexes to be highlighted.
1398  *
1399  * This checks the hexes that the provided unit can attack. If there is a valid
1400  * target there, that location is inserted into current_paths_.destinations.
1401  */
1403 {
1404  // Cannot attack if no attacks are left.
1405  if(u->attacks_left() == 0) {
1406  return;
1407  }
1408 
1409  // Get the teams involved.
1410  const team& cur_team = current_team();
1411  const team& u_team = pc_.get_teams()[u->side() - 1];
1412 
1413  // Check each adjacent hex.
1414  for(const map_location& loc : get_adjacent_tiles(u->get_location())) {
1415  // No attack option shown if no visible unit present.
1416  // (Visible to current team, not necessarily the unit's team.)
1417  if(!pc_.get_map().on_board(loc)) {
1418  continue;
1419  }
1420 
1422  if(!i || !i->is_visible_to_team(cur_team, false)) {
1423  continue;
1424  }
1425 
1426  const unit& target = *i;
1427 
1428  // Can only attack non-petrified enemies.
1429  if(u_team.is_enemy(target.side()) && !target.incapacitated()) {
1431  }
1432  }
1433 }
1434 
1436 {
1437  game_board& board = pc_.gamestate().board_;
1438 
1439  if(!it) {
1440  return false;
1441  }
1442 
1443  if(it->side() != side_num_ || it->user_end_turn() || gui().fogged(it->get_location()) || !board.unit_can_move(*it)) {
1444  return false;
1445  }
1446 
1447  if(current_team().is_enemy(static_cast<int>(gui().viewing_team() + 1)) && it->invisible(it->get_location())) {
1448  return false;
1449  }
1450 
1451  if(it->get_hidden()) {
1452  return false;
1453  }
1454 
1455  return true;
1456 }
1457 
1458 void mouse_handler::cycle_units(const bool browse, const bool reverse)
1459 {
1460  unit_map& units = pc_.get_units();
1461 
1462  if(units.begin() == units.end()) {
1463  return;
1464  }
1465 
1467  if(!it) {
1468  it = units.begin();
1469  }
1470 
1471  const unit_map::const_iterator itx = it;
1472 
1473  do {
1474  if(reverse) {
1475  if(it == units.begin()) {
1476  it = units.end();
1477  }
1478 
1479  --it;
1480  } else {
1481  if(it == units.end()) {
1482  it = units.begin();
1483  } else {
1484  ++it;
1485  }
1486  }
1487  } while(it != itx && !unit_in_cycle(it));
1488 
1489  if(unit_in_cycle(it)) {
1490  gui().scroll_to_tile(it->get_location(), game_display::WARP);
1491 
1492  select_hex(it->get_location(), browse);
1493  // mouse_update(browse);
1494  }
1495 }
1496 
1498 {
1499  gui().unhighlight_reach();
1500 
1501  current_paths_ = new_paths;
1502  current_route_.steps.clear();
1503 
1504  gui().set_route(nullptr);
1505 
1506  pc_.get_whiteboard()->erase_temp_move();
1507 }
1508 
1510 {
1511  return pc_.get_teams()[gui().viewing_team()];
1512 }
1513 
1515 {
1516  return pc_.get_teams()[gui().viewing_team()];
1517 }
1518 
1520 {
1521  return pc_.get_teams()[side_num_ - 1];
1522 }
1523 
1525 
1527 {
1529 }
1530 
1532 {
1534 }
1535 
1536 } // end namespace events
pathfind::marked_route get_route(const unit *un, map_location go_to, team &team) const
bool mouse_motion_default(int x, int y, bool update)
This handles minimap scrolling and click-drag.
int drag_from_x_
Drag start position x.
bool is_keep(const map_location &loc) const
Definition: map.cpp:72
void set_current_paths(const pathfind::paths &new_paths)
Game board class.
Definition: game_board.hpp:51
marked_route mark_route(const plain_route &rt, bool update_move_cost)
Add marks on a route rt assuming that the unit located at the first hex of rt travels along it...
Definition: pathfind.cpp:648
unit_iterator end()
Definition: map.hpp:429
virtual const std::vector< team > & teams() const override
Definition: game_board.hpp:85
int fill_weapon_choices(std::vector< battle_context > &bc_vector, unit_map::iterator attacker, unit_map::iterator defender)
void invalidate_game_status()
Function to invalidate the game status displayed on the sidebar.
Definition: display.hpp:296
std::unique_ptr< game_lua_kernel > lua_kernel_
Definition: game_state.hpp:51
void get_adjacent_tiles(const map_location &a, map_location *res)
Function which, given a location, will place all adjacent locations in res.
Definition: location.cpp:475
virtual const unit_map & units() const override
Definition: game_board.hpp:112
void set(CURSOR_TYPE type)
Use the default parameter to reset cursors.
Definition: cursor.cpp:176
DIRECTION get_relative_dir(const map_location &loc, map_location::RELATIVE_DIR_MODE mode) const
Definition: location.cpp:227
This class represents a single unit of a specific type.
Definition: unit.hpp:121
const std::string & type_id() const
The id of this unit&#39;s type.
Definition: unit.cpp:1801
const unit_map & get_units() const
bool dragging_touch_
Finger drag init flag.
unit_map::const_iterator find_unit(const map_location &hex) const
const map_location hex_clicked_on(int x, int y) const
given x,y co-ordinates of an onscreen pixel, will return the location of the hex that this pixel corr...
Definition: display.cpp:599
map_location selected_hex_
bool dragging_started_
Actual drag flag.
Various functions that implement attacks and attack calculations.
pathfind::paths current_paths_
const map_location hovered_hex() const
Uses SDL and game_display::hex_clicked_on to fetch the hex the mouse is hovering, if applicable...
can_move_result unit_can_move(const unit &u) const
Work out what u can do - this does not check which player&#39;s turn is currently active, the result is calculated assuming that the unit&#39;s owner is currently active.
game_events::wml_event_pump & pump()
void show_transient_message(const std::string &title, const std::string &message, const std::string &image, const bool message_use_markup, const bool title_use_markup, const bool restore_background)
Shows a transient message to the user.
General purpose widgets.
map_location minimap_location_on(int x, int y)
given x,y co-ordinates of the mouse, will return the location of the hex in the minimap that the mous...
Definition: display.cpp:760
virtual const gamemap & map() const override
Definition: game_board.hpp:102
dest_vect destinations
Definition: pathfind.hpp:101
unit_iterator begin()
Definition: map.hpp:419
int viewing_side() const
Definition: display.hpp:107
std::shared_ptr< bool > whiteboard_lock
Definition: typedefs.hpp:56
std::size_t move_unit_and_record(const std::vector< map_location > &steps, undo_list *undo_stack, bool continued_move, bool show_move, bool *interrupted, move_unit_spectator *move_spectator)
Moves a unit across the board.
Definition: move.cpp:1227
const gamemap & get_map() const
bool move_unit_along_current_route()
Moves a unit along the currently cached route.
bool unit_in_cycle(unit_map::const_iterator it)
void touch_motion(int x, int y, const bool browse, bool update=false, map_location loc=map_location::null_location())
map_location previous_hex_
static std::string _(const char *str)
Definition: gettext.hpp:93
bool show(const unsigned auto_close_time=0)
Shows the window.
void mouse_motion(int x, int y, const bool browse, bool update=false, map_location loc=map_location::null_location())
Use update to force an update of the mouse state.
void select_hex(const map_location &hex, const bool browse, const bool highlight=true, const bool fire_event=true)
Definitions for the interface to Wesnoth Markup Language (WML).
std::shared_ptr< unit > unit_ptr
Definition: ptr.hpp:26
static mouse_handler * singleton_
void highlight_another_reach(const pathfind::paths &paths_list, const map_location &goal=map_location::null_location())
Add more paths to highlight.
void touch_action(const map_location hex, bool browse)
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:72
void process(int mousex, int mousey)
Definition: tooltips.cpp:194
bool minimap_scrolling_
minimap scrolling (scroll-drag) state flag
bool hex_hosts_unit(const map_location &hex) const
Unit exists on the hex, no matter if friend or foe.
void disable_units_highlight()
Use this to disable hovering an unit from highlighting its movement range.
unsigned int level
Definition: attack.hpp:71
int w() const
Effective map width.
Definition: map.hpp:50
Structure which holds a single route between one location and another.
Definition: pathfind.hpp:132
This file contains the settings handling of the widget library.
int drag_threshold() const
Minimum dragging distance to fire the drag&drop.
std::size_t move_unit_along_route(const std::vector< map_location > &steps, bool &interrupted)
Moves a unit across the board for a player.
#define ERR_WML
const time_of_day & get_time_of_day(int for_turn=0) const
Returns global time of day for the passed turn.
Definition: tod_manager.hpp:56
bool valid() const
Definition: location.hpp:89
Contains typedefs for the whiteboard.
std::vector< team > & get_teams()
Computes the statistics of a battle between an attacker and a defender unit.
Definition: attack.hpp:173
bool is_enemy(int n) const
Definition: team.hpp:255
Object which temporarily resets a unit&#39;s movement.
Definition: unit.hpp:1992
std::string path
Definition: game_config.cpp:39
void highlight_reach(const pathfind::paths &paths_list)
Sets the paths that are currently displayed as available for the unit to move along.
std::set< map_location > get_adj_enemies(const map_location &loc, int side) const
int attack_num
Index into unit->attacks() or -1 for none.
Definition: attack.hpp:54
Structure describing the statistics of a unit involved in the battle.
Definition: attack.hpp:51
Structure which holds a single route and marks for special events.
Definition: pathfind.hpp:141
std::shared_ptr< wb::manager > get_whiteboard() const
bool fogged(const map_location &loc) const
Returns true if location (x,y) is covered in fog.
Definition: display.cpp:745
static config get_attack(const map_location &a, const map_location &b, int att_weapon, int def_weapon, const std::string &attacker_type_id, const std::string &defender_type_id, int attacker_lvl, int defender_lvl, const std::size_t turn, const time_of_day &t)
bool point_in_rect(int x, int y, const SDL_Rect &rect)
Tests whether a point is inside a rectangle.
Definition: rect.cpp:23
bool right_click_show_menu(int x, int y, const bool browse)
Called in the default right_click when the context menu is about to be shown, can be used for preproc...
Encapsulates the map of the game.
Definition: location.hpp:38
Various functions related to moving units.
void enable_units_highlight()
When unit highlighting is disabled, call this when the mouse no longer hovers any unit to enable high...
unit_iterator find(std::size_t id)
Definition: map.cpp:310
actions::undo_list & get_undo_stack()
static lg::log_domain log_engine("engine")
bool click(int mousex, int mousey)
Definition: tooltips.cpp:212
pointer get_shared_ptr() const
This is exactly the same as operator-> but it&#39;s slightly more readable, and can replace &*iter syntax...
Definition: map.hpp:218
bool fire_event(const ui_event event, std::vector< std::pair< widget *, ui_event >> &event_chain, widget *dispatcher, widget *w, F &&... params)
Helper function for fire_event.
std::size_t i
Definition: function.cpp:967
map_location current_unit_attacks_from(const map_location &loc) const
void scroll_to_tile(const map_location &loc, SCROLL_TYPE scroll_type=ONSCREEN, bool check_fogged=true, bool force=true)
Scroll such that location loc is on-screen.
Definition: display.cpp:2203
virtual void highlight_hex(map_location hex) override
Function to highlight a location.
void attack_enemy(const map_location &attacker_loc, const map_location &defender_loc, int choice)
unit_map::iterator selected_unit()
void save_whiteboard_attack(const map_location &attacker_loc, const map_location &defender_loc, int weapon_choice)
virtual void select_hex(map_location hex) override
Function to display a location as selected.
Define the game&#39;s event mechanism.
map_location previous_free_hex_
ONLY IF whiteboard is currently active, applies the planned unit map for the duration of the struct&#39;s...
Definition: manager.hpp:273
CURSOR_TYPE get()
Definition: cursor.cpp:216
bool on_board(const map_location &loc) const
Tell if a location is on the map.
Definition: map.cpp:385
DIRECTION
Valid directions which can be moved in our hexagonal world.
Definition: location.hpp:40
mouse_handler(game_display *gui, play_controller &pc)
void set_goto(const map_location &new_goto)
Sets this unit&#39;s long term destination.
Definition: unit.hpp:1383
void cycle_units(const bool browse, const bool reverse=false)
#define LOG_NG
play_controller & pc_
pump_result_t fire(const std::string &event, const entity_location &loc1=entity_location::null_entity, const entity_location &loc2=entity_location::null_entity, const config &data=config())
Function to fire an event.
Definition: pump.cpp:481
Handling of system events.
Definition: manager.hpp:43
static bool run_and_throw(const std::string &commandname, const config &data, bool use_undo=true, bool show=true, synced_command::error_handler_function error_handler=default_error_function)
map_location last_hex_
last highlighted hex
void show_attack_options(const unit_map::const_iterator &u)
Causes attackable hexes to be highlighted.
game_state & gamestate()
bool unhighlight_reach()
Reset highlighting of paths.
const tod_manager & get_tod_manager() const
This shows the dialog for attacking units.
Definition: unit_attack.hpp:41
const map_location & get_location() const
The current map location this unit is at.
Definition: unit.hpp:1346
game_board board_
Definition: game_state.hpp:47
std::size_t viewing_team() const
The viewing team is the team currently viewing the game.
Definition: display.hpp:106
Various functions that implement the undoing (and redoing) of in-game commands.
bool contains(const map_location &) const
Definition: pathfind.cpp:515
void play_UI_sound(const std::string &files)
Definition: sound.cpp:1052
void set_action_bonus_count(const int count)
Definition: team.hpp:226
Standard logging facilities (interface).
int current_side() const
Returns the number of the side whose turn it is.
pathfind::marked_route current_route_
const teleport_map get_teleport_locations(const unit &u, const team &viewing_team, bool see_all, bool ignore_units, bool check_vision)
Definition: teleport.cpp:270
Object which contains all the possible locations a unit can move to, with associated best routes to t...
Definition: pathfind.hpp:72
static const map_location & null_location()
Definition: location.hpp:81
Container associating units to locations.
Definition: map.hpp:98
unit * find_unit_nonowning(const map_location &hex)
bool incapacitated() const
Check if the unit has been petrified.
Definition: unit.hpp:894
int turn() const
int side() const
The side this unit belongs to.
Definition: unit.hpp:334
int side_number
Definition: game_info.hpp:40
static void reverse(lua_State *L, StkId from, StkId to)
Definition: lapi.cpp:203
const_iterator find(const map_location &) const
Definition: pathfind.cpp:479
void set_side(int side_number)
Ensures that the real unit map is active for the duration of the struct&#39;s life.
Definition: manager.hpp:283
void attack_enemy_(const map_location &attacker_loc, const map_location &defender_loc, int choice)
plain_route a_star_search(const map_location &src, const map_location &dst, double stop_at, const cost_calculator &calc, const std::size_t width, const std::size_t height, const teleport_map *teleports, bool border)
bool valid() const
Definition: map.hpp:274
#define ERR_NG
void set_route(const pathfind::marked_route *route)
Sets the route along which footsteps are drawn to show movement of a unit.
static lg::log_domain log_wml("wml")
game_display & gui()
Due to the way this class is constructed we can assume that the display* gui_ member actually points ...
static map_location::DIRECTION n
void clear_attack_indicator()
int h() const
Effective map height.
Definition: map.hpp:53
bool scroll(int xmov, int ymov, bool force=false)
Scrolls the display by xmov,ymov pixels.
Definition: display.cpp:1926
void display_unit_hex(map_location hex)
Change the unit to be displayed in the sidebar.
unit_map::iterator find_visible_unit(const map_location &loc, const team &current_team, bool see_all=false)
Definition: game_board.cpp:183
bool simple_warp_
MMB click (on game map) state flag.
void set_attack_indicator(const map_location &src, const map_location &dst)
Set the attack direction indicator.
std::vector< map_location > & steps
Definition: pathfind.hpp:187
void insert(const map_location &)
Definition: pathfind.cpp:486
This object is used to temporary move a unit in the unit map, swapping out any unit that is already t...
Definition: game_board.hpp:229
int show_attack_dialog(const map_location &attacker_loc, const map_location &defender_loc)
int drag_from_y_
Drag start position y.
void move_action(bool browse)
Overridden in derived class.
std::stringstream & log_to_chat()
Use this to show WML errors in the ingame chat.
Definition: log.cpp:289
void select_or_action(bool browse)