whiteboard/move.cpp

Go to the documentation of this file.
00001 /* $Id: move.cpp 53781 2012-04-05 11:04:26Z gabba $ */
00002 /*
00003  Copyright (C) 2010 - 2012 by Gabriel Morin <gabrielmorin (at) gmail (dot) com>
00004  Part of the Battle for Wesnoth Project http://www.wesnoth.org
00005 
00006  This program is free software; you can redistribute it and/or modify
00007  it under the terms of the GNU General Public License as published by
00008  the Free Software Foundation; either version 2 of the License, or
00009    (at your option) any later version.
00010  This program is distributed in the hope that it will be useful,
00011  but WITHOUT ANY WARRANTY.
00012 
00013  See the COPYING file for more details.
00014  */
00015 
00016 /**
00017  * @file
00018  */
00019 
00020 #include "move.hpp"
00021 
00022 #include "visitor.hpp"
00023 #include "manager.hpp"
00024 #include "side_actions.hpp"
00025 #include "utility.hpp"
00026 
00027 #include "arrow.hpp"
00028 #include "config.hpp"
00029 #include "foreach.hpp"
00030 #include "game_end_exceptions.hpp"
00031 #include "mouse_events.hpp"
00032 #include "play_controller.hpp"
00033 #include "replay.hpp"
00034 #include "resources.hpp"
00035 #include "team.hpp"
00036 #include "unit.hpp"
00037 #include "unit_display.hpp"
00038 #include "unit_map.hpp"
00039 
00040 namespace wb {
00041 
00042 std::ostream& operator<<(std::ostream &s, move_ptr move)
00043 {
00044     assert(move);
00045     return move->print(s);
00046 }
00047 
00048 std::ostream& operator<<(std::ostream &s, move_const_ptr move)
00049 {
00050     assert(move);
00051     return move->print(s);
00052 }
00053 
00054 std::ostream& move::print(std::ostream &s) const
00055 {
00056     s << "Move for unit " << get_unit()->name() << " [" << get_unit()->id() << "] "
00057             << "from (" << get_source_hex() << ") to (" << get_dest_hex() << ")";
00058     return s;
00059 }
00060 
00061 move::move(size_t team_index, bool hidden, unit& u, const pathfind::marked_route& route,
00062         arrow_ptr arrow, fake_unit_ptr fake_unit)
00063 : action(team_index,hidden),
00064   unit_underlying_id_(u.underlying_id()),
00065   unit_id_(),
00066   route_(new pathfind::marked_route(route)),
00067   movement_cost_(0),
00068   turn_number_(0),
00069   arrow_(arrow),
00070   fake_unit_(fake_unit),
00071   valid_(true),
00072   arrow_brightness_(),
00073   arrow_texture_(),
00074   mover_(),
00075   fake_unit_hidden_(false)
00076 {
00077     assert(!route_->steps.empty());
00078 
00079     if(hidden)
00080         fake_unit_->set_hidden(true);
00081 
00082     this->init();
00083 }
00084 
00085 move::move(config const& cfg, bool hidden)
00086     : action(cfg,hidden)
00087     , unit_underlying_id_(0)
00088     , unit_id_()
00089     , route_(new pathfind::marked_route())
00090     , movement_cost_(0)
00091     , turn_number_(0)
00092     , arrow_(new arrow(hidden))
00093     , fake_unit_()
00094     , valid_(true)
00095     , arrow_brightness_()
00096     , arrow_texture_()
00097     , mover_()
00098     , fake_unit_hidden_(false)
00099 {
00100     // Construct and validate unit_
00101     unit_map::iterator unit_itor = resources::units->find(cfg["unit_"]);
00102     if(unit_itor == resources::units->end())
00103         throw action::ctor_err("move: Invalid underlying_id");
00104     unit_underlying_id_ = unit_itor->underlying_id();
00105 
00106     // Construct and validate route_
00107     config const& route_cfg = cfg.child("route_");
00108     if(!route_cfg)
00109         throw action::ctor_err("move: Invalid route_");
00110     route_->move_cost = route_cfg["move_cost"];
00111     foreach(config const& loc_cfg, route_cfg.child_range("step")) {
00112         route_->steps.push_back(map_location(loc_cfg["x"],loc_cfg["y"]));
00113     }
00114     foreach(config const& mark_cfg, route_cfg.child_range("mark")) {
00115         route_->marks[map_location(mark_cfg["x"],mark_cfg["y"])]
00116                 = pathfind::marked_route::mark(mark_cfg["turns"],mark_cfg["zoc"],mark_cfg["capture"],mark_cfg["invisible"]);
00117     }
00118 
00119     // Validate route_ some more
00120     std::vector<map_location> const& steps = route_->steps;
00121     if(steps.empty())
00122         throw action::ctor_err("move: Invalid route_");
00123 
00124     // Construct arrow_
00125     arrow_->set_color(team::get_side_color_index(side_number()));
00126     arrow_->set_style(arrow::STYLE_STANDARD);
00127     arrow_->set_path(route_->steps);
00128 
00129     // Construct fake_unit_
00130     fake_unit_.reset(new game_display::fake_unit(*get_unit()) );
00131     if(hidden)
00132         fake_unit_->set_hidden(true);
00133     fake_unit_->place_on_game_display(resources::screen);
00134     fake_unit_->set_ghosted(true);
00135     unit_display::move_unit(route_->steps, *fake_unit_, *resources::teams, false); //get facing right
00136     fake_unit_->set_location(route_->steps.back());
00137 
00138     this->init();
00139 }
00140 
00141 void move::init()
00142 {
00143     assert(get_unit());
00144     unit_id_ = get_unit()->id();
00145 
00146     //This action defines the future position of the unit, make its fake unit more visible
00147     //than previous actions' fake units
00148     if (fake_unit_)
00149     {
00150         fake_unit_->set_ghosted(true);
00151     }
00152     side_actions_ptr side_actions = resources::teams->at(team_index()).get_side_actions();
00153     side_actions::iterator action = side_actions->find_last_action_of(get_unit());
00154     if (action != side_actions->end())
00155     {
00156         if (move_ptr move = boost::dynamic_pointer_cast<class move>(*action))
00157         {
00158             if (move->fake_unit_)
00159                 move->fake_unit_->set_disabled_ghosted(true);
00160         }
00161     }
00162 
00163     this->calculate_move_cost();
00164 
00165     // Initialize arrow_brightness_ and arrow_texture_ using arrow_->style_
00166     arrow::STYLE arrow_style = arrow_->get_style();
00167     if(arrow_style == arrow::STYLE_STANDARD)
00168     {
00169         arrow_brightness_ = ARROW_BRIGHTNESS_STANDARD;
00170         arrow_texture_ = ARROW_TEXTURE_VALID;
00171     }
00172     else if(arrow_style == arrow::STYLE_HIGHLIGHTED)
00173     {
00174         arrow_brightness_ = ARROW_BRIGHTNESS_HIGHLIGHTED;
00175         arrow_texture_ = ARROW_TEXTURE_VALID;
00176     }
00177     else if(arrow_style == arrow::STYLE_FOCUS)
00178     {
00179         arrow_brightness_ = ARROW_BRIGHTNESS_FOCUS;
00180         arrow_texture_ = ARROW_TEXTURE_VALID;
00181     }
00182     else if(arrow_style == arrow::STYLE_FOCUS_INVALID)
00183     {
00184         arrow_brightness_ = ARROW_BRIGHTNESS_STANDARD;
00185         arrow_texture_ = ARROW_TEXTURE_INVALID;
00186     }
00187 }
00188 
00189 void move::accept(visitor& v)
00190 {
00191     v.visit(shared_from_this());
00192 }
00193 
00194 void move::execute(bool& success, bool& complete)
00195 {
00196     if (!valid_) {
00197         success = false;
00198         //Setting complete to true signifies to side_actions to delete the planned action.
00199         complete = true;
00200         return;
00201     }
00202 
00203     if (get_source_hex() == get_dest_hex()) {
00204         //zero-hex move, used by attack subclass
00205         success = complete = true;
00206         return;
00207     }
00208 
00209     LOG_WB << "Executing: " << shared_from_this() << "\n";
00210 
00211     set_arrow_brightness(ARROW_BRIGHTNESS_HIGHLIGHTED);
00212     hide_fake_unit();
00213 
00214     events::mouse_handler& mouse_handler = resources::controller->get_mouse_handler_base();
00215     std::set<map_location> adj_enemies = mouse_handler.get_adj_enemies(get_dest_hex(), side_number());
00216 
00217     map_location final_location;
00218     bool steps_finished;
00219     bool enemy_sighted;
00220     {
00221         team const& owner_team = resources::teams->at(team_index());
00222         try {
00223             steps_finished = mouse_handler.move_unit_along_route(*route_, &final_location, owner_team.auto_shroud_updates(), &enemy_sighted);
00224         } catch (end_turn_exception&) {
00225             set_arrow_brightness(ARROW_BRIGHTNESS_STANDARD);
00226             throw; // we rely on the caller to delete this action
00227         }
00228         // final_location now contains the final unit location
00229         // if that isn't needed, pass NULL rather than &final_location
00230         // Also, enemy_sighted now tells whether a unit was sighted during the move
00231     }
00232     if(mouse_handler.get_adj_enemies(final_location,side_number()) != adj_enemies)
00233         enemy_sighted = true; //< "ambushed" on last hex
00234 
00235     unit_map::const_iterator unit_it;
00236 
00237     if (final_location == route_->steps.front())
00238     {
00239         LOG_WB << "Move execution resulted in zero movement.\n";
00240         success = false;
00241         complete = true;
00242     }
00243     else if (final_location.valid() &&
00244             (unit_it = resources::units->find(final_location)) != resources::units->end()
00245             && unit_it->id() == unit_id_)
00246     {
00247         if (steps_finished && route_->steps.back() == final_location) //reached destination
00248         {
00249             complete = true;
00250 
00251             //check if new enemies are now visible
00252             if(enemy_sighted)
00253             {
00254                 LOG_WB << "Move completed, but interrupted on final hex. Halting.\n";
00255                 //reset to a single-hex path, just in case *this is a wb::attack
00256                 arrow_.reset();
00257                 route_->steps = std::vector<map_location>(1,route_->steps.back());
00258                 success = false;
00259             }
00260             else // Everything went smoothly
00261                 success = true;
00262         }
00263         else // Move was interrupted, probably by enemy unit sighted
00264         {
00265             success = false;
00266 
00267             LOG_WB << "Move finished at (" << final_location << ") instead of at (" << get_dest_hex() << "), analyzing\n";
00268             std::vector<map_location>::iterator start_new_path;
00269             bool found = false;
00270             for (start_new_path = route_->steps.begin(); ((start_new_path != route_->steps.end()) && !found); ++start_new_path)
00271             {
00272                 if (*start_new_path == final_location)
00273                 {
00274                     found = true;
00275                 }
00276             }
00277             if (found)
00278             {
00279                 --start_new_path; //since the for loop incremented the iterator once after we found the right one.
00280                 std::vector<map_location> new_path(start_new_path, route_->steps.end());
00281                 LOG_WB << "Setting new path for this move from (" << new_path.front()
00282                         << ") to (" << new_path.back() << ").\n";
00283                 //FIXME: probably better to use the new calculate_new_route instead of doing this
00284                 route_->steps = new_path;
00285                 arrow_->set_path(new_path);
00286                 complete = false;
00287             }
00288             else //Unit ended up in location outside path, likely due to a WML event
00289             {
00290                 WRN_WB << "Unit ended up in location outside path during move execution.\n";
00291                 complete = true;
00292             }
00293         }
00294     }
00295     else //Unit disappeared from the map, likely due to a WML event
00296     {
00297         WRN_WB << "Unit disappeared from map during move execution.\n";
00298         success = false;
00299         complete = true;
00300     }
00301 
00302     if(!complete)
00303     {
00304         set_arrow_brightness(ARROW_BRIGHTNESS_STANDARD);
00305         show_fake_unit();
00306     }
00307 }
00308 
00309 unit* move::get_unit() const
00310 {
00311     unit_map::iterator itor = resources::units->find(unit_underlying_id_);
00312     if (itor.valid())
00313         return &*itor;
00314     else
00315         return NULL;
00316 }
00317 
00318 map_location move::get_source_hex() const
00319 {
00320     assert(route_ && !route_->steps.empty());
00321     return route_->steps.front();
00322 }
00323 
00324 map_location move::get_dest_hex() const
00325 {
00326     assert(route_ && !route_->steps.empty());
00327     return route_->steps.back();
00328 }
00329 
00330 void move::set_route(const pathfind::marked_route& route)
00331 {
00332     route_.reset(new pathfind::marked_route(route));
00333     this->calculate_move_cost();
00334     arrow_->set_path(route_->steps);
00335 }
00336 
00337 bool move::calculate_new_route(const map_location& source_hex, const map_location& dest_hex)
00338 {
00339     pathfind::plain_route new_plain_route;
00340     pathfind::shortest_path_calculator path_calc(*get_unit(),
00341                         resources::teams->at(team_index()), *resources::units,
00342                         *resources::teams, *resources::game_map);
00343     new_plain_route = pathfind::a_star_search(source_hex,
00344                         dest_hex, 10000, &path_calc, resources::game_map->w(), resources::game_map->h());
00345     if (new_plain_route.move_cost >= path_calc.getNoPathValue()) return false;
00346     route_.reset(new pathfind::marked_route(pathfind::mark_route(new_plain_route)));
00347     calculate_move_cost();
00348     return true;
00349 }
00350 
00351 void move::apply_temp_modifier(unit_map& unit_map)
00352 {
00353     if (get_source_hex() == get_dest_hex())
00354         return; //zero-hex move, used by attack subclass
00355 
00356     //@todo: deal with multi-turn moves, which may for instance end their first turn
00357     // by capturing a village
00358 
00359     //@todo: we may need to change unit status here and change it back in remove_temp_modifier
00360     unit* unit;
00361     {
00362         unit_map::iterator unit_it = unit_map.find(get_source_hex());
00363         assert(unit_it != unit_map.end());
00364         unit = &*unit_it;
00365     }
00366 
00367     //Modify movement points
00368     DBG_WB <<"Move: Changing movement points for unit " << unit->name() << " [" << unit->id()
00369             << "] from " << unit->movement_left() << " to "
00370             << unit->movement_left() - movement_cost_ << ".\n";
00371     unit->set_movement(unit->movement_left() - movement_cost_);
00372 
00373     // Move the unit
00374     DBG_WB << "Move: Temporarily moving unit " << unit->name() << " [" << unit->id()
00375             << "] from (" << get_source_hex() << ") to (" << get_dest_hex() <<")\n";
00376     mover_.reset(new temporary_unit_mover(unit_map,get_source_hex(), get_dest_hex()));
00377 
00378     //Update status of fake unit (not undone by remove_temp_modifiers)
00379     //@todo this contradicts the name "temp_modifiers"
00380     fake_unit_->set_movement(unit->movement_left());
00381 }
00382 
00383 void move::remove_temp_modifier(unit_map&)
00384 {
00385     if (get_source_hex() == get_dest_hex())
00386         return; //zero-hex move, probably used by attack subclass
00387 
00388     unit* unit;
00389     {
00390         unit_map::iterator unit_it = resources::units->find(get_dest_hex());
00391         assert(unit_it != resources::units->end());
00392         unit = &*unit_it;
00393     }
00394 
00395     // Restore movement points
00396     DBG_WB << "Move: Changing movement points for unit " << unit->name() << " [" << unit->id()
00397                 << "] from " << unit->movement_left() << " to "
00398                 << unit->movement_left() + movement_cost_ << ".\n";
00399     unit->set_movement(unit->movement_left() + movement_cost_);
00400 
00401     // Restore the unit to its original position
00402     mover_.reset();
00403 }
00404 
00405 void move::draw_hex(map_location const& hex)
00406 {
00407     //display turn info for turns 2 and above
00408     if (hex == get_dest_hex() && turn_number_ >= 2)
00409     {
00410         std::stringstream turn_text;
00411         turn_text << turn_number_;
00412         resources::screen->draw_text_in_hex(hex, display::LAYER_MOVE_INFO, turn_text.str(), 17, font::NORMAL_COLOR, 0.5,0.8);
00413     }
00414 }
00415 
00416 void move::do_hide()
00417 {
00418     arrow_->hide();
00419     if(!fake_unit_hidden_)
00420         fake_unit_->set_hidden(true);
00421 }
00422 
00423 void move::do_show()
00424 {
00425     arrow_->show();
00426     if(!fake_unit_hidden_)
00427         fake_unit_->set_hidden(false);
00428 }
00429 
00430 void move::hide_fake_unit()
00431 {
00432     fake_unit_hidden_ = true;
00433     if(!hidden())
00434         fake_unit_->set_hidden(true);
00435 }
00436 
00437 void move::show_fake_unit()
00438 {
00439     fake_unit_hidden_ = false;
00440     if(!hidden())
00441         fake_unit_->set_hidden(false);
00442 }
00443 
00444 map_location move::get_numbering_hex() const
00445 {
00446     return get_dest_hex();
00447 }
00448 
00449 void move::set_valid(bool valid)
00450 {
00451     if(valid_ != valid)
00452     {
00453         valid_ = valid;
00454         if(valid_)
00455             set_arrow_texture(ARROW_TEXTURE_VALID);
00456         else
00457             set_arrow_texture(ARROW_TEXTURE_INVALID);
00458     }
00459 }
00460 
00461 config move::to_config() const
00462 {
00463     config final_cfg = action::to_config();
00464 
00465     final_cfg["type"]="move";
00466     final_cfg["unit_"]=static_cast<int>(unit_underlying_id_);
00467 //  final_cfg["movement_cost_"]=movement_cost_; //Unnecessary
00468 //  final_cfg["unit_id_"]=unit_id_; //Unnecessary
00469 
00470     //Serialize route_
00471     config route_cfg;
00472     route_cfg["move_cost"]=route_->move_cost;
00473     foreach(map_location const& loc, route_->steps)
00474     {
00475         config loc_cfg;
00476         loc_cfg["x"]=loc.x;
00477         loc_cfg["y"]=loc.y;
00478         route_cfg.add_child("step",loc_cfg);
00479     }
00480     typedef std::pair<map_location,pathfind::marked_route::mark> pair_loc_mark;
00481     foreach(pair_loc_mark const& item, route_->marks)
00482     {
00483         config mark_cfg;
00484         mark_cfg["x"]=item.first.x;
00485         mark_cfg["y"]=item.first.y;
00486         mark_cfg["turns"]=item.second.turns;
00487         mark_cfg["zoc"]=item.second.zoc;
00488         mark_cfg["capture"]=item.second.capture;
00489         mark_cfg["invisible"]=item.second.invisible;
00490         route_cfg.add_child("mark",mark_cfg);
00491     }
00492     final_cfg.add_child("route_",route_cfg);
00493 
00494     return final_cfg;
00495 }
00496 
00497 void move::calculate_move_cost()
00498 {
00499     assert(get_unit());
00500     assert(route_);
00501     if (get_source_hex().valid() && get_dest_hex().valid() && get_source_hex() != get_dest_hex())
00502     {
00503 
00504         // @todo: find a better treatment of movement points when defining moves out-of-turn
00505         if(get_unit()->movement_left() - route_->move_cost < 0
00506                 && resources::controller->current_side() == resources::screen->viewing_side()) {
00507             WRN_WB << "Move defined with insufficient movement left.\n";
00508         }
00509 
00510         // If unit finishes move in a village it captures, set the move cost to unit's movement_left()
00511          if (route_->marks[get_dest_hex()].capture)
00512          {
00513              movement_cost_ = get_unit()->movement_left();
00514          }
00515          else
00516          {
00517              movement_cost_ = route_->move_cost;
00518          }
00519     }
00520 }
00521 
00522 //If you add more arrow styles, this will need to change
00523 /* private */
00524 void move::update_arrow_style()
00525 {
00526     if(arrow_texture_ == ARROW_TEXTURE_INVALID)
00527     {
00528         arrow_->set_style(arrow::STYLE_FOCUS_INVALID);
00529         return;
00530     }
00531 
00532     switch(arrow_brightness_)
00533     {
00534     case ARROW_BRIGHTNESS_STANDARD:
00535         arrow_->set_style(arrow::STYLE_STANDARD);
00536         break;
00537     case ARROW_BRIGHTNESS_HIGHLIGHTED:
00538         arrow_->set_style(arrow::STYLE_HIGHLIGHTED);
00539         break;
00540     case ARROW_BRIGHTNESS_FOCUS:
00541         arrow_->set_style(arrow::STYLE_FOCUS);
00542         break;
00543     }
00544 }
00545 
00546 } // end namespace wb
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Fri May 25 2012 01:03:15 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs