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