whiteboard/validate_visitor.cpp

Go to the documentation of this file.
00001 /* $Id: validate_visitor.cpp 53876 2012-04-09 12:30:04Z jamit $ */
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 "validate_visitor.hpp"
00021 #include "attack.hpp"
00022 #include "manager.hpp"
00023 #include "move.hpp"
00024 #include "recall.hpp"
00025 #include "recruit.hpp"
00026 #include "side_actions.hpp"
00027 #include "suppose_dead.hpp"
00028 #include "utility.hpp"
00029 
00030 #include "arrow.hpp"
00031 #include "foreach.hpp"
00032 #include "pathfind/pathfind.hpp"
00033 #include "play_controller.hpp"
00034 #include "resources.hpp"
00035 #include "team.hpp"
00036 
00037 namespace wb
00038 {
00039 
00040 validate_visitor::validate_visitor(unit_map& unit_map)
00041     : builder_(unit_map,*this)
00042     , viewer_actions_(*viewer_actions())
00043     , actions_to_erase_()
00044     , arg_itor_()
00045 {
00046     assert(!resources::whiteboard->has_planned_unit_map());
00047 }
00048 
00049 validate_visitor::~validate_visitor()
00050 {
00051 }
00052 
00053 bool validate_visitor::validate_actions()
00054 {
00055     builder_.build_map();
00056 
00057     //FIXME: by reverse iterating this can be done in a more efficiant way
00058     // by using the iterator returned by remove_action it could even be done in visit_all above
00059     if (!actions_to_erase_.empty())
00060     {
00061         int side_actions_size_before = viewer_actions_.size();
00062         LOG_WB << "Erasing " << actions_to_erase_.size() << " invalid actions.\n";
00063         foreach(action_ptr action, actions_to_erase_)
00064         {
00065             viewer_actions_.remove_action(viewer_actions_.get_position_of(action), false);
00066         }
00067         assert(side_actions_size_before - viewer_actions_.size() == actions_to_erase_.size());
00068         actions_to_erase_.clear();
00069         return false;
00070     }
00071     else
00072     {
00073         return true;
00074     }
00075 }
00076 
00077 /* private */
00078 validate_visitor::VALIDITY validate_visitor::evaluate_move_validity(move_ptr m_ptr)
00079 {
00080     move& m = *m_ptr;
00081 
00082     if (!(m.get_source_hex().valid() && m.get_dest_hex().valid()))
00083         return WORTHLESS;
00084 
00085     //Check that the unit still exists in the source hex
00086     unit_map::iterator unit_it;
00087     unit_it = resources::units->find(m.get_source_hex());
00088     if (unit_it == resources::units->end())
00089         return WORTHLESS;
00090 
00091     //check if the unit in the source hex has the same unit id as before,
00092     //i.e. that it's the same unit
00093     if (m.unit_id_ != unit_it->id() || m.unit_underlying_id_ != unit_it->underlying_id())
00094         return WORTHLESS;
00095 
00096     //If the path has at least two hexes (it can have less with the attack subclass), ensure destination hex is free
00097     if (m.get_route().steps.size() >= 2 && get_visible_unit(m.get_dest_hex(),resources::teams->at(viewer_team())) != NULL) {
00098         return WORTHLESS;
00099     }
00100 
00101     //check that the path is good
00102     if (m.get_source_hex() != m.get_dest_hex()) //skip zero-hex move used by attack subclass
00103     {
00104         // Mark the plain route to see if the move can still be done in one turn,
00105         // which is always the case for planned moves
00106         pathfind::marked_route checked_route = pathfind::mark_route(m.get_route().route);
00107 
00108         if (checked_route.marks[checked_route.steps.back()].turns != 1)
00109             return OBSTRUCTED;
00110     }
00111 
00112     return VALID;
00113 }
00114 
00115 // This helper function determines whether there are any invalid actions planned for (*itor)->get_unit()
00116 // that occur earlier in viewer_actions_ than itor.
00117 /* private */
00118 bool validate_visitor::no_previous_invalids(side_actions::iterator const& itor)
00119 {
00120     if(itor == viewer_actions_.begin())
00121         return true;
00122     side_actions::iterator prev_action_of_unit = viewer_actions_.find_last_action_of((*itor)->get_unit(),itor-1);
00123     if(prev_action_of_unit == viewer_actions_.end())
00124         return true;
00125     return (*prev_action_of_unit)->is_valid();
00126 }
00127 
00128 void validate_visitor::visit(move_ptr move)
00129 {
00130     DBG_WB <<"visiting move from " << move->get_source_hex()
00131             << " to " << move->get_dest_hex() << "\n";
00132     //invalidate start and end hexes so number display is updated properly
00133     resources::screen->invalidate(move->get_source_hex());
00134     resources::screen->invalidate(move->get_dest_hex());
00135 
00136     switch(evaluate_move_validity(move)) //< private helper fcn
00137     {
00138     case VALID:
00139         // Now call the superclass to apply the result of this move to the unit map,
00140         // so that further pathfinding takes it into account.
00141         move->set_valid(true);
00142         break;
00143     case OBSTRUCTED:
00144         move->set_valid(false);
00145         break;
00146     case WORTHLESS:
00147         move->set_valid(false);
00148         // Erase only if no previous invalid actions are planned for this unit -- otherwise, just mark it invalid.
00149         // Otherwise, we wouldn't be able to keep invalid actions that depend on previous invalid actions.
00150         if(viewer_team() == move->team_index() //< Don't mess with any other team's queue -- only our own
00151                 && no_previous_invalids(arg_itor_)) //< private helper fcn
00152         {
00153             LOG_WB << "Worthless invalid move detected, adding to actions_to_erase_.\n";
00154             actions_to_erase_.insert(move);
00155         }
00156         break;
00157     }
00158 }
00159 
00160 void validate_visitor::visit(attack_ptr attack)
00161 {
00162     DBG_WB <<"visiting attack from " << attack->get_dest_hex()
00163             << " to " << attack->target_hex_ << "\n";
00164     //invalidate target hex to make sure attack indicators are updated
00165     resources::screen->invalidate(attack->get_dest_hex());
00166     resources::screen->invalidate(attack->target_hex_);
00167 
00168     if  (
00169             // Verify that the unit that planned this attack exists
00170             attack->get_unit()
00171             // Verify that the target hex is still valid
00172             && attack->target_hex_.valid()
00173             // Verify that the target hex isn't empty
00174             && resources::units->find(attack->target_hex_) != resources::units->end()
00175             // Verify that the attacking unit has attacks left
00176             && attack->get_unit()->attacks_left() > 0
00177             // Verify that the attacker and target are enemies
00178             && (*resources::teams)[attack->get_unit()->side() - 1].is_enemy(resources::units->find(attack->target_hex_)->side())
00179 
00180             //@todo: (maybe) verify that the target hex contains the same unit that before,
00181             // comparing for example the unit ID
00182         )
00183     {
00184         //All checks pass, so call the visitor on the superclass
00185         visit(boost::static_pointer_cast<move>(attack));
00186     }
00187     else
00188     {
00189         attack->set_valid(false);
00190 
00191         if (viewer_team() == attack->team_index()) //< Don't mess with any other team's queue -- only our own
00192         {
00193             LOG_WB << "Worthless invalid attack detected, adding to actions_to_erase_.\n";
00194             actions_to_erase_.insert(attack);
00195         }
00196     }
00197 }
00198 
00199 void validate_visitor::visit(recruit_ptr recruit)
00200 {
00201     DBG_WB << "visiting recruit on hex " << recruit->recruit_hex_ << "\n";
00202     //invalidate recruit hex so number display is updated properly
00203     resources::screen->invalidate(recruit->recruit_hex_);
00204 
00205     size_t team_index = recruit->team_index();
00206 
00207     //Check that destination hex is still free
00208     if(resources::units->find(recruit->recruit_hex_) != resources::units->end())
00209     {
00210         LOG_WB << "Recruit set as invalid because target hex is occupied.\n";
00211         recruit->set_valid(false);
00212     }
00213     //Check that unit to recruit is still in side's recruit list
00214     if (recruit->is_valid())
00215     {
00216         const std::set<std::string>& recruits = (*resources::teams)[team_index].recruits();
00217         if (recruits.find(recruit->unit_name_) == recruits.end())
00218         {
00219             recruit->set_valid(false);
00220             LOG_WB << " Validate visitor: Planned recruit invalid since unit is not in recruit list anymore.\n";
00221         }
00222     }
00223     //Check that there is still enough gold to recruit this unit
00224     if (recruit->is_valid() && recruit->temp_unit_->cost() > (*resources::teams)[team_index].gold())
00225     {
00226         LOG_WB << "Recruit set as invalid, team doesn't have enough gold.\n";
00227         recruit->set_valid(false);
00228     }
00229     //Check that there is a leader available to recruit this unit
00230     if(recruit->is_valid() && !find_recruiter(recruit->team_index(),recruit->get_recruit_hex()))
00231     {
00232         LOG_WB << "Recruit set as invalid, no leader can recruit this unit.\n";
00233         recruit->set_valid(false);
00234     }
00235 
00236     if(!recruit->is_valid())
00237     {
00238         if(viewer_team() == recruit->team_index()) //< Don't mess with any other team's queue -- only our own
00239         {
00240             LOG_WB << "Invalid recruit detected, adding to actions_to_erase_.\n";
00241             actions_to_erase_.insert(recruit);
00242         }
00243     }
00244 }
00245 
00246 void validate_visitor::visit(recall_ptr recall)
00247 {
00248     DBG_WB << "visiting recall on hex " << recall->recall_hex_ << "\n";
00249     //invalidate recall hex so number display is updated properly
00250     resources::screen->invalidate(recall->recall_hex_);
00251 
00252     size_t team_index = recall->team_index();
00253 
00254     //Check that destination hex is still free
00255     if(resources::units->find(recall->recall_hex_) != resources::units->end())
00256     {
00257         LOG_WB << "Recall set as invalid because target hex is occupied.\n";
00258         recall->set_valid(false);
00259     }
00260     //Check that unit to recall is still in side's recall list
00261     if (recall->is_valid())
00262     {
00263         const std::vector<unit>& recalls = (*resources::teams)[team_index].recall_list();
00264         if ( find_if_matches_id(recalls, recall->temp_unit_->id()) == recalls.end() )
00265         {
00266             recall->set_valid(false);
00267             LOG_WB << " Validate visitor: Planned recall invalid since unit is not in recall list anymore.\n";
00268         }
00269     }
00270     //Check that there is still enough gold to recall this unit
00271     if (recall->is_valid()
00272             && (*resources::teams)[team_index].recall_cost() > (*resources::teams)[team_index].gold())
00273     {
00274         LOG_WB << "Recall set as invalid, team doesn't have enough gold.\n";
00275         recall->set_valid(false);
00276     }
00277     //Check that there is a leader available to recall this unit
00278     if(recall->is_valid() && !find_recruiter(recall->team_index(),recall->get_recall_hex()))
00279     {
00280         LOG_WB << "Recall set as invalid, no leader can recall this unit.\n";
00281         recall->set_valid(false);
00282     }
00283 
00284 
00285     if(!recall->is_valid())
00286     {
00287         if(viewer_team() == recall->team_index()) //< Don't mess with any other team's queue -- only our own
00288         {
00289             LOG_WB << "Invalid recall detected, adding to actions_to_erase_.\n";
00290             actions_to_erase_.insert(recall);
00291         }
00292     }
00293 }
00294 
00295 void validate_visitor::visit(suppose_dead_ptr sup_d)
00296 {
00297     DBG_WB << "visiting suppose_dead on hex " << sup_d->loc_ << "\n";
00298     //invalidate suppose-dead hex so number display is updated properly
00299     resources::screen->invalidate(sup_d->loc_);
00300 
00301     if(!sup_d->get_source_hex().valid())
00302         sup_d->set_valid(false);
00303 
00304     unit_map::const_iterator unit_it;
00305     //Check that the unit still exists in the source hex
00306     if(sup_d->valid_)
00307     {
00308         unit_it = resources::units->find(sup_d->get_source_hex());
00309 
00310         if(unit_it == resources::units->end())
00311         {
00312             sup_d->set_valid(false);
00313         }
00314     }
00315 
00316     //check if the unit in the source hex has the same unit id as before,
00317     //i.e. that it's the same unit
00318     if(sup_d->valid_ && sup_d->unit_id_ != unit_it->id())
00319     {
00320         sup_d->set_valid(false);
00321     }
00322 
00323     if(!sup_d->valid_)
00324     {
00325         if(viewer_team() == sup_d->team_index()) //< Don't mess with any other team's queue -- only our own
00326         {
00327             LOG_WB << "Invalid suppose_dead detected, adding to actions_to_erase_.\n";
00328             actions_to_erase_.insert(sup_d);
00329         }
00330     }
00331 }
00332 
00333 void validate_visitor::helper::validate(side_actions::iterator const& itor)
00334 {
00335     parent_.arg_itor_ = itor;
00336     (*itor)->accept(parent_);
00337 }
00338 
00339 }//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