The Battle for Wesnoth  1.15.9+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;
164  if( pc_.get_map().on_board(last_hex_) ) {
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().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  if(new_hex == map_location::null_location()) {
385  new_hex = gui().hex_clicked_on(x, y);
386  }
387 
388  if(new_hex != last_hex_) {
389  if(game_lua_kernel* lk = pc_.gamestate().lua_kernel_.get()) {
390  lk->mouse_over_hex_callback(new_hex);
391  }
392 
393  update = true;
394 
395  if(pc_.get_map().on_board(last_hex_)) {
396  // we store the previous hexes used to propose attack direction
398 
399  // the hex of the selected unit is also "free"
400  { // start planned unit map scope
404  }
405  } // end planned unit map scope
406  }
407 
408  last_hex_ = new_hex;
409  }
410 
411  if(reachmap_invalid_) {
412  update = true;
413  }
414 
415  if(!update) {
416  return;
417  }
418 
419  if(reachmap_invalid_) {
420  reachmap_invalid_ = false;
421 
423  bool selected_hex_has_unit;
424  { // start planned unit map scope
425  wb::future_map_if_active planned_unit_map;
426  selected_hex_has_unit = find_unit(selected_hex_).valid();
427  } // end planned unit map scope
428 
429  if(selected_hex_.valid() && selected_hex_has_unit) {
430  // reselect the unit without firing events (updates current_paths_)
431  select_hex(selected_hex_, true);
432  }
433 
434  // we do never deselect here, mainly because of canceled attack-move
435  }
436  }
437 
438  // reset current_route_ and current_paths if not valid anymore
439  // we do it before cursor selection, because it uses current_paths_
440  if(!pc_.get_map().on_board(new_hex)) {
441  current_route_.steps.clear();
442  gui().set_route(nullptr);
443  pc_.get_whiteboard()->erase_temp_move();
444  }
445 
446  if(unselected_paths_) {
447  unselected_paths_ = false;
450  } else if(over_route_) {
451  over_route_ = false;
452  current_route_.steps.clear();
453  gui().set_route(nullptr);
454  pc_.get_whiteboard()->erase_temp_move();
455  }
456 
457  gui().highlight_hex(new_hex);
458  pc_.get_whiteboard()->on_mouseover_change(new_hex);
459 
461  unit_map::iterator mouseover_unit;
462  map_location attack_from;
463 
464  { // start planned unit map scope
465  wb::future_map_if_active planned_unit_map;
466  selected_unit = find_unit(selected_hex_);
467  mouseover_unit = find_unit(new_hex);
468 
469  // we search if there is an attack possibility and where
470  attack_from = current_unit_attacks_from(new_hex);
471 
472  // see if we should show the normal cursor, the movement cursor, or
473  // the attack cursor
474  // If the cursor is on WAIT, we don't change it and let the setter
475  // of this state end it
476  if(cursor::get() != cursor::WAIT) {
477  if(selected_unit && selected_unit->side() == side_num_ && !selected_unit->incapacitated() && !browse) {
478  if(attack_from.valid()) {
480  } else if(!mouseover_unit && current_paths_.destinations.contains(new_hex)) {
482  } else {
483  // selected unit can't attack or move there
485  }
486  } else {
487  // no selected unit or we can't move it
488 
489  if(selected_hex_.valid() && mouseover_unit && mouseover_unit->side() == side_num_) {
490  // empty hex field selected and unit on our site under the cursor
492  } else {
494  }
495  }
496  }
497  } // end planned unit map scope
498 
499  // show (or cancel) the attack direction indicator
500  if(attack_from.valid() && (!browse || pc_.get_whiteboard()->is_active())) {
501  gui().set_attack_indicator(attack_from, new_hex);
502  } else {
504  }
505 
506  unit_ptr un; // will later point to unit at mouseover_hex_
507 
508  // the destination is the pointed hex or the adjacent hex
509  // used to attack it
510  map_location dest;
511  unit_map::const_iterator dest_un;
512  /* start planned unit map scope*/
513  {
515  if(attack_from.valid()) {
516  dest = attack_from;
517  dest_un = find_unit(dest);
518  } else {
519  dest = new_hex;
520  dest_un = find_unit(new_hex);
521  }
522 
523  if(dest == selected_hex_ || dest_un) {
524  current_route_.steps.clear();
525  gui().set_route(nullptr);
526  pc_.get_whiteboard()->erase_temp_move();
527  } else if(!current_paths_.destinations.empty() && pc_.get_map().on_board(selected_hex_) && pc_.get_map().on_board(new_hex)) {
528  if(selected_unit && !selected_unit->incapacitated()) {
529  // Show the route from selected unit to mouseover hex
530  current_route_ = get_route(&*selected_unit, dest, viewing_team());
531 
532  pc_.get_whiteboard()->create_temp_move();
533 
534  if(!browse) {
536  }
537  }
538  }
539 
540  if(pc_.get_map().on_board(selected_hex_) && !selected_unit && mouseover_unit.valid() && mouseover_unit) {
541  // Show the route from selected hex to mouseover unit
542  current_route_ = get_route(&*mouseover_unit, selected_hex_, viewing_team());
543 
544  pc_.get_whiteboard()->create_temp_move();
545 
546  if(!browse) {
548  }
549  } else if(!selected_unit) {
550  current_route_.steps.clear();
551  gui().set_route(nullptr);
552  pc_.get_whiteboard()->erase_temp_move();
553  }
554 
555  if(mouseover_unit) {
556  un = mouseover_unit.get_shared_ptr();
557  } else {
558  un.reset();
559  }
560  } /*end planned unit map scope*/
561 
562  if(!selected_hex_.valid() && un && current_paths_.destinations.empty() && !gui().fogged(un->get_location())) {
563  /*
564  * Only process unit if toggler not preventing normal unit
565  * processing. This can happen e.g. if, after activating 'show
566  * [best possible] enemy movements' through the UI menu, the
567  * mouse cursor lands on a hex with unit in it.
568  */
570  if(un->side() == side_num_) {
571  // unit is on our team, show path if the unit has one
572  const map_location go_to = un->get_goto();
573  if(pc_.get_map().on_board(go_to)) {
575  { // start planned unit map scope
577  route = get_route(un.get(), go_to, current_team());
578  } // end planned unit map scope
579  gui().set_route(&route);
580  }
581  over_route_ = true;
582 
584  current_paths_ = pathfind::paths(*un, false, true, viewing_team(), path_turns_);
585  } else {
586  // unit under cursor is not on our team
587  // Note: planned unit map must be activated after this is done,
588  // since the future state includes changes to units' movement.
589  unit_movement_resetter move_reset(*un);
590 
592  current_paths_ = pathfind::paths(*un, false, true, viewing_team(), path_turns_);
593  }
594 
595  unselected_paths_ = true;
597 
598  }
599  }
600 
601  if(!un && preventing_units_highlight_) {
602  // Cursor on empty hex, turn unit highlighting back on.
604  }
605 }
606 
608 {
610  if(res) {
611  return res;
612  }
613 
614  return find_unit(last_hex_);
615 }
616 
618 {
620  if(it.valid()) {
621  return it;
622  }
623 
624  return pc_.get_units().end();
625 }
626 
628 {
630 }
631 
633 {
635  return it.valid() ? &*it : nullptr;
636 }
637 
639 {
641  return it.valid() ? &*it : nullptr;
642 }
643 
645 {
646  int x = -1;
647  int y = -1;
648  SDL_GetMouseState(&x, &y);
649  return gui_->hex_clicked_on(x, y);
650 }
651 
653 {
654  return find_unit(hex).valid();
655 }
656 
658 {
659  if(loc == selected_hex_) {
660  return map_location();
661  }
662 
663  bool wb_active = pc_.get_whiteboard()->is_active();
664 
665  {
666  // Check the unit SOURCE of the attack
667 
668  // Check that there's a selected unit
669  const unit_map::const_iterator source_unit = find_unit(selected_hex_);
670 
671  bool source_eligible = source_unit.valid();
672  if(!source_eligible) {
673  return map_location();
674  }
675 
676  // The selected unit must at least belong to the player currently controlling this client.
677  source_eligible &= source_unit->side() == gui_->viewing_side();
678  if(!source_eligible) {
679  return map_location();
680  }
681 
682  // In addition:
683  // - If whiteboard is enabled, we allow planning attacks outside of player's turn
684  // - If whiteboard is disabled, it must be the turn of the player controlling this client
685  if(!wb_active) {
686  source_eligible &= gui_->viewing_side() == pc_.current_side();
687  if(!source_eligible) {
688  return map_location();
689  }
690  }
691 
692  // Unit must have attacks left
693  source_eligible &= source_unit->attacks_left() != 0;
694  if(!source_eligible) {
695  return map_location();
696  }
697 
698  // Check the unit TARGET of the attack
699 
700  const team& viewer = viewing_team();
701 
702  // Check that there's a unit at the target location
703  const unit_map::const_iterator target_unit = find_unit(loc);
704 
705  bool target_eligible = target_unit.valid();
706  if(!target_eligible) {
707  return map_location();
708  }
709 
710  // The player controlling this client must be an enemy of the target unit's side
711  target_eligible &= viewer.is_enemy(target_unit->side());
712  if(!target_eligible) {
713  return map_location();
714  }
715 
716  // Sanity check: source and target of the attack shouldn't be on the same team
717  assert(source_unit->side() != target_unit->side());
718 
719  target_eligible &= !target_unit->incapacitated();
720  if(!target_eligible) {
721  return map_location();
722  }
723  }
724 
726  const map_location::DIRECTION second_preferred = loc.get_relative_dir(previous_free_hex_);
727 
728  int best_rating = 100; // smaller is better
729 
730  map_location res;
731  const auto adj = get_adjacent_tiles(loc);
732 
733  for(std::size_t n = 0; n < adj.size(); ++n) {
734  if(pc_.get_map().on_board(adj[n]) == false) {
735  continue;
736  }
737 
738  if(adj[n] != selected_hex_ && find_unit(adj[n])) {
739  continue;
740  }
741 
742  if(current_paths_.destinations.contains(adj[n])) {
743  static const std::size_t NDIRECTIONS = map_location::NDIRECTIONS;
744 
745  unsigned int difference = std::abs(static_cast<int>(preferred - n));
746  if(difference > NDIRECTIONS / 2) {
747  difference = NDIRECTIONS - difference;
748  }
749 
750  unsigned int second_difference = std::abs(static_cast<int>(second_preferred - n));
751  if(second_difference > NDIRECTIONS / 2) {
752  second_difference = NDIRECTIONS - second_difference;
753  }
754 
755  const int rating = difference * 2 + (second_difference > difference);
756  if(rating < best_rating || res.valid() == false) {
757  best_rating = rating;
758  res = adj[n];
759  }
760  }
761  }
762 
763  return res;
764 }
765 
767 {
768  game_board& board = pc_.gamestate().board_;
769 
770  // The pathfinder will check unit visibility (fogged/stealthy).
771  const pathfind::shortest_path_calculator calc(*un, team, board.teams(), board.map());
772 
774 
775  pathfind::plain_route route;
776 
777  route = pathfind::a_star_search(
778  un->get_location(), go_to, 10000.0, calc, board.map().w(), board.map().h(), &allowed_teleports);
779 
780  return mark_route(route);
781 }
782 
783 bool mouse_handler::right_click_show_menu(int x, int y, const bool /*browse*/)
784 {
786  unselected_reach_ = false;
787  return false;
788  }
789 
790  return sdl::point_in_rect(x, y, gui().map_area());
791 }
792 
794 {
795  if(!pc_.get_map().on_board(last_hex_)) {
797  return;
798  }
799 
800  // Load whiteboard partial moves
801  wb::future_map_if_active planned_unit_map;
802 
803  if(game_lua_kernel* lk = pc_.gamestate().lua_kernel_.get()) {
804  lk->select_hex_callback(last_hex_);
805  }
806 
809 
810  if(clicked_u && (!selected_u || selected_u->side() != side_num_ ||
811  (clicked_u->side() == side_num_ && clicked_u->id() != selected_u->id()))
812  ) {
813  select_hex(last_hex_, false);
814  } else {
815  move_action(browse);
816  }
817 }
818 
819 void mouse_handler::move_action(bool browse)
820 {
821  // Lock whiteboard activation state to avoid problems due to
822  // its changing while an animation takes place.
823  wb::whiteboard_lock wb_lock = pc_.get_whiteboard()->get_activation_state_lock();
824 
825  // we use the last registered highlighted hex
826  // since it's what update our global state
827  map_location hex = last_hex_;
828 
829  // TODO
830  // // Clicks on border hexes mean to deselect.
831  // // (Check this before doing processing that might not be needed.)
832  // if ( !pc_.get_map().on_board(hex) ) {
833  // deselect_hex();
834  // return false;
835  // }
836 
837  unit* u = nullptr;
838  const unit* clicked_u = nullptr;
839 
840  map_location src;
841  pathfind::paths orig_paths;
842  map_location attack_from;
843 
844  { // start planned unit map scope
845  wb::future_map_if_active planned_unit_map;
847 
848  // if the unit is selected and then itself clicked on,
849  // any goto command is canceled
850  if(u && !browse && selected_hex_ == hex && u->side() == side_num_) {
851  u->set_goto(map_location());
852  }
853 
854  clicked_u = find_unit_nonowning(hex);
855 
856  src = selected_hex_;
857  orig_paths = current_paths_;
858  attack_from = current_unit_attacks_from(hex);
859  } // end planned unit map scope
860 
861  // see if we're trying to do a attack or move-and-attack
862  if((!browse || pc_.get_whiteboard()->is_active()) && attack_from.valid()) {
863  // Ignore this command if commands are disabled.
864  if(commands_disabled) {
865  return;
866  }
867 
868  if(((u != nullptr && u->side() == side_num_) || pc_.get_whiteboard()->is_active()) && clicked_u != nullptr) {
869  if(attack_from == selected_hex_) { // no move needed
870  int choice = -1;
871  {
872  wb::future_map_if_active planned_unit_map; // start planned unit map scope
873  choice = show_attack_dialog(attack_from, clicked_u->get_location());
874  } // end planned unit map scope
875 
876  if(choice >= 0) {
877  if(pc_.get_whiteboard()->is_active()) {
878  save_whiteboard_attack(attack_from, clicked_u->get_location(), choice);
879  } else {
880  // clear current unit selection so that any other unit selected
881  // triggers a new selection
883 
884  attack_enemy(u->get_location(), clicked_u->get_location(), choice);
885  }
886  }
887 
888  return;
889  } else {
890  int choice = -1; // for the attack dialog
891 
892  {
893  wb::future_map_if_active planned_unit_map; // start planned unit map scope
894  // we will now temporary move next to the enemy
895  pathfind::paths::dest_vect::const_iterator itor = current_paths_.destinations.find(attack_from);
896  if(itor == current_paths_.destinations.end()) {
897  // can't reach the attacking location
898  // not supposed to happen, so abort
899  return;
900  }
901 
902  // block where we temporary move the unit
903  {
904  temporary_unit_mover temp_mover(pc_.get_units(), src, attack_from, itor->move_left);
905  choice = show_attack_dialog(attack_from, clicked_u->get_location());
906  }
907 
908  if(choice < 0) {
909  // user hit cancel, don't start move+attack
910  return;
911  }
912  } // end planned unit map scope
913 
914  if(pc_.get_whiteboard()->is_active()) {
915  save_whiteboard_attack(attack_from, hex, choice);
916  } else {
917  bool not_interrupted = move_unit_along_current_route();
918  bool alt_unit_selected = (selected_hex_ != src);
919  src = selected_hex_;
920  // clear current unit selection so that any other unit selected
921  // triggers a new selection
923 
924  if(not_interrupted)
925  attack_enemy(attack_from, hex, choice); // Fight !!
926 
927  // TODO: Maybe store the attack choice so "press t to continue"
928  // can also continue the attack?
929 
930  if(alt_unit_selected && !selected_hex_.valid()) {
931  // reselect other unit if selected during movement animation
932  select_hex(src, browse);
933  }
934  }
935 
936  return;
937  }
938  }
939  }
940  // otherwise we're trying to move to a hex
941  else if(
942  // The old use case: move selected unit to mouse hex field.
943  (
944  (!browse || pc_.get_whiteboard()->is_active())
945  && selected_hex_.valid()
946  && selected_hex_ != hex
947  && u != nullptr
948  && (u->side() == side_num_ || pc_.get_whiteboard()->is_active())
949  && !clicked_u
950  && !current_route_.steps.empty()
951  && current_route_.steps.front() == selected_hex_
952  )
953  || // The new use case: move mouse unit to selected hex field.
954  (
955  (!browse || pc_.get_whiteboard()->is_active())
956  && selected_hex_.valid()
957  && selected_hex_ != hex
958  && clicked_u
959  && !current_route_.steps.empty()
960  && current_route_.steps.back() == selected_hex_
961  && !u
962  && clicked_u->side() == side_num_
963  )
964  ) {
965  // Ignore this command if commands are disabled.
966  if(commands_disabled) {
967  return;
968  }
969 
970  // If the whiteboard is active, it intercepts any unit movement.
971  if(pc_.get_whiteboard()->is_active()) {
972  // Deselect the current hex, and create planned move for whiteboard.
974 
977  gui().set_route(nullptr);
978 
979  show_partial_move_ = false;
980 
982 
984  current_route_.steps.clear();
985 
986  pc_.get_whiteboard()->save_temp_move();
987 
988  // Otherwise proceed to normal unit movement
989  } else {
990  // Don't move if the unit already has actions
991  // from the whiteboard.
992  if(pc_.get_whiteboard()->unit_has_actions(u ? u : clicked_u)) {
993  return;
994  }
995 
997 
998  // During the move, we may have selected another unit
999  // (but without triggering a select event (command was disabled)
1000  // in that case reselect it now to fire the event (+ anim & sound)
1001  if(selected_hex_ != src) {
1002  select_hex(selected_hex_, browse);
1003  }
1004  }
1005 
1006  return;
1007  }
1008 }
1009 
1010 void mouse_handler::touch_action(const map_location touched_hex, bool browse)
1011 {
1012  unit_map::iterator unit = find_unit(touched_hex);
1013 
1014  if (touched_hex.valid() && unit.valid() && !unit->get_hidden()) {
1015  select_or_action(browse);
1016  } else {
1017  deselect_hex();
1018  }
1019 }
1020 
1021 void mouse_handler::select_hex(const map_location& hex, const bool browse, const bool highlight, const bool fire_event)
1022 {
1023  selected_hex_ = hex;
1024 
1027  gui().set_route(nullptr);
1028 
1029  show_partial_move_ = false;
1030 
1031  wb::future_map_if_active planned_unit_map; // lasts for whole method
1032 
1034 
1035  if(selected_hex_.valid() && unit.valid() && !unit->get_hidden()) {
1036  next_unit_ = unit->get_location();
1037 
1038  {
1039  current_paths_ = pathfind::paths(*unit, false, true, viewing_team(), path_turns_);
1040  }
1041 
1042  if(highlight) {
1043  show_attack_options(unit);
1045  }
1046 
1047  // The highlight now comes from selection
1048  // and not from the mouseover on an enemy
1049  unselected_paths_ = false;
1050  gui().set_route(nullptr);
1051 
1052  // Selection have impact only if we are not observing and it's our unit
1053  if((!commands_disabled || pc_.get_whiteboard()->is_active()) && unit->side() == gui().viewing_side()) {
1054  if(!(browse || pc_.get_whiteboard()->unit_has_actions(&*unit))) {
1055  sound::play_UI_sound("select-unit.wav");
1056 
1057  unit->anim_comp().set_selecting();
1058 
1059  if(fire_event) {
1060  // Ensure unit map is back to normal while event is fired
1061  wb::real_map srum;
1062  pc_.pump().fire("select", hex);
1063  // end forced real unit map
1064  }
1065  }
1066  }
1067 
1068  return;
1069  }
1070 
1071  if(selected_hex_.valid() && !unit) {
1072  // Compute unit in range of the empty selected_hex field
1073 
1075 
1076  pathfind::paths reaching_unit_locations;
1077 
1078  pathfind::paths clicked_location;
1079  clicked_location.destinations.insert(hex);
1080 
1081  for(unit_map::iterator u = pc_.get_units().begin(); u != pc_.get_units().end();
1082  ++u) {
1083  bool invisible = u->invisible(u->get_location());
1084 
1085  if(!gui_->fogged(u->get_location()) && !u->incapacitated() && !invisible) {
1086  const pathfind::paths& path =
1087  pathfind::paths(*u, false, true, viewing_team(), path_turns_, false, false);
1088 
1089  if(path.destinations.find(hex) != path.destinations.end()) {
1090  reaching_unit_locations.destinations.insert(u->get_location());
1091  gui_->highlight_another_reach(clicked_location);
1092  }
1093  }
1094  }
1095 
1096  gui_->highlight_another_reach(reaching_unit_locations);
1097  } else {
1098  if(!pc_.get_units().find(last_hex_)) {
1100  }
1101 
1103  current_route_.steps.clear();
1104 
1105  pc_.get_whiteboard()->on_deselect_hex();
1106  }
1107 }
1108 
1110 {
1111  select_hex(map_location(), true);
1112 }
1113 
1114 /**
1115  * Moves a unit along the currently cached route.
1116  *
1117  * @returns true if the end of the route was reached and no information was
1118  * uncovered that would warrant interrupting a chain of actions;
1119  * false otherwise.
1120  */
1122 {
1123  // Copy the current route to ensure it remains valid throughout the animation.
1124  const std::vector<map_location> steps = current_route_.steps;
1125 
1126  // do not show footsteps during movement
1127  gui().set_route(nullptr);
1128  gui().unhighlight_reach();
1129 
1130  // do not keep the hex highlighted that we started from
1133 
1134  bool interrupted = false;
1135  if(steps.size() > 1) {
1136  std::size_t num_moves = move_unit_along_route(steps, interrupted);
1137 
1138  interrupted = interrupted || num_moves + 1 < steps.size();
1139  next_unit_ = steps[num_moves];
1140  }
1141 
1142  // invalid after the move
1144  current_route_.steps.clear();
1145 
1146  return !interrupted;
1147 }
1148 
1149 /**
1150  * Moves a unit across the board for a player.
1151  * This is specifically for movement at the time it is initiated by a player,
1152  * whether via a mouse click or executing whiteboard actions. Continued moves
1153  * (including goto execution) can bypass this and call actions::move_unit() directly.
1154  * This function call may include time for an animation, so make sure the
1155  * provided route will remain unchanged (the caller should probably make a local
1156  * copy).
1157  *
1158  * @param[in] steps The route to be traveled. The unit to be moved is at the beginning of this route.
1159  * @param[out] interrupted This is set to true if information was uncovered that warrants interrupting a chain of
1160  * actions (and set to false otherwise).
1161  *
1162  * @returns The number of hexes entered. This can safely be used as an index
1163  * into steps to get the location where movement ended, provided
1164  * steps is not empty (the return value is guaranteed to be less
1165  * than steps.size() ).
1166  */
1167 std::size_t mouse_handler::move_unit_along_route(const std::vector<map_location>& steps, bool& interrupted)
1168 {
1169  if(steps.empty()) {
1170  interrupted = false;
1171  return 0;
1172  }
1173 
1174  // Default return value.
1175  interrupted = true;
1176 
1177  // If this is a leader on a keep, ask permission to the whiteboard to move it
1178  // since otherwise it may cause planned recruits to be erased.
1179  if(pc_.get_map().is_keep(steps.front())) {
1180  unit_map::const_iterator const u = pc_.get_units().find(steps.front());
1181 
1182  if(u && u->can_recruit() && u->side() == gui().viewing_side()
1183  && !pc_.get_whiteboard()->allow_leader_to_move(*u)) {
1185  _("You cannot move your leader away from the keep with some planned recruits or recalls left."));
1186  return 0;
1187  }
1188  }
1189 
1190  LOG_NG << "move unit along route from " << steps.front() << " to " << steps.back() << "\n";
1191  std::size_t moves = actions::move_unit_and_record(steps, &pc_.get_undo_stack(), false, true, &interrupted);
1192 
1195 
1196  if(moves == 0)
1197  return 0;
1198 
1199  if(interrupted && moves + 1 < steps.size()) {
1200  // reselect the unit (for "press t to continue")
1201  select_hex(steps[moves], false, false, false);
1202  // the new discovery is more important than the new movement range
1203  show_partial_move_ = true;
1204  }
1205 
1206  return moves;
1207 }
1208 
1210  const map_location& attacker_loc, const map_location& defender_loc, int weapon_choice)
1211 {
1212  {
1213  // @todo Fix flickering/reach highlight anomaly after the weapon choice dialog is closed
1214  // This method should do the cleanup of highlights and selection but it doesn't work properly
1215 
1216  // gui().highlight_hex(map_location());
1217 
1218  gui().unhighlight_reach();
1220 
1221  // remove footsteps if any - useless for whiteboard as of now
1222  gui().set_route(nullptr);
1223 
1224  // do not keep the hex that we started from highlighted
1227  show_partial_move_ = false;
1228 
1229  // invalid after saving the move
1231  current_route_.steps.clear();
1232  }
1233 
1234  // create planned attack for whiteboard
1235  pc_.get_whiteboard()->save_temp_attack(attacker_loc, defender_loc, weapon_choice);
1236 }
1237 
1239  std::vector<battle_context>& bc_vector, unit_map::iterator attacker, unit_map::iterator defender)
1240 {
1241  int best = 0;
1242  for(unsigned int i = 0; i < attacker->attacks().size(); i++) {
1243  // skip weapons with attack_weight=0
1244  if(attacker->attacks()[i].attack_weight() > 0) {
1245  battle_context bc(pc_.get_units(), attacker->get_location(), defender->get_location(), i);
1246 
1247  // Don't include if the attacker's weapon has at least one active "disable" special.
1248  if(bc.get_attacker_stats().disable) {
1249  continue;
1250  }
1251 
1252  if(!bc_vector.empty() && bc.better_attack(bc_vector[best], 0.5)) {
1253  // as some weapons can be hidden, i is not a valid index into the resulting vector
1254  best = bc_vector.size();
1255  }
1256 
1257  bc_vector.emplace_back(std::move(bc));
1258  }
1259  }
1260 
1261  return best;
1262 }
1263 
1264 int mouse_handler::show_attack_dialog(const map_location& attacker_loc, const map_location& defender_loc)
1265 {
1266  game_board& board = pc_.gamestate().board_;
1267 
1268  unit_map::iterator attacker = board.units().find(attacker_loc);
1269  unit_map::iterator defender = board.units().find(defender_loc);
1270 
1271  if(!attacker || !defender) {
1272  ERR_NG << "One fighter is missing, can't attack";
1273  return -1; // abort, click will do nothing
1274  }
1275 
1276  std::vector<battle_context> bc_vector;
1277  const int best = fill_weapon_choices(bc_vector, attacker, defender);
1278 
1279  if(bc_vector.empty()) {
1280  gui2::show_transient_message("No Attacks", _("This unit has no usable weapons."));
1281 
1282  return -1;
1283  }
1284 
1285  gui2::dialogs::unit_attack dlg(attacker, defender, std::move(bc_vector), best);
1286 
1287  if(dlg.show()) {
1288  return dlg.get_selected_weapon();
1289  }
1290 
1291  return -1;
1292 }
1293 
1294 void mouse_handler::attack_enemy(const map_location& attacker_loc, const map_location& defender_loc, int choice)
1295 {
1296  try {
1297  attack_enemy_(attacker_loc, defender_loc, choice);
1298  } catch(const std::bad_alloc&) {
1299  lg::wml_error() << "Memory exhausted a unit has either a lot hitpoints or a negative amount.\n";
1300  }
1301 }
1302 
1303 void mouse_handler::attack_enemy_(const map_location& att_loc, const map_location& def_loc, int choice)
1304 {
1305  // NOTE: copy the values because the const reference may change!
1306  // (WML events and mouse inputs during animations may modify
1307  // the data of the caller)
1308  const map_location attacker_loc = att_loc;
1309  const map_location defender_loc = def_loc;
1310 
1311  unit* attacker = nullptr;
1312  const unit* defender = nullptr;
1313  std::vector<battle_context> bc_vector;
1314 
1315  {
1316  unit_map::iterator attacker_it = find_unit(attacker_loc);
1317  if(!attacker_it || attacker_it->side() != side_num_ || attacker_it->incapacitated()) {
1318  return;
1319  }
1320 
1321  unit_map::iterator defender_it = find_unit(defender_loc);
1322  if(!defender_it || current_team().is_enemy(defender_it->side()) == false || defender_it->incapacitated()) {
1323  return;
1324  }
1325 
1326  fill_weapon_choices(bc_vector, attacker_it, defender_it);
1327 
1328  attacker = &*attacker_it;
1329  defender = &*defender_it;
1330  }
1331 
1332  if(std::size_t(choice) >= bc_vector.size()) {
1333  return;
1334  }
1335 
1336  events::command_disabler disabler;
1337  const battle_context_unit_stats& att = bc_vector[choice].get_attacker_stats();
1338  const battle_context_unit_stats& def = bc_vector[choice].get_defender_stats();
1339 
1340  attacker->set_goto(map_location());
1341 
1343 
1344  // make the attacker's stats appear during the attack
1345  gui().display_unit_hex(attacker_loc);
1346 
1347  // remove highlighted hexes etc..
1351  gui().unhighlight_reach();
1352 
1353  current_team().set_action_bonus_count(1 + current_team().action_bonus_count());
1354  // TODO: change ToD to be location specific for the defender
1355 
1356  const tod_manager& tod_man = pc_.get_tod_manager();
1357 
1360  attacker_loc,
1361  defender_loc,
1362  att.attack_num,
1363  def.attack_num,
1364  attacker->type_id(),
1365  defender->type_id(),
1366  att.level,
1367  def.level,
1368  tod_man.turn(),
1369  tod_man.get_time_of_day()
1370  )
1371  );
1372 }
1373 
1374 std::set<map_location> mouse_handler::get_adj_enemies(const map_location& loc, int side) const
1375 {
1376  std::set<map_location> res;
1377 
1378  const team& uteam = pc_.get_teams()[side - 1];
1379 
1380  for(const map_location& aloc : get_adjacent_tiles(loc)) {
1382 
1383  if(i && uteam.is_enemy(i->side())) {
1384  res.insert(aloc);
1385  }
1386  }
1387 
1388  return res;
1389 }
1390 
1391 /**
1392  * Causes attackable hexes to be highlighted.
1393  *
1394  * This checks the hexes that the provided unit can attack. If there is a valid
1395  * target there, that location is inserted into current_paths_.destinations.
1396  */
1398 {
1399  // Cannot attack if no attacks are left.
1400  if(u->attacks_left() == 0) {
1401  return;
1402  }
1403 
1404  // Get the teams involved.
1405  const team& cur_team = current_team();
1406  const team& u_team = pc_.get_teams()[u->side() - 1];
1407 
1408  // Check each adjacent hex.
1409  for(const map_location& loc : get_adjacent_tiles(u->get_location())) {
1410  // No attack option shown if no visible unit present.
1411  // (Visible to current team, not necessarily the unit's team.)
1412  if(!pc_.get_map().on_board(loc)) {
1413  continue;
1414  }
1415 
1417  if(!i || !i->is_visible_to_team(cur_team, false)) {
1418  continue;
1419  }
1420 
1421  const unit& target = *i;
1422 
1423  // Can only attack non-petrified enemies.
1424  if(u_team.is_enemy(target.side()) && !target.incapacitated()) {
1426  }
1427  }
1428 }
1429 
1431 {
1432  game_board& board = pc_.gamestate().board_;
1433 
1434  if(!it) {
1435  return false;
1436  }
1437 
1438  if(it->side() != side_num_ || it->user_end_turn() || gui().fogged(it->get_location()) || !board.unit_can_move(*it)) {
1439  return false;
1440  }
1441 
1442  if(current_team().is_enemy(static_cast<int>(gui().viewing_team() + 1)) && it->invisible(it->get_location())) {
1443  return false;
1444  }
1445 
1446  if(it->get_hidden()) {
1447  return false;
1448  }
1449 
1450  return true;
1451 }
1452 
1453 void mouse_handler::cycle_units(const bool browse, const bool reverse)
1454 {
1455  unit_map& units = pc_.get_units();
1456 
1457  if(units.begin() == units.end()) {
1458  return;
1459  }
1460 
1462  if(!it) {
1463  it = units.begin();
1464  }
1465 
1466  const unit_map::const_iterator itx = it;
1467 
1468  do {
1469  if(reverse) {
1470  if(it == units.begin()) {
1471  it = units.end();
1472  }
1473 
1474  --it;
1475  } else {
1476  if(it == units.end()) {
1477  it = units.begin();
1478  } else {
1479  ++it;
1480  }
1481  }
1482  } while(it != itx && !unit_in_cycle(it));
1483 
1484  if(unit_in_cycle(it)) {
1485  gui().scroll_to_tile(it->get_location(), game_display::WARP);
1486 
1487  select_hex(it->get_location(), browse);
1488  // mouse_update(browse);
1489  }
1490 }
1491 
1493 {
1494  gui().unhighlight_reach();
1495 
1496  current_paths_ = new_paths;
1497  current_route_.steps.clear();
1498 
1499  gui().set_route(nullptr);
1500 
1501  pc_.get_whiteboard()->erase_temp_move();
1502 }
1503 
1505 {
1506  return pc_.get_teams()[gui().viewing_team()];
1507 }
1508 
1510 {
1511  return pc_.get_teams()[gui().viewing_team()];
1512 }
1513 
1515 {
1516  return pc_.get_teams()[side_num_ - 1];
1517 }
1518 
1520 
1522 {
1524 }
1525 
1527 {
1529 }
1530 
1531 } // 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:70
void set_current_paths(const pathfind::paths &new_paths)
Game board class.
Definition: game_board.hpp:48
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:647
int h() const
Effective map height, in hexes.
Definition: map.hpp:124
unit_iterator end()
Definition: map.hpp:428
virtual const std::vector< team > & teams() const override
Definition: game_board.hpp:82
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:293
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:109
void set(CURSOR_TYPE type)
Use the default parameter to reset cursors.
Definition: cursor.cpp:175
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:120
const std::string & type_id() const
The id of this unit&#39;s type.
Definition: unit.cpp:1792
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:597
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:758
virtual const gamemap & map() const override
Definition: game_board.hpp:99
dest_vect destinations
Definition: pathfind.hpp:100
unit_iterator begin()
Definition: map.hpp:418
int viewing_side() const
Definition: display.hpp:105
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:1226
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)
bool on_board(const map_location &loc) const
Tell if a location is on the map.
Definition: map.cpp:382
void touch_motion(int x, int y, const bool browse, bool update=false, map_location loc=map_location::null_location())
map_location previous_hex_
bool unit_can_move(const unit &u) const
True if, and only if, at least one of the following is true:
static std::string _(const char *str)
Definition: gettext.hpp:92
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:25
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:44
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:70
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:88
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:172
bool is_enemy(int n) const
Definition: team.hpp:251
Object which temporarily resets a unit&#39;s movement.
Definition: unit.hpp:1951
std::string path
Definition: game_config.cpp:38
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:53
Structure describing the statistics of a unit involved in the battle.
Definition: attack.hpp:50
Structure which holds a single route and marks for special events.
Definition: pathfind.hpp:140
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:743
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:37
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:309
actions::undo_list & get_undo_stack()
static lg::log_domain log_engine("engine")
int w() const
Effective map width, in hexes.
Definition: map.hpp:121
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:217
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:291
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:2204
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:215
DIRECTION
Valid directions which can be moved in our hexagonal world.
Definition: location.hpp:39
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:1385
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:42
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:42
const map_location & get_location() const
The current map location this unit is at.
Definition: unit.hpp:1348
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:104
Various functions that implement the undoing (and redoing) of in-game commands.
bool contains(const map_location &) const
Definition: pathfind.cpp:514
void play_UI_sound(const std::string &files)
Definition: sound.cpp:1051
void set_action_bonus_count(const int count)
Definition: team.hpp:222
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:265
Object which contains all the possible locations a unit can move to, with associated best routes to t...
Definition: pathfind.hpp:71
static const map_location & null_location()
Definition: location.hpp:80
Container associating units to locations.
Definition: map.hpp:97
unit * find_unit_nonowning(const map_location &hex)
bool incapacitated() const
Check if the unit has been petrified.
Definition: unit.hpp:896
int turn() const
int side() const
The side this unit belongs to.
Definition: unit.hpp:333
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:478
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:273
#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:1924
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:182
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:485
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:226
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)