diff --git a/src/actions.cpp b/src/actions.cpp index 7295377e327..2b27910400c 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -70,6 +70,86 @@ private: const gamemap& map_; }; +void move_unit_spectator::add_seen_friend(const unit_map::const_iterator &u) +{ + seen_friends_.push_back(u); +} + + +void move_unit_spectator::add_seen_enemy(const unit_map::const_iterator &u) +{ + seen_enemies_.push_back(u); +} + + +const unit_map::const_iterator& move_unit_spectator::get_ambusher() const +{ + return ambusher_; +} + + +const unit_map::const_iterator& move_unit_spectator::get_failed_teleport() const +{ + return failed_teleport_; +} + + +const std::vector& move_unit_spectator::get_seen_enemies() const +{ + return seen_enemies_; +} + + +const std::vector& move_unit_spectator::get_seen_friends() const +{ + return seen_friends_; +} + + +const unit_map::const_iterator& move_unit_spectator::get_unit() const +{ + return unit_; +} + + +move_unit_spectator::move_unit_spectator(const unit_map &units) + : ambusher_(units.end()),failed_teleport_(units.end()),seen_enemies_(),seen_friends_(),unit_(units.end()) +{ +} + + +move_unit_spectator::~move_unit_spectator() +{ +} + +void move_unit_spectator::reset(const unit_map &units) +{ + ambusher_ = units.end(); + failed_teleport_ = units.end(); + seen_enemies_.clear(); + seen_friends_.clear(); + unit_ = units.end(); +} + + +void move_unit_spectator::set_ambusher(const unit_map::const_iterator &u) +{ + ambusher_ = u; +} + + +void move_unit_spectator::set_failed_teleport(const unit_map::const_iterator &u) +{ + failed_teleport_ = u; +} + + +void move_unit_spectator::set_unit(const unit_map::const_iterator &u) +{ + unit_ = u; +} + + // Conditions placed on victory must be accessible from the global function // check_victory, but shouldn't be passed to that function as parameters, // since it is called from a variety of places. @@ -2177,6 +2257,7 @@ bool clear_shroud(game_display& disp, const gamemap& map, } size_t move_unit(game_display* disp, + move_unit_spectator *move_spectator, const gamemap& map, unit_map& units, std::vector& teams, std::vector route, @@ -2239,11 +2320,17 @@ size_t move_unit(game_display* disp, const unit_map::const_iterator enemy_unit = units.find(*step); if (enemy_unit != units.end()) { if (team.is_enemy(enemy_unit->second.side())) { + if (move_spectator!=NULL) { + move_spectator->set_ambusher(enemy_unit); + } // can't traverse enemy (bug in fog or pathfinding?) should_clear_stack = true; // assuming that this enemy was hidden somehow break; } else if (!tiles_adjacent(*(step-1),*step)) { // can't teleport on ally (on fogged village, with no-leader and view not-shared) + if (move_spectator!=NULL) { + move_spectator->set_failed_teleport(enemy_unit); + } teleport_failed = true; should_clear_stack = true; // we have info not supposed to be shared break; @@ -2304,6 +2391,9 @@ size_t move_unit(game_display* disp, discovered_unit = true; should_clear_stack = true; moves_left = 0; + if (move_spectator!=NULL) { + move_spectator->set_ambusher(it); + } unit_ability_list hides = it->second.get_abilities("hides"); @@ -2408,6 +2498,9 @@ size_t move_unit(game_display* disp, event_mutated |= game_events::pump(); ui = units.find(steps.back()); + if (move_spectator!=NULL) { + move_spectator->set_unit(ui); + } if(undo_stack != NULL) { if(event_mutated || should_clear_stack || ui == units.end()) { @@ -2419,7 +2512,7 @@ size_t move_unit(game_display* disp, } } - if(disp != NULL) { + if ( (disp != NULL) || (move_spectator!=NULL) ) { bool redraw = false; // Show messages on the screen here @@ -2427,13 +2520,17 @@ size_t move_unit(game_display* disp, if (ambushed_string.empty()) ambushed_string = _("Ambushed!"); // We've been ambushed, display an appropriate message - disp->announce(ambushed_string, font::BAD_COLOUR); + if (disp!=NULL) { + disp->announce(ambushed_string, font::BAD_COLOUR); + } redraw = true; } if(teleport_failed) { std::string teleport_string = _ ("Failed teleport! Exit not empty"); - disp->announce(teleport_string, font::BAD_COLOUR); + if (disp!=NULL) { + disp->announce(teleport_string, font::BAD_COLOUR); + } redraw = true; } @@ -2453,56 +2550,68 @@ size_t move_unit(game_display* disp, if(team.is_enemy(u->second.side())) { ++nenemies; + if (move_spectator!=NULL) { + move_spectator->add_seen_enemy(u); + } } else { ++nfriends; + if (move_spectator!=NULL) { + move_spectator->add_seen_friend(u); + } } DBG_NG << "processed...\n"; team.see(u->second.side()-1); } - // The message we display is different depending on - // whether the units sighted were enemies or friends, - // and their respective number. - utils::string_map symbols; - symbols["friends"] = lexical_cast(nfriends); - symbols["enemies"] = lexical_cast(nenemies); - std::string message; - SDL_Color msg_colour; - if(nfriends == 0 || nenemies == 0) { - if(nfriends > 0) { - message = vngettext("Friendly unit sighted", "$friends friendly units sighted", nfriends, symbols); - msg_colour = font::GOOD_COLOUR; - } else if(nenemies > 0) { - message = vngettext("Enemy unit sighted!", "$enemies enemy units sighted!", nenemies, symbols); - msg_colour = font::BAD_COLOUR; + if (disp!=NULL) { + // The message we display is different depending on + // whether the units sighted were enemies or friends, + // and their respective number. + utils::string_map symbols; + symbols["friends"] = lexical_cast(nfriends); + symbols["enemies"] = lexical_cast(nenemies); + std::string message; + SDL_Color msg_colour; + if(nfriends == 0 || nenemies == 0) { + if(nfriends > 0) { + message = vngettext("Friendly unit sighted", "$friends friendly units sighted", nfriends, symbols); + msg_colour = font::GOOD_COLOUR; + } else if(nenemies > 0) { + message = vngettext("Enemy unit sighted!", "$enemies enemy units sighted!", nenemies, symbols); + msg_colour = font::BAD_COLOUR; + } } - } - else { - symbols["friendphrase"] = vngettext("Part of 'Units sighted! (...)' sentence^1 friendly", "$friends friendly", nfriends, symbols); - symbols["enemyphrase"] = vngettext("Part of 'Units sighted! (...)' sentence^1 enemy", "$enemies enemy", nenemies, symbols); - message = vgettext("Units sighted! ($friendphrase, $enemyphrase)", symbols); - msg_colour = font::NORMAL_COLOUR; - } - - if(steps.size() < route.size()) { - // See if the "Continue Move" action has an associated hotkey - const hotkey::hotkey_item& hk = hotkey::get_hotkey(hotkey::HOTKEY_CONTINUE_MOVE); - if(!hk.null()) { - symbols["hotkey"] = hk.get_name(); - message += "\n" + vgettext("(press $hotkey to keep moving)", symbols); + else { + symbols["friendphrase"] = vngettext("Part of 'Units sighted! (...)' sentence^1 friendly", "$friends friendly", nfriends, symbols); + symbols["enemyphrase"] = vngettext("Part of 'Units sighted! (...)' sentence^1 enemy", "$enemies enemy", nenemies, symbols); + message = vgettext("Units sighted! ($friendphrase, $enemyphrase)", symbols); + msg_colour = font::NORMAL_COLOUR; } - } - disp->announce(message, msg_colour); - redraw = true; + if(steps.size() < route.size()) { + // See if the "Continue Move" action has an associated hotkey + const hotkey::hotkey_item& hk = hotkey::get_hotkey(hotkey::HOTKEY_CONTINUE_MOVE); + if(!hk.null()) { + symbols["hotkey"] = hk.get_name(); + message += "\n" + vgettext("(press $hotkey to keep moving)", symbols); + } + } + + disp->announce(message, msg_colour); + redraw = true; + } } if (redraw) { // Not sure why this would be needed. Maybe during replays? - disp->draw(); + if (disp!=NULL) { + disp->draw(); + } + } + if (disp!=NULL) { + disp->recalculate_minimap(); } - disp->recalculate_minimap(); } assert(steps.size() <= route.size()); diff --git a/src/actions.hpp b/src/actions.hpp index 3c6e55d7993..5823cc1db7d 100644 --- a/src/actions.hpp +++ b/src/actions.hpp @@ -227,6 +227,66 @@ class attack { }; + +class move_unit_spectator { +public: + /** add a location of a seen friend */ + void add_seen_friend(const unit_map::const_iterator &u); + + + /** add the location of new seen enemy */ + void add_seen_enemy(const unit_map::const_iterator &u); + + + /** get the location of an ambusher */ + const unit_map::const_iterator& get_ambusher() const; + + + /** get the location of a failed teleport */ + const unit_map::const_iterator& get_failed_teleport() const; + + + /** get the locations of seen enemies */ + const std::vector& get_seen_enemies() const; + + + /** get the locations of seen friends */ + const std::vector& get_seen_friends() const; + + + /** get new location of moved unit */ + const unit_map::const_iterator& get_unit() const; + + + /** constructor */ + move_unit_spectator(const unit_map &units); + + + /** destructor */ + virtual ~move_unit_spectator(); + + /** reset all locations to empty values*/ + void reset(const unit_map &units); + + + /** set the location of an ambusher */ + void set_ambusher(const unit_map::const_iterator &u); + + + /** set the location of a failed teleport */ + void set_failed_teleport(const unit_map::const_iterator &u); + + + /** set the iterator to moved unit*/ + void set_unit(const unit_map::const_iterator &u); +private: + unit_map::const_iterator ambusher_; + unit_map::const_iterator failed_teleport_; + std::vector seen_enemies_; + std::vector seen_friends_; + unit_map::const_iterator unit_; +}; + /** * Given the location of a village, will return the 0-based index * of the team that currently owns it, and -1 if it is unowned. @@ -360,6 +420,7 @@ typedef std::deque undo_list; * If undos is not NULL, undo information will be added. */ size_t move_unit(game_display* disp, + move_unit_spectator* move_spectator, const gamemap& map, unit_map& units, std::vector& teams, std::vector steps, diff --git a/src/ai/actions.cpp b/src/ai/actions.cpp index 4ff8efa121c..ead167dd21f 100644 --- a/src/ai/actions.cpp +++ b/src/ai/actions.cpp @@ -306,6 +306,7 @@ move_result::move_result(side_number side, const map_location& from, const map_location& to, bool remove_movement) : action_result(side) , from_(from) + , move_spectator(get_info().units) , to_(to) , remove_movement_(remove_movement) , route_() @@ -336,7 +337,7 @@ const unit *move_result::get_unit(const unit_map &units, const std::vector bool move_result::test_route(const unit &un, const team &my_team, const unit_map &units, const std::vector &teams, const gamemap &map, bool) { - if (from_==to_) { + if (from_==to_) {//@todo 1.7 move is ok in this case if remove_movement_ is set and movement_left is >0 set_error(E_EMPTY_MOVE); return false; } @@ -349,7 +350,7 @@ bool move_result::test_route(const unit &un, const team &my_team, const unit_map //do an A*-search route_ = a_star_search(un.get_location(), to_, 10000.0, &calc, map.w(), map.h(), &allowed_teleports); - return true; + return true;//@todo 1.7 do some tests on returned route } void move_result::do_check_before() @@ -390,6 +391,20 @@ const map_location& move_result::get_unit_location() const void move_result::do_check_after() { + if (move_spectator.get_ambusher().valid()) { + set_error(E_AMBUSHED); + return; + } + if (move_spectator.get_failed_teleport().valid()) { + set_error(E_FAILED_TELEPORT); + return; + } + //@todo 1.7 add 'new units spotted' failure mode + + if (unit_location_!=to_) { + set_error(E_NOT_REACHED_DESTINATION); + return; + } } @@ -418,6 +433,7 @@ void move_result::do_execute() move_unit( /*game_display* disp*/ &info.disp, + /*move_unit_spectator* move_spectator*/ &move_spectator, /*const gamemap& map*/ info.map, /*unit_map& units*/ info.units, /*std::vector& teams*/ info.teams, @@ -428,7 +444,14 @@ void move_result::do_execute() /*bool continue_move*/ true, //@todo: 1.7 set to false after implemeting interrupt awareness /*bool should_clear_shroud*/ true, /*bool is_replay*/ false); - unit_location_ = to_;//@todo: 1.7 modify move_unit to get this info from it + + if (move_spectator.get_unit().valid()){ + unit_location_ = move_spectator.get_unit()->first; + } else { + unit_location_ = map_location(); + } + + set_gamestate_changed(); manager::raise_unit_moved(); @@ -437,6 +460,7 @@ void move_result::do_execute() void move_result::do_init_for_execution() { + move_spectator.reset(get_info().units); } diff --git a/src/ai/actions.hpp b/src/ai/actions.hpp index abac40bb02a..cab46f8225b 100644 --- a/src/ai/actions.hpp +++ b/src/ai/actions.hpp @@ -166,6 +166,9 @@ public: static const int E_NO_UNIT = 2002; static const int E_NOT_OWN_UNIT = 2003; static const int E_INCAPACITATED_UNIT = 2004; + static const int E_AMBUSHED = 2005; + static const int E_FAILED_TELEPORT = 2006; + static const int E_NOT_REACHED_DESTINATION = 2007; virtual std::string do_describe() const; virtual const map_location& get_unit_location() const; protected: @@ -177,6 +180,7 @@ private: const unit *get_unit(const unit_map &units, const std::vector &teams, bool update_knowledge = false); bool test_route(const unit &un, const team &my_team, const unit_map &units, const std::vector &teams, const gamemap &map, bool update_knowledge = false); const map_location from_; + move_unit_spectator move_spectator; const map_location to_; bool remove_movement_; plain_route route_; diff --git a/src/menu_events.cpp b/src/menu_events.cpp index 77d3392f961..b803459c4d6 100644 --- a/src/menu_events.cpp +++ b/src/menu_events.cpp @@ -1565,7 +1565,7 @@ private: assert(route.steps.front() == ui->first); gui_->set_route(&route); - move_unit(gui_,map_,units_,teams_,route.steps,&recorder,&undo_stack_,NULL,continue_move); + move_unit(gui_,NULL,map_,units_,teams_,route.steps,&recorder,&undo_stack_,NULL,continue_move); gui_->invalidate_game_status(); } diff --git a/src/mouse_events.cpp b/src/mouse_events.cpp index aa42c36d26a..67d6bb233c6 100644 --- a/src/mouse_events.cpp +++ b/src/mouse_events.cpp @@ -517,7 +517,7 @@ bool mouse_handler::move_unit_along_current_route(bool check_shroud, bool attack attackmove_ = attackmove; size_t moves = 0; try{ - moves = ::move_unit(&gui(),map_,units_,teams_, + moves = ::move_unit(&gui(),NULL,map_,units_,teams_, steps,&recorder,&undo_stack_,&next_unit_,false,check_shroud); } catch(end_turn_exception&) { attackmove_ = false; diff --git a/src/replay.cpp b/src/replay.cpp index aa1cf5bd5fa..507cbd481eb 100644 --- a/src/replay.cpp +++ b/src/replay.cpp @@ -1057,7 +1057,7 @@ bool do_replay_handle(game_display& disp, const gamemap& map, replay::throw_error(errbuf.str()); } - ::move_unit(&disp, map, units, teams, steps, NULL, NULL, NULL, true, true, true); + ::move_unit(&disp, NULL, map, units, teams, steps, NULL, NULL, NULL, true, true, true); //NOTE: The AI fire sighetd event whem moving in the FoV of team 1 // (supposed to be the human player in SP)