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