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