The Battle for Wesnoth  1.17.23+dev
move.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2023
3  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 /**
17  * @file
18  * Movement.
19  */
20 
21 #include "actions/move.hpp"
22 
23 #include "actions/undo.hpp"
24 #include "actions/vision.hpp"
25 
26 #include "game_events/pump.hpp"
27 #include "preferences/game.hpp"
28 #include "gettext.hpp"
29 #include "hotkey/hotkey_item.hpp"
31 #include "log.hpp"
32 #include "map/map.hpp"
33 #include "mouse_handler_base.hpp"
34 #include "pathfind/pathfind.hpp"
35 #include "pathfind/teleport.hpp"
36 #include "replay.hpp"
37 #include "replay_helper.hpp"
38 #include "synced_context.hpp"
39 #include "play_controller.hpp"
40 #include "resources.hpp"
41 #include "units/udisplay.hpp"
42 #include "font/standard_colors.hpp"
43 #include "formula/string_utils.hpp"
44 #include "team.hpp"
45 #include "units/unit.hpp"
47 #include "whiteboard/manager.hpp"
48 
49 #include <deque>
50 #include <map>
51 #include <set>
52 
53 static lg::log_domain log_engine("engine");
54 #define DBG_NG LOG_STREAM(debug, log_engine)
55 
56 
57 namespace actions {
58 
59 
61 {
62  seen_friends_.push_back(u);
63 }
64 
65 
67 {
68  seen_enemies_.push_back(u);
69 }
70 
71 
73 {
74  return ambusher_;
75 }
76 
77 
79 {
80  return failed_teleport_;
81 }
82 
83 
84 const std::vector<unit_map::const_iterator>& move_unit_spectator::get_seen_enemies() const
85 {
86  return seen_enemies_;
87 }
88 
89 
90 const std::vector<unit_map::const_iterator>& move_unit_spectator::get_seen_friends() const
91 {
92  return seen_friends_;
93 }
94 
95 
97 {
98  return unit_;
99 }
100 
101 
103  : ambusher_(units.end()),failed_teleport_(units.end()),seen_enemies_(),seen_friends_(),unit_(units.end())
104 {
105 }
106 
107 
109 {
110 }
111 
113 {
114  ambusher_ = units.end();
115  failed_teleport_ = units.end();
116  seen_enemies_.clear();
117  seen_friends_.clear();
118  unit_ = units.end();
119 }
120 
121 
123 {
124  ambusher_ = u;
125 }
126 
127 
129 {
130  failed_teleport_ = u;
131 }
132 
133 
135 {
136  unit_ = u;
137 }
138 
139 
140 game_events::pump_result_t get_village(const map_location& loc, int side, bool *action_timebonus, bool fire_event)
141 {
142  std::vector<team> &teams = resources::gameboard->teams();
143  team *t = static_cast<unsigned>(side - 1) < teams.size() ? &teams[side - 1] : nullptr;
144  if (t && t->owns_village(loc)) {
146  }
147 
148  bool not_defeated = t && !resources::gameboard->team_is_defeated(*t);
149 
150  bool grants_timebonus = false;
151 
152  int old_owner_side = 0;
153  // We strip the village off all other sides, unless it is held by an ally
154  // and our side is already defeated (and thus we can't occupy it)
155  for(std::vector<team>::iterator i = teams.begin(); i != teams.end(); ++i) {
156  int i_side = std::distance(teams.begin(), i) + 1;
157  if (!t || not_defeated || t->is_enemy(i_side)) {
158  if(i->owns_village(loc)) {
159  old_owner_side = i_side;
160  i->lose_village(loc);
161  }
162  if (side != i_side && action_timebonus) {
163  grants_timebonus = true;
164  }
165  }
166  }
167 
168  if (!t) {
170  }
171 
172  if(grants_timebonus) {
173  t->set_action_bonus_count(1 + t->action_bonus_count());
174  *action_timebonus = true;
175  }
176 
177  if(not_defeated) {
178  if (display::get_singleton() != nullptr) {
180  }
181  return t->get_village(loc, old_owner_side, fire_event ? resources::gamedata : nullptr);
182  }
183 
185 }
186 
187 
188 namespace { // Private helpers for move_unit()
189 
190  /** Helper class for move_unit(). */
191  class unit_mover {
192  typedef std::vector<map_location>::const_iterator route_iterator;
193 
194  public:
195  unit_mover(const unit_mover&) = delete;
196  unit_mover& operator=(const unit_mover&) = delete;
197 
198  unit_mover(const std::vector<map_location> & route,
199  move_unit_spectator *move_spectator,
200  bool skip_sightings, bool skip_ally_sightings);
201  ~unit_mover();
202 
203  /** Determines how far along the route the unit can expect to move this turn. */
204  bool check_expected_movement();
205  /** Attempts to move the unit along the expected path. */
206  void try_actual_movement(bool show);
207  /** Does some bookkeeping and event firing, for use after movement. */
208  void post_move(undo_list *undo_stack);
209  /** Shows the various on-screen messages, for use after movement. */
210  void feedback() const;
211 
212  /** After checking expected movement, this is the expected path. */
213  std::vector<map_location> expected_path() const
214  { return std::vector<map_location>(begin_, expected_end_); }
215  /** After moving, this is the final hex reached. */
216  const map_location & final_hex() const
217  { return *move_loc_; }
218  /** The number of hexes actually entered. */
219  std::size_t steps_travelled() const
220  { return move_loc_ - begin_; }
221  /** After moving, use this to detect if movement was less than expected. */
222  bool stopped_early() const { return expected_end_ != real_end_; }
223  /**
224  * After moving, use this to detect if something happened that would
225  * interrupt movement (even if movement ended for a different reason).
226  */
227  bool interrupted(bool include_end_of_move_events=true) const
228  {
229  return ambushed_ || blocked() || sighted_ || teleport_failed_ ||
230  (include_end_of_move_events ? (wml_removed_unit_ || wml_move_aborted_): event_mutated_mid_move_ ) ||
231  !move_it_.valid();
232  }
233 
234  private: // functions
235  /** Returns whether or not movement was blocked by a non-ambushing enemy. */
236  bool blocked() const { return blocked_loc_ != map_location::null_location(); }
237  /** Checks the expected route for hidden units. */
238  void cache_hidden_units(const route_iterator & start,
239  const route_iterator & stop);
240  /** Fires the enter_hex or exit_hex event and updates our data as needed. */
241  void fire_hex_event(const std::string & event_name,
242  const route_iterator & current,
243  const route_iterator & other);
244  /** AI moves are supposed to not change the "goto" order. */
245  bool is_ai_move() const { return spectator_ != nullptr; }
246  /** Checks how far it appears we can move this turn. */
247  route_iterator plot_turn(const route_iterator & start,
248  const route_iterator & stop);
249  /** Updates our stored info after a WML event might have changed something. */
250  void post_wml(game_events::pump_result_t pump_res, const route_iterator & step);
251  void post_wml(game_events::pump_result_t pump_res) { return post_wml(pump_res, full_end_); }
252  /** Fires the sighted events that were raised earlier. */
253  void pump_sighted(const route_iterator & from);
254  /** Returns the ambush alert (if any) for the given unit. */
255  static std::string read_ambush_string(const unit & ambusher);
256  /** Reveals the unit at the indicated location. */
257  void reveal_ambusher(const map_location & hex, bool update_alert=true);
258 
259  /** Returns whether or not undoing this move should be blocked. */
260  bool undo_blocked() const
261  { return ambushed_ || blocked() || wml_removed_unit_ || wml_undo_disabled_ || fog_changed_ ||
263 
264  // The remaining private functions are suggested to be inlined because
265  // each is used in only one place. (They are separate functions to ease
266  // code reading.)
267 
268  /** Checks for ambushers around @a hex, setting flags as appropriate. */
269  inline void check_for_ambushers(const map_location & hex);
270  /** Makes sure the path is not obstructed by a unit. */
271  inline bool check_for_obstructing_unit(const map_location & hex,
272  const map_location & prev_hex);
273  /** Moves the unit the next step. */
274  inline bool do_move(const route_iterator & step_from,
275  const route_iterator & step_to,
276  unit_display::unit_mover & animator);
277  /** Clears fog/shroud and handles units being sighted. */
278  inline void handle_fog(const map_location & hex, bool new_animation);
279  inline bool is_reasonable_stop(const map_location & hex) const;
280  /** Reveals the units stored in ambushers_ (and blocked_loc_). */
281  inline void reveal_ambushers();
282  /** Makes sure the units in ambushers_ still exist. */
283  inline void validate_ambushers();
284 
285  private: // data
286  // (The order of the fields is somewhat important for the constructor.)
287 
288  // Movement parameters (these decrease the number of parameters needed
289  // for individual functions).
290  move_unit_spectator * const spectator_;
291  const bool skip_sighting_;
294  // Needed to interface with unit_display::unit_mover.
295  const std::vector<map_location> & route_;
296 
297  // The route to traverse.
298  const route_iterator begin_;
299  const route_iterator full_end_; // The end of the plotted route.
300  route_iterator expected_end_; // The end of this turn's portion of the plotted route.
301  route_iterator ambush_limit_; // How far we can go before encountering hidden units, ignoring allied units.
302  route_iterator obstructed_; // Points to either full_end_ or a hex we cannot enter. This is used so that exit_hex can fire before we decide we cannot enter this hex.
303  route_iterator real_end_; // How far we actually can move this turn.
304 
305  // The unit that is moving.
307 
308  // This data stores the state from before the move started.
309  const int orig_side_;
310  const int orig_moves_;
313 
314  // This data tracks the current state as the move is in progress.
316  team * current_team_; // Will default to the original team if the moving unit becomes invalid.
318  route_iterator move_loc_; // Will point to the last moved-to location (in case the moving unit disappears).
319  // Data accumulated while making the move.
321  map_location ambush_stop_; // Could be inaccurate if ambushed_ is false.
322  map_location blocked_loc_; // Location of a blocking, enemy, non-ambusher unit.
323  bool ambushed_;
328  bool event_mutated_mid_move_; // Cache of wml_removed_unit_ || wml_move_aborted_ from just before the end-of-move handling.
330  bool sighted_; // Records if sightings were made that could interrupt movement.
331  bool sighted_stop_; // Records if sightings were made that did interrupt movement (the same as sighted_ unless movement ended for another reason).
333  std::size_t enemy_count_;
334  std::size_t friend_count_;
335  std::string ambush_string_;
336  std::vector<map_location> ambushers_;
337  std::deque<int> moves_left_; // The front value is what the moving unit's remaining moves should be set to after the next step through the route.
338 
339  shroud_clearer clearer_;
340  };
341 
342 
343  /**
344  * This constructor assumes @a route is not empty, and it will assert() that
345  * there is a unit at route.front().
346  * Iterators into @a route must remain valid for the life of this object.
347  * It is assumed that move_spectator is only supplied for AI moves (only
348  * affects whether or not gotos are changed).
349  */
350  unit_mover::unit_mover(const std::vector<map_location> & route,
351  move_unit_spectator *move_spectator,
352  bool skip_sightings, bool skip_ally_sightings)
353  : spectator_(move_spectator)
354  , skip_sighting_(skip_sightings)
355  , skip_ally_sighting_(skip_ally_sightings)
356  , playing_team_is_viewing_(display::get_singleton()->playing_team() == display::get_singleton()->viewing_team() || display::get_singleton()->show_everything())
357  , route_(route)
358  , begin_(route.begin())
359  , full_end_(route.end())
363  , real_end_(begin_)
364  // Unit information:
365  , move_it_(resources::gameboard->units().find(*begin_))
366  , orig_side_(( static_cast<void>(assert(move_it_ != resources::gameboard->units().end())), move_it_->side() ))
367  , orig_moves_(move_it_->movement_left())
368  , orig_dir_(move_it_->facing())
369  , goto_( is_ai_move() ? move_it_->get_goto() : route.back() )
372  , current_uses_fog_(current_team_->fog_or_shroud() && current_team_->auto_shroud_updates())
373  , move_loc_(begin_)
374  // The remaining fields are set to some sort of "zero state".
375  , zoc_stop_(map_location::null_location())
376  , ambush_stop_(map_location::null_location())
377  , blocked_loc_(map_location::null_location())
378  , ambushed_(false)
379  , show_ambush_alert_(false)
380  , wml_removed_unit_(false)
381  , wml_undo_disabled_(false)
382  , wml_move_aborted_(false)
383  , event_mutated_mid_move_(false)
384  , fog_changed_(false)
385  , sighted_(false)
386  , sighted_stop_(false)
387  , teleport_failed_(false)
388  , enemy_count_(0)
389  , friend_count_(0)
390  , ambush_string_()
391  , ambushers_()
392  , moves_left_()
393  , clearer_()
394  {
395  if ( !is_ai_move() )
396  // Clear the "goto" instruction during movement.
397  // (It will be reset in the destructor if needed.)
399  }
400 
401 
402  unit_mover::~unit_mover()
403  {
404  // Set the "goto" order? (Not if WML set it.)
405  if ( !is_ai_move() && move_it_.valid() &&
406  move_it_->get_goto() == map_location::null_location() )
407  {
408  // Only set the goto if movement was not complete and was not
409  // interrupted.
410  if (real_end_ != full_end_ && !interrupted(false)) {
411  // End-of-move-events do not cancel a goto. (Use case: tutorial S2.)
412  move_it_->set_goto(goto_);
413  }
414  }
415  }
416 
417 
418  // Private inlines:
419 
420  /**
421  * Checks for ambushers around @a hex, setting flags as appropriate.
422  */
423  inline void unit_mover::check_for_ambushers(const map_location & hex)
424  {
425  const unit_map &units = resources::gameboard->units();
426 
427  // Need to check each adjacent hex for hidden enemies.
428  for(const map_location& loc : get_adjacent_tiles(hex)) {
429  const unit_map::const_iterator neighbor_it = units.find(loc);
430 
431  if ( neighbor_it != units.end() &&
432  current_team_->is_enemy(neighbor_it->side()) &&
433  neighbor_it->invisible(loc) )
434  {
435  // Ambushed!
436  ambushed_ = true;
437  ambush_stop_ = hex;
438  ambushers_.push_back(loc);
439  }
440  }
441  }
442 
443 
444  /**
445  * Makes sure the path is not obstructed by a unit.
446  * @param hex The hex to check.
447  * @param prev_hex The previous hex in the route (used to detect a teleport).
448  * @return true if @a hex is obstructed.
449  */
450  inline bool unit_mover::check_for_obstructing_unit(const map_location & hex,
451  const map_location & prev_hex)
452  {
453  const unit_map::const_iterator blocking_unit = resources::gameboard->units().find(hex);
454 
455  // If no unit, then the path is not obstructed.
456  if (blocking_unit == resources::gameboard->units().end()) {
457  return false;
458  }
459 
460  // Check for units blocking a teleport exit. This can now only happen
461  // if these units are not visible to the current side, as otherwise no
462  // valid path is found.
463  if ( !tiles_adjacent(hex, prev_hex) ) {
464  if ( current_team_->is_enemy(blocking_unit->side()) ) {
465  // Enemy units always block the tunnel.
466  teleport_failed_ = true;
467  return true;
468  } else {
469  // By contrast, allied units (of a side which does not share vision) only
470  // block the tunnel if pass_allied_units=true. Whether the teleport is possible
471  // is checked by getting the teleport map with the see_all flag set to true.
472  const pathfind::teleport_map teleports = pathfind::get_teleport_locations(*move_it_, *current_team_, true, false, false);
473  const auto allowed_teleports = teleports.get_adjacents(prev_hex);
474 
475  if(allowed_teleports.count(hex) == 0) {
476  teleport_failed_ = true;
477  return true;
478  }
479  }
480  }
481 
482  if ( current_team_->is_enemy(blocking_unit->side()) ) {
483  // Trying to go through an enemy.
484  blocked_loc_ = hex;
485  return true;
486  }
487 
488  // If we get here, the unit does not interfere with movement.
489  return false;
490  }
491 
492 
493  /**
494  * Moves the unit the next step.
495  * @a step_to is the hex being moved to.
496  * @a step_from is the hex before that in the route.
497  * (The unit is actually at *move_loc_.)
498  * @a animator is the unit_display::unit_mover being used.
499  * @return whether or not we started a new animation.
500  */
501  inline bool unit_mover::do_move(const route_iterator & step_from,
502  const route_iterator & step_to,
503  unit_display::unit_mover & animator)
504  {
506 
507  // Adjust the movement even if we cannot move yet.
508  // We will eventually be able to move if nothing unexpected
509  // happens, and if something does happen, this movement is the
510  // cost to discover it.
511  move_it_->set_movement(moves_left_.front(), true);
512  moves_left_.pop_front();
513 
514  // Invalidate before moving so we invalidate neighbor hexes if needed.
515  move_it_->anim_comp().invalidate(disp);
516 
517  // Attempt actually moving. Fails if *step_to is occupied.
518  auto [unit_it, success] = resources::gameboard->units().move(*move_loc_, *step_to);
519 
520  if(success) {
521  // Update the moving unit.
522  move_it_ = unit_it;
523  move_it_->set_facing(step_from->get_relative_dir(*step_to));
524  // Disable bars. The expectation here is that the animation
525  // unit_mover::finish() will clean after us at a later point. Ugly,
526  // but it works.
527  move_it_->anim_comp().set_standing(false);
528  disp.invalidate_unit_after_move(*move_loc_, *step_to);
529  disp.invalidate(*step_to);
530  move_loc_ = step_to;
531 
532  // Show this move.
533  animator.proceed_to(move_it_.get_shared_ptr(), step_to - begin_,
534  move_it_->appearance_changed(), false);
535  move_it_->set_appearance_changed(false);
536  disp.redraw_minimap();
537  }
538 
539  return success;
540  }
541 
542 
543  /**
544  * Clears fog/shroud and raises events for units being sighted.
545  * Only call this if the current team uses fog or shroud.
546  * @a hex is both the center of fog clearing and the filtered location of
547  * the moving unit when the sighted events will be fired.
548  */
549  inline void unit_mover::handle_fog(const map_location & hex,
550  bool new_animation)
551  {
552  // Clear the fog.
553  if ( clearer_.clear_unit(hex, *move_it_, *current_team_, nullptr,
555  !new_animation) )
556  {
557  clearer_.invalidate_after_clear();
558  fog_changed_ = true;
559  }
560 
561  // Check for sighted units?
562  if ( !skip_sighting_ ) {
563  sighted_ = enemy_count_ != 0 ;
564  }
566  {
567  sighted_ |= (friend_count_ != 0);
568  }
569  }
570 
571 
572  /**
573  * @return true if an unscheduled stop at @a hex is not likely to negatively
574  * impact the player's plans.
575  * (E.g. it would not kill movement by making an unintended village capture.)
576  */
577  inline bool unit_mover::is_reasonable_stop(const map_location & hex) const
578  {
579  // We cannot reasonably stop if move_it_ could not be moved to this
580  // hex (the hex was occupied by someone else).
581  if (*move_loc_ != hex) {
582  return false;
583  }
584 
585  // We can reasonably stop if the hex is not an unowned village.
586  return !resources::gameboard->map().is_village(hex) ||
588  }
589 
590 
591  /**
592  * Reveals the units stored in ambushers_ (and blocked_loc_).
593  * Also sets ambush_string_.
594  * May fire "sighted" events.
595  * Only call this if appropriate; this function does not itself check
596  * ambushed_ or blocked().
597  */
598  inline void unit_mover::reveal_ambushers()
599  {
600  // Reveal the blocking unit.
601  if (blocked()) {
602  reveal_ambusher(blocked_loc_, false);
603  }
604  // Reveal ambushers.
605  for(const map_location & reveal : ambushers_) {
606  reveal_ambusher(reveal, true);
607  }
608 
609  // Default "Ambushed!" message?
610  if (ambush_string_.empty()) {
611  ambush_string_ = _("Ambushed!");
612  }
613  }
614 
615 
616  /**
617  * Makes sure the units in ambushers_ still exist.
618  */
619  inline void unit_mover::validate_ambushers()
620  {
621  const unit_map &units = resources::gameboard->units();
622 
623  // Loop through the previously-detected ambushers.
624  std::size_t i = 0;
625  while ( i != ambushers_.size() ) {
626  if (units.count(ambushers_[i]) == 0) {
627  // Ambusher is gone.
628  ambushers_.erase(ambushers_.begin() + i);
629  }
630  else {
631  // Proceed to the next ambusher.
632  ++i;
633  }
634  }
635  }
636 
637 
638  // Private utilities:
639 
640  /**
641  * Checks the expected route for hidden units.
642  * This basically handles all the checks for surprises that can be done
643  * without visibly notifying a player. Thus this can be called at the
644  * start of movement and immediately after events, rather than tie up
645  * CPU time in the middle of animating a move.
646  *
647  * @param[in] start The beginning of the path to check.
648  * @param[in] stop The end of the path to check.
649  */
650  void unit_mover::cache_hidden_units(const route_iterator & start,
651  const route_iterator & stop)
652  {
653  // Clear the old cache.
656  teleport_failed_ = false;
657  // The ambush cache needs special treatment since we cannot re-detect
658  // an ambush if we are already at the ambushed location.
660  if ( ambushed_ ) {
661  validate_ambushers();
662  ambushed_ = !ambushers_.empty();
663  }
664  if ( !ambushed_ ) {
666  ambushers_.clear();
667  }
668 
669  // Update the shroud clearer.
670  clearer_.cache_units(current_uses_fog_ ? current_team_ : nullptr);
671 
672 
673  // Abort for null routes.
674  if ( start == stop ) {
676  return;
677  }
678 
679  // This loop will end with ambush_limit_ pointing one element beyond
680  // where the unit would be forced to stop by a hidden unit.
681  for ( ambush_limit_ = start+1; ambush_limit_ != stop; ++ambush_limit_ ) {
682  // Check if we need to stop in the previous hex.
683  if ( ambushed_ ) {
684  break;
685  }
686  // Check for being unable to enter this hex.
687  if ( check_for_obstructing_unit(*ambush_limit_, *(ambush_limit_-1)) ) {
688  // No replay check here? Makes some sense, I guess.
689  obstructed_ = ambush_limit_++; // The limit needs to be after obstructed_ in order for the latter to do anything.
690  break;
691  }
692 
693  // We can enter this hex.
694  // See if we are stopped in this hex.
695  check_for_ambushers(*ambush_limit_);
696  }
697  }
698 
699 
700  /**
701  * Fires the enter_hex or exit_hex event and updates our data as needed.
702  *
703  * @param[in] event_name The name of the event ("enter_hex" or "exit_hex").
704  * @param[in] current The currently occupied hex.
705  * @param[in] other The secondary hex to provide to the event.
706  *
707  */
708  void unit_mover::fire_hex_event(const std::string & event_name,
709  const route_iterator & current,
710  const route_iterator & other)
711  {
712 
713  const game_events::entity_location mover(*move_it_, *current);
714 
715  post_wml(resources::game_events->pump().fire(event_name, mover, *other), current);
716  }
717 
718 
719  /**
720  * Checks how far it appears we can move this turn.
721  *
722  * @param[in] start The beginning of the plotted path.
723  * @param[in] stop The end of the plotted path.
724  *
725  * @return An end iterator for the path that can be traversed this turn.
726  */
727  unit_mover::route_iterator unit_mover::plot_turn(const route_iterator & start,
728  const route_iterator & stop)
729  {
730  const gamemap &map = resources::gameboard->map();
731 
732  // Handle null routes.
733  if (start == stop) {
734  return start;
735  }
736 
737  int remaining_moves = move_it_->movement_left();
739  moves_left_.clear();
740 
741  if ( start != begin_ ) {
742  // Check for being unable to leave the current hex.
743  if ( !move_it_->get_ability_bool("skirmisher", *start) &&
745  zoc_stop_ = *start;
746  }
747 
748  // This loop will end with end pointing one element beyond where the
749  // unit thinks it will stop (the usual notion of "end" for iterators).
750  route_iterator end = start + 1;
751  for ( ; end != stop; ++end )
752  {
753  // Break out of the loop if we cannot leave the previous hex.
755  break;
756  }
757  remaining_moves -= move_it_->movement_cost(map[*end]);
758  if ( remaining_moves < 0 ) {
759  break;
760  }
761 
762  // We can enter this hex. Record the cost.
763  moves_left_.push_back(remaining_moves);
764 
765  // Check for being unable to leave this hex.
766  if (!move_it_->get_ability_bool("skirmisher", *end) &&
768  {
769  zoc_stop_ = *end;
770  }
771  }
772 
773  route_iterator min_end = start == begin_ ? start : start + 1;
774  while (end != min_end && resources::gameboard->has_visible_unit(*(end - 1), *current_team_)) {
775  // Backtrack.
776  --end;
777  }
778 
779  return end;
780  }
781 
782 
783  /**
784  * Updates our stored info after a WML event might have changed something.
785  *
786  * @param step Indicates the position in the path where we might need to start recalculating movement.
787  * Set this to full_end_ (or do not supply it) to skip such recalculations (because movement has finished).
788  *
789  * @returns false if continuing is impossible (i.e. we lost the moving unit).
790  */
791  void unit_mover::post_wml(game_events::pump_result_t pump_res, const route_iterator & step)
792  {
793  wml_move_aborted_ |= std::get<1>(pump_res);
794  wml_undo_disabled_ |= std::get<0>(pump_res);
795 
796  // Re-find the moving unit.
798  const bool found = move_it_ != resources::gameboard->units().end();
799 
800  // Update the current unit data.
801  current_side_ = found ? move_it_->side() : orig_side_;
804  ( current_side_ != orig_side_ ||
806 
807  // Update the path.
808  if ( found && step != full_end_ ) {
809  const route_iterator new_limit = plot_turn(step, expected_end_);
810  cache_hidden_units(step, new_limit);
811  // Just in case: length 0 paths become length 1 paths.
812  if (ambush_limit_ == step) {
813  ++ambush_limit_;
814  }
815  }
816 
817  wml_removed_unit_ |= !found;
818  }
819 
820 
821  /**
822  * Fires the sighted events that were raised earlier.
823  *
824  * @param[in] from Points to the hex the sighting unit currently occupies.
825  *
826  * @return sets event_mutated_ || wml_move_aborted_ to true if this event should interrupt movement.
827  */
828  void unit_mover::pump_sighted(const route_iterator & from)
829  {
830  game_events::pump_result_t pump_res = clearer_.fire_events();
831  post_wml(pump_res, from);
832  }
833 
834 
835  /**
836  * Returns the ambush alert (if any) for the given unit.
837  */
838  std::string unit_mover::read_ambush_string(const unit & ambusher)
839  {
840  for(const unit_ability &hide : ambusher.get_abilities("hides"))
841  {
842  const std::string & ambush_string = (*hide.ability_cfg)["alert"].str();
843  if (!ambush_string.empty()) {
844  return ambush_string;
845  }
846  }
847 
848  // No string found.
849  return std::string();
850  }
851 
852 
853  /**
854  * Reveals the unit at the indicated location.
855  * Can also update the current ambushed alert.
856  * May fire "sighted" events.
857  */
858  void unit_mover::reveal_ambusher(const map_location & hex, bool update_alert)
859  {
860  // Convenient alias:
861  unit_map &units = resources::gameboard->units();
863 
864  // Find the unit at the indicated location.
865  unit_map::iterator ambusher = units.find(hex);
866  if ( ambusher != units.end() ) {
867  // Prepare for sighted events.
868  std::vector<int> sight_cache(get_sides_not_seeing(*ambusher));
869  // Make sure the unit is visible (during sighted events, and in case
870  // we had to backtrack).
871  ambusher->set_state(unit::STATE_UNCOVERED, true);
872 
873  // Record this in the move spectator.
874  if (spectator_) {
875  spectator_->set_ambusher(ambusher);
876  }
877  // Override the default ambushed message?
878  if ( update_alert ) {
879  // Observers don't get extra information.
880  if ( playing_team_is_viewing_ || !disp.fogged(hex) ) {
881  show_ambush_alert_ = true;
882  // We only support one custom ambush message; use the first one.
883  if (ambush_string_.empty()) {
884  ambush_string_ = read_ambush_string(*ambusher);
885  }
886  }
887  }
888 
889  // Make sure this hex is drawn correctly.
890  disp.invalidate(hex);
891  // Fire sighted events.
892  auto [wml_undo_blocked, wml_move_aborted] = actor_sighted(*ambusher, &sight_cache);
893  // TODO: should we call post_wml ?
894  wml_move_aborted_ |= wml_move_aborted;
895  wml_undo_disabled_ |= wml_undo_blocked;
896  }
897  }
898 
899 
900  // Public interface:
901 
902  /**
903  * Determines how far along the route the unit can expect to move this turn.
904  * This is based solely on data known to the player, and will not plot a move
905  * that ends on another (known) unit.
906  * (For example, this prevents a player from plotting a multi-turn move that
907  * has this turn's movement ending on a (slower) unit, and thereby clearing
908  * fog as if the moving unit actually made it on top of that other unit.)
909  *
910  * @returns false if the expectation is to not move at all.
911  */
912  bool unit_mover::check_expected_movement()
913  {
914  expected_end_ = plot_turn(begin_, full_end_);
915  return expected_end_ != begin_;
916  }
917 
918 
919  /**
920  * Attempts to move the unit along the expected path.
921  * (This will do nothing unless check_expected_movement() was called first.)
922  *
923  * @param[in] show Set to false to suppress animations.
924  */
925  void unit_mover::try_actual_movement(bool show)
926  {
927  static const std::string enter_hex_str("enter hex");
928  static const std::string exit_hex_str("exit hex");
929 
930 
931  bool obstructed_stop = false;
932 
933 
934  // Check for hidden units along the expected path before we start
935  // animating and firing events.
936  cache_hidden_units(begin_, expected_end_);
937 
938  if ( begin_ != ambush_limit_ ) {
939  // Cache the moving unit's visibility.
940  std::vector<int> not_seeing = get_sides_not_seeing(*move_it_);
941 
942  // Prepare to animate.
944  animator.start(move_it_.get_shared_ptr());
945 
946  // Traverse the route to the hex where we need to stop.
947  // Each iteration performs the move from real_end_-1 to real_end_.
948  for ( real_end_ = begin_+1; real_end_ != ambush_limit_; ++real_end_ ) {
949  const route_iterator step_from = real_end_ - 1;
950 
951  // See if we can leave *step_from.
952  // Already accounted for: ambusher
954  break;
955  }
956  if ( sighted_ && is_reasonable_stop(*step_from) ) {
957  sighted_stop_ = true;
958  break;
959  }
960  // Already accounted for: ZoC
961  // Already accounted for: movement cost
962  fire_hex_event(exit_hex_str, step_from, real_end_);
964  break;
965  }
966  if ( real_end_ == obstructed_ ) {
967  // We did not check for being a replay when checking for an
968  // obstructed hex, so we do not check can_break here.
969  obstructed_stop = true;
970  break;
971  }
972 
973  // We can leave *step_from. Make the move to *real_end_.
974  bool new_animation = do_move(step_from, real_end_, animator);
975  // Update the fog.
976  if ( current_uses_fog_ )
977  handle_fog(*real_end_, new_animation);
978  animator.wait_for_anims();
979 
980  // Fire the events for this step.
981  // (These return values are not checked since real_end_ still
982  // needs to be incremented. The wml_move_aborted_ check will break
983  // us out of the loop if needed.)
984  fire_hex_event(enter_hex_str, real_end_, step_from);
985  // Sighted events only fire if we could stop due to sighting.
986  if (is_reasonable_stop(*real_end_)) {
987  pump_sighted(real_end_);
988  }
989  }//for
990  // Make sure any remaining sighted events get fired.
991  pump_sighted(real_end_-1);
992 
993  if ( move_it_.valid() ) {
994  // Finish animating.
995  animator.finish(move_it_.get_shared_ptr());
996  // Check for the moving unit being seen.
997  auto [wml_undo_blocked, wml_move_aborted] = actor_sighted(*move_it_, &not_seeing);
998  // TODO: should we call post_wml ?
999  wml_move_aborted_ |= wml_move_aborted;
1000  wml_undo_disabled_ |= wml_undo_blocked;
1001  }
1002  }//if
1003 
1004  // Some flags were set to indicate why we might stop.
1005  // Update those to reflect whether or not we got to them.
1007  if (!obstructed_stop) {
1009  }
1010  teleport_failed_ = teleport_failed_ && obstructed_stop;
1011  // event_mutated_ does not get unset, regardless of other reasons
1012  // for stopping, but we do save its current value.
1014  }
1015 
1016 
1017  /**
1018  * Does some bookkeeping and event firing, for use after movement.
1019  * This includes village capturing and the undo stack.
1020  */
1021  void unit_mover::post_move(undo_list *undo_stack)
1022  {
1023  const map_location & final_loc = final_hex();
1024 
1025  int orig_village_owner = 0;
1026  bool action_time_bonus = false;
1027 
1028  // Reveal ambushers?
1029  if (ambushed_ || blocked()) {
1030  reveal_ambushers();
1031  }
1032  else if (teleport_failed_ && spectator_) {
1033  spectator_->set_failed_teleport(resources::gameboard->units().find(*obstructed_));
1034  }
1036 
1037  if ( move_it_.valid() ) {
1038  // Update the moving unit.
1039  move_it_->set_interrupted_move(
1040  sighted_stop_ && !resources::whiteboard->is_executing_actions() ?
1041  *(full_end_-1) :
1043  if (ambushed_ || final_loc == zoc_stop_) {
1044  move_it_->set_movement(0, true);
1045  }
1046 
1047  // Village capturing.
1048  if ( resources::gameboard->map().is_village(final_loc) ) {
1049  // Is this a capture?
1050  orig_village_owner = resources::gameboard->village_owner(final_loc);
1051  if ( orig_village_owner != current_side_) {
1052  // Captured. Zap movement and take over the village.
1053  move_it_->set_movement(0, true);
1054  post_wml(get_village(final_loc, current_side_, &action_time_bonus));
1055  }
1056  }
1057  }
1058 
1059  // Finally, the moveto event.
1060  post_wml(resources::game_events->pump().fire("moveto", final_loc, *begin_));
1061 
1062  // Record keeping.
1063  if (spectator_) {
1064  spectator_->set_unit(move_it_);
1065  }
1066  if ( undo_stack ) {
1067  const bool mover_valid = move_it_.valid();
1068 
1069  if ( mover_valid ) {
1070  // MP_COUNTDOWN: added param
1073  action_time_bonus, orig_village_owner, orig_dir_);
1074  }
1075 
1076  if ( !mover_valid || undo_blocked() ||
1078  {
1079  undo_stack->clear();
1080  }
1081  }
1082 
1083  // Update the screen.
1085  }
1086 
1087 
1088  /**
1089  * Shows the various on-screen messages, for use after movement.
1090  */
1091  void unit_mover::feedback() const
1092  {
1093  // Alias some resources.
1095 
1096  // Multiple messages may be displayed simultaneously
1097  // this variable is used to keep them from overlapping
1098  std::string message_prefix = "";
1099 
1100  // Ambush feedback?
1101  if ( ambushed_ && show_ambush_alert_ ) {
1102  disp.announce(message_prefix + ambush_string_, font::BAD_COLOR);
1103  message_prefix += " \n";
1104  }
1105 
1106  display::announce_options announce_options;
1107  announce_options.discard_previous = false;
1108 
1109  // Failed teleport feedback?
1111  std::string teleport_string = _("Failed teleport! Exit not empty");
1112  disp.announce(message_prefix + teleport_string, font::BAD_COLOR, announce_options);
1113  message_prefix += " \n";
1114  }
1115 
1116  // Sighted units feedback?
1117  if ( playing_team_is_viewing_ && (enemy_count_ != 0 || friend_count_ != 0) ) {
1118  // Create the message to display (depends on whether friends,
1119  // enemies, or both were sighted, and on how many of each).
1120  utils::string_map symbols;
1121  symbols["enemies"] = std::to_string(enemy_count_);
1122  symbols["friends"] = std::to_string(friend_count_);
1123  std::string message;
1124  color_t msg_color;
1125  if ( friend_count_ != 0 && enemy_count_ != 0 ) {
1126  // TRANSLATORS: This becomes the "friendphrase" in "Units sighted! ($friendphrase, $enemyphrase)"
1127  symbols["friendphrase"] = VNGETTEXT("Part of 'Units sighted! (...)' sentence^1 friendly", "$friends friendly", friend_count_, symbols);
1128  // TRANSLATORS: This becomes the "enemyphrase" in "Units sighted! ($friendphrase, $enemyphrase)"
1129  symbols["enemyphrase"] = VNGETTEXT("Part of 'Units sighted! (...)' sentence^1 enemy", "$enemies enemy", enemy_count_, symbols);
1130  // TRANSLATORS: Both friends and enemies sighted -- neutral message.
1131  // This is shown when a move is interrupted because units were revealed from the fog of war.
1132  message = VGETTEXT("Units sighted! ($friendphrase, $enemyphrase)", symbols);
1133  msg_color = font::NORMAL_COLOR;
1134  } else if ( enemy_count_ != 0 ) {
1135  // TRANSLATORS: Only enemies sighted -- bad message.
1136  // This is shown when a move is interrupted because units were revealed from the fog of war.
1137  message = VNGETTEXT("Enemy unit sighted!", "$enemies enemy units sighted!", enemy_count_, symbols);
1138  msg_color = font::BAD_COLOR;
1139  } else if ( friend_count_ != 0 ) {
1140  // TRANSLATORS: Only friends sighted -- good message.
1141  // This is shown when a move is interrupted because units were revealed from the fog of war.
1142  message = VNGETTEXT("Friendly unit sighted", "$friends friendly units sighted", friend_count_, symbols);
1143  msg_color = font::GOOD_COLOR;
1144  }
1145 
1146  disp.announce(message_prefix + message, msg_color, announce_options);
1147  message_prefix += " \n";
1148  }
1149 
1150  // Suggest "continue move"?
1151  if ( playing_team_is_viewing_ && sighted_stop_ && !resources::whiteboard->is_executing_actions() ) {
1152  // See if the "Continue Move" action has an associated hotkey
1154  if ( !name.empty() ) {
1155  utils::string_map symbols;
1156  symbols["hotkey"] = name;
1157  std::string message = VGETTEXT("(press $hotkey to keep moving)", symbols);
1158  disp.announce(message_prefix + message, font::NORMAL_COLOR, announce_options);
1159  message_prefix += " \n";
1160  }
1161  }
1162  }
1163 
1164 }//end anonymous namespace
1165 
1166 
1168  bool show_move, bool* interrupted, unit_mover& mover)
1169 {
1170  const events::command_disabler disable_commands;
1171  // Default return value.
1172  if (interrupted) {
1173  *interrupted = false;
1174  }
1175 
1176  // Attempt moving.
1177  mover.try_actual_movement(show_move);
1178 
1179  config co;
1180  config cn {
1181  "stopped_early", mover.stopped_early(),
1182  "final_hex_x", mover.final_hex().wml_x(),
1183  "final_hex_y", mover.final_hex().wml_y(),
1184  };
1185  bool matches_replay = checkup_instance->local_checkup(cn,co);
1186  if(!matches_replay)
1187  {
1188  replay::process_error("calculated movement destination (x="+ cn["final_hex_x"].str() + " y=" + cn["final_hex_y"].str() +
1189  ") didn't match the original destination(x="+ co["final_hex_x"].str() + " y=" + co["final_hex_y"].str() + ")\n");
1190 
1191  //TODO: move the unit by force to the desired destination with something like mover.reset_final_hex(co["x"], co["y"]);
1192  }
1193 
1194  // Bookkeeping, etc.
1195  // also fires the moveto event
1196  mover.post_move(undo_stack);
1197  if (show_move) {
1198  mover.feedback();
1199  }
1200 
1201  // Set return value.
1202  if (interrupted) {
1203  *interrupted = mover.interrupted();
1204  }
1205 
1206  return mover.steps_travelled();
1207 }
1208 
1209 /**
1210  * Moves a unit across the board.
1211  *
1212  * This function handles actual movement, checking terrain costs as well as
1213  * things that might interrupt movement (e.g. ambushes). If the full path
1214  * cannot be reached this turn, the remainder is stored as the unit's "goto"
1215  * instruction. (The unit itself is whatever unit is at the beginning of the
1216  * supplied path.)
1217  *
1218  * @param[in] steps The route to be traveled. The unit to be moved is at the beginning of this route.
1219  * @param undo_stack If supplied, then either this movement will be added to the stack or the stack will be cleared.
1220  * @param[in] continued_move If set to true, this is a continuation of an earlier move (movement is not interrupted should units be spotted).
1221  * @param[in] show_move Controls whether or not the movement is animated for the player.
1222  * @param[out] interrupted If supplied, then this is set to true if information was uncovered that warrants interrupting a chain of actions (and set to false otherwise).
1223  * @param[out] move_spectator If supplied, this will be given the information uncovered by the move (and the unit's "goto" instruction will be preserved).
1224  *
1225  * @returns The number of hexes entered. This can safely be used as an index
1226  * into @a steps to get the location where movement ended, provided
1227  * @a steps is not empty (the return value is guaranteed to be less
1228  * than steps.size() ).
1229  */
1230 std::size_t move_unit_and_record(const std::vector<map_location> &steps,
1231  undo_list* undo_stack, bool continued_move, bool show_move,
1232  bool* interrupted, move_unit_spectator* move_spectator)
1233 {
1234 
1235  // Avoid some silliness.
1236  if ( steps.size() < 2 || (steps.size() == 2 && steps.front() == steps.back()) ) {
1237  DBG_NG << "Ignoring a unit trying to jump on its hex at " <<
1238  ( steps.empty() ? map_location::null_location() : steps.front() ) << ".";
1239  return 0;
1240  }
1241  //if we have no fog activated then we always skip sighted
1242  if(resources::gameboard->units().find(steps.front()) != resources::gameboard->units().end())
1243  {
1244  const team &current_team = resources::gameboard->teams()[
1245  resources::gameboard->units().find(steps.front())->side() - 1];
1246  continued_move |= !current_team.fog_or_shroud();
1247  }
1248  const bool skip_ally_sighted = !preferences::interrupt_when_ally_sighted();
1249 
1250  // Evaluate this move.
1251  unit_mover mover(steps, move_spectator, continued_move, skip_ally_sighted);
1252  if ( !mover.check_expected_movement() )
1253  return 0;
1255  {
1256  /*
1257  enter the synced mode and do the actual movement.
1258  */
1259  resources::recorder->add_synced_command("move",replay_helper::get_movement(steps, continued_move, skip_ally_sighted));
1260  set_scontext_synced sync;
1261  std::size_t r = move_unit_internal(undo_stack, show_move, interrupted, mover);
1264  sync.do_final_checkup();
1265  return r;
1266  }
1267  else
1268  {
1269  //we are already in synced mode and don't need to reenter it again.
1270  return move_unit_internal(undo_stack, show_move, interrupted, mover);
1271  }
1272 }
1273 
1274 std::size_t move_unit_from_replay(const std::vector<map_location> &steps,
1275  undo_list* undo_stack, bool continued_move, bool skip_ally_sighted,
1276  bool show_move)
1277 {
1278  // Evaluate this move.
1279  unit_mover mover(steps, nullptr, continued_move,skip_ally_sighted);
1280  if ( !mover.check_expected_movement() )
1281  {
1282  replay::process_error("found corrupt movement in replay.");
1283  return 0;
1284  }
1285 
1286  return move_unit_internal(undo_stack, show_move, nullptr, mover);
1287 }
1288 
1289 
1290 }//namespace actions
bool sighted_
Definition: move.cpp:330
std::string ambush_string_
Definition: move.cpp:335
route_iterator real_end_
Definition: move.cpp:303
bool wml_removed_unit_
Definition: move.cpp:325
int current_side_
Definition: move.cpp:315
bool sighted_stop_
Definition: move.cpp:331
const int orig_moves_
Definition: move.cpp:310
bool event_mutated_mid_move_
Definition: move.cpp:328
bool fog_changed_
Definition: move.cpp:329
std::vector< map_location > ambushers_
Definition: move.cpp:336
const map_location goto_
Definition: move.cpp:312
std::size_t friend_count_
Definition: move.cpp:334
unit_map::iterator move_it_
Definition: move.cpp:306
bool ambushed_
Definition: move.cpp:323
route_iterator expected_end_
Definition: move.cpp:300
bool current_uses_fog_
Definition: move.cpp:317
map_location blocked_loc_
Definition: move.cpp:322
team * current_team_
Definition: move.cpp:316
static lg::log_domain log_engine("engine")
const int orig_side_
Definition: move.cpp:309
const std::vector< map_location > & route_
Definition: move.cpp:295
map_location ambush_stop_
Definition: move.cpp:321
const bool playing_team_is_viewing_
Definition: move.cpp:293
bool teleport_failed_
Definition: move.cpp:332
route_iterator obstructed_
Definition: move.cpp:302
std::deque< int > moves_left_
Definition: move.cpp:337
route_iterator ambush_limit_
Definition: move.cpp:301
move_unit_spectator *const spectator_
Definition: move.cpp:290
shroud_clearer clearer_
Definition: move.cpp:339
#define DBG_NG
Definition: move.cpp:54
bool wml_undo_disabled_
Definition: move.cpp:326
const route_iterator begin_
Definition: move.cpp:298
map_location zoc_stop_
Definition: move.cpp:320
const bool skip_sighting_
Definition: move.cpp:291
bool show_ambush_alert_
Definition: move.cpp:324
const map_location::DIRECTION orig_dir_
Definition: move.cpp:311
route_iterator move_loc_
Definition: move.cpp:318
std::size_t enemy_count_
Definition: move.cpp:333
bool wml_move_aborted_
Definition: move.cpp:327
const route_iterator full_end_
Definition: move.cpp:299
const bool skip_ally_sighting_
Definition: move.cpp:292
Various functions related to moving units.
double t
Definition: astarsearch.cpp:65
void set_ambusher(const unit_map::const_iterator &u)
set the location of an ambusher
Definition: move.cpp:122
void add_seen_friend(const unit_map::const_iterator &u)
add a location of a seen friend
Definition: move.cpp:60
const std::vector< unit_map::const_iterator > & get_seen_enemies() const
get the locations of seen enemies
Definition: move.cpp:84
const unit_map::const_iterator & get_ambusher() const
get the location of an ambusher
Definition: move.cpp:72
void add_seen_enemy(const unit_map::const_iterator &u)
add the location of new seen enemy
Definition: move.cpp:66
const unit_map::const_iterator & get_unit() const
get new location of moved unit
Definition: move.cpp:96
unit_map::const_iterator ambusher_
Definition: move.hpp:89
unit_map::const_iterator unit_
Definition: move.hpp:93
void reset(const unit_map &units)
reset all locations to empty values
Definition: move.cpp:112
void set_unit(const unit_map::const_iterator &u)
set the iterator to moved unit
Definition: move.cpp:134
std::vector< unit_map::const_iterator > seen_enemies_
Definition: move.hpp:91
void set_failed_teleport(const unit_map::const_iterator &u)
set the location of a failed teleport
Definition: move.cpp:128
std::vector< unit_map::const_iterator > seen_friends_
Definition: move.hpp:92
virtual ~move_unit_spectator()
destructor
Definition: move.cpp:108
const std::vector< unit_map::const_iterator > & get_seen_friends() const
get the locations of seen friends
Definition: move.cpp:90
const unit_map::const_iterator & get_failed_teleport() const
get the location of a failed teleport
Definition: move.cpp:78
unit_map::const_iterator failed_teleport_
Definition: move.hpp:90
move_unit_spectator(const unit_map &units)
constructor
Definition: move.cpp:102
Class to store the actions that a player can undo and redo.
Definition: undo.hpp:34
void add_move(const unit_const_ptr u, const std::vector< map_location >::const_iterator &begin, const std::vector< map_location >::const_iterator &end, int start_moves, int timebonus=0, int village_owner=-1, const map_location::DIRECTION dir=map_location::NDIRECTIONS)
Adds a move to the undo stack.
Definition: undo.cpp:167
void clear()
Clears the stack of undoable (and redoable) actions.
Definition: undo.cpp:212
virtual bool local_checkup(const config &expected_data, config &real_data)=0
Compares data to the results calculated during the original game.
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:161
int village_owner(const map_location &loc) const
Given the location of a village, will return the 1-based number of the team that currently owns it,...
Sort-of-Singleton that many classes, both GUI and non-GUI, use to access the game data.
Definition: display.hpp:87
void redraw_minimap()
Schedule the minimap to be redrawn.
Definition: display.cpp:1673
bool invalidate(const map_location &loc)
Function to invalidate a specific tile for redrawing.
Definition: display.cpp:3147
void announce(const std::string &msg, const color_t &color=font::GOOD_COLOR, const announce_options &options=announce_options())
Announce a message prominently.
Definition: display.cpp:1637
bool fogged(const map_location &loc) const
Returns true if location (x,y) is covered in fog.
Definition: display.cpp:707
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:101
virtual const std::vector< team > & teams() const override
Definition: game_board.hpp:86
team & get_team(int i)
Definition: game_board.hpp:98
bool team_is_defeated(const team &t) const
Calculates whether a team is defeated.
Definition: game_board.cpp:266
virtual const unit_map & units() const override
Definition: game_board.hpp:113
virtual const gamemap & map() const override
Definition: game_board.hpp:103
void invalidate_unit_after_move(const map_location &src, const map_location &dst)
Same as invalidate_unit() if moving the displayed unit.
static game_display * get_singleton()
Encapsulates the map of the game.
Definition: map.hpp:172
bool is_village(const map_location &loc) const
Definition: map.cpp:66
std::set< map_location > get_adjacents(map_location loc) const
Definition: teleport.cpp:234
void maybe_throw_return_to_play_side() const
void check_victory()
Checks to see if a side has won.
static config get_movement(const std::vector< map_location > &steps, bool skip_sighted, bool skip_ally_sighted)
Records a move that follows the provided steps.
void add_synced_command(const std::string &name, const config &command)
Definition: replay.cpp:248
static void process_error(const std::string &msg)
Definition: replay.cpp:199
A RAII object to enter the synced context, cannot be called if we are already in a synced context.
void do_final_checkup(bool dont_throw=false)
static bool undo_blocked()
static synced_state get_synced_state()
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:76
bool fog_or_shroud() const
Definition: team.hpp:307
bool auto_shroud_updates() const
Definition: team.hpp:326
bool is_enemy(int n) const
Definition: team.hpp:231
bool owns_village(const map_location &loc) const
Definition: team.hpp:173
A class to encapsulate the steps of drawing a unit's move.
Definition: udisplay.hpp:45
void wait_for_anims()
Waits for the final animation of the most recent proceed_to() to finish.
Definition: udisplay.cpp:397
void start(unit_ptr u)
Initiates the display of movement for the supplied unit.
Definition: udisplay.cpp:261
void finish(unit_ptr u, map_location::DIRECTION dir=map_location::NDIRECTIONS)
Finishes the display of movement for the supplied unit.
Definition: udisplay.cpp:437
void proceed_to(unit_ptr u, std::size_t path_index, bool update=false, bool wait=true)
Visually moves a unit from the last hex we drew to the one specified by path_index.
Definition: udisplay.cpp:318
Container associating units to locations.
Definition: map.hpp:99
unit_iterator end()
Definition: map.hpp:429
std::size_t count(const map_location &loc) const
Definition: map.hpp:414
unit_iterator find(std::size_t id)
Definition: map.cpp:301
umap_retval_pair_t move(const map_location &src, const map_location &dst)
Moves a unit from location src to location dst.
Definition: map.cpp:93
This class represents a single unit of a specific type.
Definition: unit.hpp:135
static void clear_status_caches()
Clear this unit status cache for all units.
Definition: unit.cpp:679
#define VGETTEXT(msgid,...)
Handy wrappers around interpolate_variables_into_string and gettext.
#define VNGETTEXT(msgid, msgid_plural, count,...)
std::size_t i
Definition: function.cpp:968
const unit * unit_
static std::string _(const char *str)
Definition: gettext.hpp:93
unit_ability_list get_abilities(const std::string &tag_name, const map_location &loc) const
Gets the unit's active abilities of a particular type if it were on a specified location.
Definition: abilities.cpp:220
@ STATE_UNCOVERED
The unit is petrified - it cannot move or be attacked.
Definition: unit.hpp:865
T end(const std::pair< T, T > &p)
T begin(const std::pair< T, T > &p)
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
bool tiles_adjacent(const map_location &a, const map_location &b)
Function which tells if two locations are adjacent.
Definition: location.cpp:503
Standard logging facilities (interface).
std::vector< int > get_sides_not_seeing(const unit &target)
Returns the sides that cannot currently see target.
Definition: vision.cpp:592
std::size_t move_unit_from_replay(const std::vector< map_location > &steps, undo_list *undo_stack, bool continued_move, bool skip_ally_sighted, bool show_move)
Moves a unit across the board.
Definition: move.cpp:1274
game_events::pump_result_t actor_sighted(const unit &target, const std::vector< int > *cache)
Fires sighted events for the sides that can see target.
Definition: vision.cpp:619
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
static std::size_t move_unit_internal(undo_list *undo_stack, bool show_move, bool *interrupted, unit_mover &mover)
Definition: move.cpp:1167
game_events::pump_result_t get_village(const map_location &loc, int side, bool *action_timebonus, bool fire_event)
Makes it so the village at the given location is owned by the given side.
Definition: move.cpp:140
EXIT_STATUS start(bool clear_id, const std::string &filename, bool take_screenshot, const std::string &screenshot_filename)
Main interface for launching the editor from the title screen.
void pump()
Process all events currently in the queue.
Definition: events.cpp:478
const color_t GOOD_COLOR
const color_t BAD_COLOR
const color_t NORMAL_COLOR
std::tuple< bool, bool > pump_result_t
Definition: fwd.hpp:29
void show(const std::string &window_id, const t_string &message, const point &mouse, const SDL_Rect &source_rect)
Shows a tip.
Definition: tooltip.cpp:81
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.
static bool is_active(const widget *wgt)
Definition: window.cpp:1284
std::string get_names(const std::string &id)
Returns a comma-separated string of hotkey names.
@ HOTKEY_CONTINUE_MOVE
bool enemy_zoc(const team &current_team, const map_location &loc, const team &viewing_team, bool see_all)
Determines if a given location is in an enemy zone of control.
Definition: pathfind.cpp:134
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
bool interrupt_when_ally_sighted()
Definition: game.cpp:786
game_board * gameboard
Definition: resources.cpp:21
game_data * gamedata
Definition: resources.cpp:23
game_events::manager * game_events
Definition: resources.cpp:25
replay * recorder
Definition: resources.cpp:29
actions::undo_list * undo_stack
Definition: resources.cpp:33
play_controller * controller
Definition: resources.cpp:22
std::shared_ptr< wb::manager > whiteboard
Definition: resources.cpp:34
std::map< std::string, t_string > string_map
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
This module contains various pathfinding functions and utilities.
Define the game's event mechanism.
Replay control code.
The basic class for representing 8-bit RGB or RGBA colour values.
Definition: color.hpp:59
Holds options for calls to function 'announce' (announce).
Definition: display.hpp:611
bool discard_previous
An announcement according these options should replace the previous announce (typical of fast announc...
Definition: display.hpp:620
static const hotkey_command & get_command_by_command(HOTKEY_COMMAND command)
the execute_command argument was changed from HOTKEY_COMMAND to hotkey_command, to be able to call it...
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
static const map_location & null_location()
Definition: location.hpp:81
Data typedef for unit_ability_list.
Definition: unit.hpp:40
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
checkup * checkup_instance
Display units performing various actions: moving, attacking, and dying.
Various functions that implement the undoing (and redoing) of in-game commands.
Various functions implementing vision (through fog of war and shroud).