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