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