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