ai/actions.cpp

Go to the documentation of this file.
00001 /* $Id: actions.cpp 53876 2012-04-09 12:30:04Z jamit $ */
00002 /*
00003    Copyright (C) 2009 - 2012 by Yurii Chernyi <terraninfo@terraninfo.net>
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  * Managing the AI-Game interaction - AI actions and their results
00018  * @file
00019  */
00020 
00021 /**
00022  * A small explanation about what's going on here:
00023  * Each action has access to two game_info objects
00024  * First is 'info' - real information
00025  * Second is 'subjective info' - AIs perception of what's going on
00026  * So, when we check_before action, we use 'subjective info' and don't
00027  * touch real 'info' at all.
00028  * But when we actually want to execute an action, we firstly check
00029  * 'subjective info' and then (if subjective check is ok) do the same
00030  * check on  real 'info'. There's a caveat: if we fail an action based
00031  * on real 'info', then we NEED to update AIs knowledge to avoid the ai
00032  * doing the same thing again.
00033  * So far the use of 'subjective info' is stubbed out.
00034  */
00035 
00036 #include "actions.hpp"
00037 #include "manager.hpp"
00038 
00039 #include "../actions.hpp"
00040 #include "../dialogs.hpp"
00041 #include "../game_end_exceptions.hpp"
00042 #include "../game_preferences.hpp"
00043 #include "../log.hpp"
00044 #include "../mouse_handler_base.hpp"
00045 #include "../pathfind/teleport.hpp"
00046 #include "../play_controller.hpp"
00047 #include "../replay.hpp"
00048 #include "../resources.hpp"
00049 #include "../statistics.hpp"
00050 #include "../team.hpp"
00051 
00052 namespace ai {
00053 
00054 static lg::log_domain log_ai_actions("ai/actions");
00055 #define DBG_AI_ACTIONS LOG_STREAM(debug, log_ai_actions)
00056 #define LOG_AI_ACTIONS LOG_STREAM(info, log_ai_actions)
00057 #define WRN_AI_ACTIONS LOG_STREAM(warn, log_ai_actions)
00058 #define ERR_AI_ACTIONS LOG_STREAM(err, log_ai_actions)
00059 
00060 // =======================================================================
00061 // AI ACTIONS
00062 // =======================================================================
00063 action_result::action_result( side_number side )
00064     : return_value_checked_(true),side_(side),status_(AI_ACTION_SUCCESS),is_execution_(false),is_gamestate_changed_(false)
00065 {
00066 }
00067 
00068 
00069 action_result::~action_result()
00070 {
00071     if (!return_value_checked_) {
00072         ERR_AI_ACTIONS << "Return value of AI ACTION was not checked. This may cause bugs! " << std::endl;
00073     }
00074 }
00075 
00076 
00077 void action_result::check_after()
00078 {
00079     do_check_after();
00080 }
00081 
00082 
00083 void action_result::check_before()
00084 {
00085     do_check_before();
00086 }
00087 
00088 
00089 void action_result::execute()
00090 {
00091     is_execution_ = true;
00092     init_for_execution();
00093     check_before();
00094     if (is_success()){
00095         do_execute();
00096         try {
00097             resources::controller->check_victory();
00098         } catch (...) {
00099             is_ok(); //Silences "unchecked result" warning
00100             throw;
00101         }
00102     }
00103     if (is_success()){
00104         check_after();
00105     }
00106     is_execution_ = false;
00107 }
00108 
00109 void action_result::init_for_execution()
00110 {
00111     return_value_checked_ = false;
00112     is_gamestate_changed_ = false;
00113     status_ =  action_result::AI_ACTION_SUCCESS;
00114     do_init_for_execution();
00115 }
00116 
00117 
00118 bool action_result::is_gamestate_changed() const
00119 {
00120     return is_gamestate_changed_;
00121 }
00122 
00123 
00124 bool action_result::is_ok()
00125 {
00126     return_value_checked_ = true;
00127     return is_success();
00128 }
00129 
00130 
00131 void action_result::set_error(int error_code, bool log_as_error){
00132     status_ = error_code;
00133     if (is_execution()) {
00134         if (log_as_error) {
00135             ERR_AI_ACTIONS << "Error #"<<error_code<<" ("<<actions::get_error_name(error_code)<<") in "<< do_describe();
00136         } else {
00137             LOG_AI_ACTIONS << "Error #"<<error_code<<" ("<<actions::get_error_name(error_code)<<") in "<< do_describe();
00138         }
00139     } else {
00140         LOG_AI_ACTIONS << "Error #"<<error_code<<" ("<<actions::get_error_name(error_code)<<") when checking "<< do_describe();
00141     }
00142 }
00143 
00144 
00145 void action_result::set_gamestate_changed()
00146 {
00147     is_gamestate_changed_ = true;
00148 }
00149 
00150 
00151 int action_result::get_status() const
00152 {
00153     return status_;
00154 }
00155 
00156 bool action_result::is_success() const
00157 {
00158     return (status_ == action_result::AI_ACTION_SUCCESS);
00159 }
00160 
00161 
00162 bool action_result::is_execution() const
00163 {
00164     return is_execution_;
00165 }
00166 
00167 
00168 game_info& action_result::get_info() const
00169 {
00170     return manager::get_active_ai_info_for_side(get_side());
00171 }
00172 
00173 
00174 team& action_result::get_my_team() const
00175 {
00176     return (*resources::teams)[side_-1];
00177 }
00178 
00179 
00180 // attack_result
00181 attack_result::attack_result( side_number side, const map_location& attacker_loc, const map_location& defender_loc, int attacker_weapon, double aggression)
00182     : action_result(side), attacker_loc_(attacker_loc), defender_loc_(defender_loc), attacker_weapon_(attacker_weapon), aggression_(aggression){
00183 }
00184 
00185 
00186 void attack_result::do_check_before()
00187 {
00188     LOG_AI_ACTIONS << " check_before " << *this << std::endl;
00189     const unit_map::const_iterator attacker = resources::units->find(attacker_loc_);
00190     const unit_map::const_iterator defender = resources::units->find(defender_loc_);
00191 
00192     if(attacker==resources::units->end())
00193     {
00194         LOG_AI_ACTIONS << "attempt to attack without attacker\n";
00195         set_error(E_EMPTY_ATTACKER);
00196         return;
00197     }
00198 
00199     if (defender==resources::units->end())
00200     {
00201         LOG_AI_ACTIONS << "attempt to attack without defender\n";
00202         set_error(E_EMPTY_DEFENDER);
00203         return;
00204     }
00205 
00206     if(attacker->incapacitated()) {
00207         LOG_AI_ACTIONS << "attempt to attack with unit that is petrified\n";
00208         set_error(E_INCAPACITATED_ATTACKER);
00209         return;
00210     }
00211 
00212     if(defender->incapacitated()) {
00213         LOG_AI_ACTIONS << "attempt to attack unit that is petrified\n";
00214         set_error(E_INCAPACITATED_DEFENDER);
00215         return;
00216     }
00217 
00218     if(!attacker->attacks_left()) {
00219         LOG_AI_ACTIONS << "attempt to attack with no attacks left\n";
00220         set_error(E_NO_ATTACKS_LEFT);
00221         return;
00222     }
00223 
00224     if(attacker->side()!=get_side()) {
00225         LOG_AI_ACTIONS << "attempt to attack with not own unit\n";
00226         set_error(E_NOT_OWN_ATTACKER);
00227         return;
00228     }
00229 
00230     if(!get_my_team().is_enemy(defender->side())) {
00231         LOG_AI_ACTIONS << "attempt to attack unit that is not enemy\n";
00232         set_error(E_NOT_ENEMY_DEFENDER);
00233         return;
00234     }
00235 
00236     if (attacker_weapon_!=-1) {
00237         if ((attacker_weapon_<0)||(attacker_weapon_ >= static_cast<int>(attacker->attacks().size()))) {
00238             LOG_AI_ACTIONS << "invalid weapon selection for the attacker\n";
00239             set_error(E_WRONG_ATTACKER_WEAPON);
00240             return;
00241         }
00242     }
00243 
00244     if (!tiles_adjacent(attacker_loc_,defender_loc_)) {
00245         LOG_AI_ACTIONS << "attacker and defender not adjacent\n";
00246         set_error(E_ATTACKER_AND_DEFENDER_NOT_ADJACENT);
00247         return;
00248     }
00249 }
00250 
00251 
00252 void attack_result::do_check_after()
00253 {
00254 }
00255 
00256 
00257 std::string attack_result::do_describe() const
00258 {
00259     std::stringstream s;
00260     s << "attack by side ";
00261     s << get_side();
00262     s << " from location "<<attacker_loc_;
00263     s << " to location "<<defender_loc_;
00264     s << " using weapon "<< attacker_weapon_;
00265     s << " with aggression "<< aggression_;
00266     s <<std::endl;
00267     return s.str();
00268 }
00269 
00270 
00271 void attack_result::do_execute()
00272 {
00273     LOG_AI_ACTIONS << "start of execution of: "<< *this << std::endl;
00274     // Stop the user from issuing any commands while the unit is attacking
00275     const events::command_disabler disable_commands;
00276     //@note: yes, this is a decision done here. It's that way because we want to allow a simpler attack 'with whatever weapon is considered best', and because we want to allow the defender to pick it's weapon. That's why aggression is needed. a cleaner solution is needed.
00277     battle_context bc(*resources::units, attacker_loc_,
00278         defender_loc_, attacker_weapon_, -1, aggression_);
00279 
00280     int attacker_weapon = bc.get_attacker_stats().attack_num;
00281     int defender_weapon = bc.get_defender_stats().attack_num;
00282 
00283     if(attacker_weapon < 0) {
00284         set_error(E_UNABLE_TO_CHOOSE_ATTACKER_WEAPON);
00285         return;
00286     }
00287 
00288     const unit_map::const_iterator a_ = resources::units->find(attacker_loc_);
00289     const unit_map::const_iterator d_ = resources::units->find(defender_loc_);
00290 
00291     ///@todo 1.9: change ToD to be location specific for the defender unit
00292     recorder.add_attack(attacker_loc_, defender_loc_, attacker_weapon, defender_weapon, a_->type_id(),
00293         d_->type_id(), a_->level(), d_->level(), resources::tod_manager->turn(),
00294         resources::tod_manager->get_time_of_day());
00295     rand_rng::invalidate_seed();
00296     rand_rng::clear_new_seed_callback();
00297     while (!rand_rng::has_valid_seed()) {
00298         manager::raise_user_interact();
00299         manager::raise_sync_network();
00300         SDL_Delay(10);
00301     }
00302     recorder.add_seed("attack", rand_rng::get_last_seed());
00303     attack_unit(attacker_loc_, defender_loc_, attacker_weapon, defender_weapon);
00304     dialogs::advance_unit(attacker_loc_, true);
00305 
00306     const unit_map::const_iterator defender = resources::units->find(defender_loc_);
00307     if(defender != resources::units->end()) {
00308         size_t defender_team = defender->side() - 1;
00309         if(defender_team < resources::teams->size()) {
00310             dialogs::advance_unit(defender_loc_ , !(*resources::teams)[defender_team].is_human());
00311         }
00312     }
00313 
00314     set_gamestate_changed();
00315     //start of ugly hack. @todo 1.9 rework that via extended event system
00316     //until event system is reworked, we note the attack this way
00317     get_info().recent_attacks.insert(defender_loc_);
00318     //end of ugly hack
00319     try {
00320         manager::raise_gamestate_changed();
00321     } catch (...) {
00322         is_ok(); //Silences "unchecked result" warning
00323         throw;
00324     }
00325 }
00326 
00327 
00328 void attack_result::do_init_for_execution()
00329 {
00330 }
00331 
00332 
00333 
00334 
00335 // move_result
00336 move_result::move_result(side_number side, const map_location& from,
00337              const map_location& to, bool remove_movement, bool unreach_is_ok)
00338     : action_result(side)
00339     , from_(from)
00340     , move_spectator_(*resources::units)
00341     , to_(to)
00342     , remove_movement_(remove_movement)
00343     , route_()
00344     , unit_location_(from)
00345     , unreach_is_ok_(unreach_is_ok)
00346 {
00347 }
00348 
00349 
00350 const unit *move_result::get_unit()
00351 {
00352     unit_map::const_iterator un = resources::units->find(from_);
00353     if (un==resources::units->end()){
00354         set_error(E_NO_UNIT);
00355         return NULL;
00356     }
00357     const unit *u = &*un;
00358     if (u->side() != get_side()) {
00359         set_error(E_NOT_OWN_UNIT);
00360         return NULL;
00361     }
00362     if (u->incapacitated()) {
00363         set_error(E_INCAPACITATED_UNIT);
00364         return NULL;
00365     }
00366     return u;
00367 }
00368 
00369 
00370 bool move_result::test_route(const unit &un)
00371 {
00372     if (from_== to_) {
00373         if (!remove_movement_ || (un.movement_left() == 0) ) {
00374             set_error(E_EMPTY_MOVE);
00375             return false;
00376         }
00377         return true;
00378     }
00379 
00380     if (un.movement_left() == 0 ) {
00381         set_error(E_EMPTY_MOVE);
00382         return false;
00383     }
00384 
00385     if (!to_.valid()) {
00386         set_error(E_NO_ROUTE);
00387         return false;
00388     }
00389 
00390     team &my_team = get_my_team();
00391     const pathfind::shortest_path_calculator calc(un, my_team, *resources::units, *resources::teams, *resources::game_map);
00392 
00393     //allowed teleports
00394     pathfind::teleport_map allowed_teleports = pathfind::get_teleport_locations(un, my_team, true);///@todo 1.9: see_all -> false
00395 
00396     //do an A*-search
00397     route_ = boost::shared_ptr<pathfind::plain_route>( new pathfind::plain_route(pathfind::a_star_search(un.get_location(), to_, 10000.0, &calc, resources::game_map->w(), resources::game_map->h(), &allowed_teleports)));
00398     if (route_->steps.empty()) {
00399         set_error(E_NO_ROUTE);
00400         return false;
00401     }
00402     return true;
00403 }
00404 
00405 void move_result::do_check_before()
00406 {
00407     LOG_AI_ACTIONS << " check_before " << *this << std::endl;
00408     const unit *u = get_unit();
00409     if (!u) {
00410         return;
00411     }
00412     if (!test_route(*u)) {
00413         return;
00414     }
00415 }
00416 
00417 
00418 const map_location& move_result::get_unit_location() const
00419 {
00420     return unit_location_;
00421 }
00422 
00423 
00424 void move_result::do_check_after()
00425 {
00426     if (move_spectator_.get_ambusher().valid()) {
00427         set_error(E_AMBUSHED,false);
00428         return;
00429     }
00430     if (move_spectator_.get_failed_teleport().valid()) {
00431         set_error(E_FAILED_TELEPORT);
00432         return;
00433     }
00434     ///@todo 1.9 add 'new units spotted' failure mode
00435 
00436     if (!unreach_is_ok_ && unit_location_!=to_) {
00437         set_error(E_NOT_REACHED_DESTINATION);
00438         return;
00439     }
00440 }
00441 
00442 
00443 std::string move_result::do_describe() const
00444 {
00445     std::stringstream s;
00446     if (remove_movement_){
00447         s << "full move by side ";
00448     } else {
00449         s << "partial move by side ";
00450     }
00451     s << get_side();
00452     s << " from location "<<from_;
00453     s << " to location "<<to_;
00454     s <<std::endl;
00455     return s.str();
00456 }
00457 
00458 
00459 void move_result::do_execute()
00460 {
00461     LOG_AI_ACTIONS << "start of execution of: "<< *this << std::endl;
00462     assert(is_success());
00463 
00464     move_spectator_.set_unit(resources::units->find(from_));
00465 
00466     if (from_ != to_) {
00467         move_unit(
00468             /*move_unit_spectator* move_spectator*/ &move_spectator_,
00469             /*std::vector<map_location> route*/ route_->steps,
00470             /*replay* move_recorder*/ &recorder,
00471             /*undo_list* undo_stack*/ NULL,
00472             /*bool show_move*/ preferences::show_ai_moves(),
00473             /*map_location *next_unit*/ NULL,
00474             /*bool continue_move*/ true, ///@todo 1.9 set to false after implemeting interrupt awareness
00475             /*bool should_clear_shroud*/ true,
00476             /*bool is_replay*/ false);
00477 
00478         if ( move_spectator_.get_ambusher().valid() || !move_spectator_.get_seen_enemies().empty() || !move_spectator_.get_seen_friends().empty() ) {
00479             set_gamestate_changed();
00480         } else if (move_spectator_.get_unit().valid()){
00481             unit_location_ = move_spectator_.get_unit()->get_location();
00482             if (unit_location_ != from_) {
00483                 set_gamestate_changed();
00484             }
00485         }
00486     } else {
00487         assert(remove_movement_);
00488     }
00489 
00490     if (move_spectator_.get_unit().valid()){
00491         unit_location_ = move_spectator_.get_unit()->get_location();
00492         if (remove_movement_ && move_spectator_.get_unit()->movement_left() > 0 && unit_location_ == to_)
00493         {
00494             stopunit_result_ptr stopunit_res = actions::execute_stopunit_action(get_side(),true,unit_location_,true,false);
00495             if (!stopunit_res->is_ok()) {
00496                 set_error(stopunit_res->get_status());
00497             }
00498             if (stopunit_res->is_gamestate_changed()) {
00499                 set_gamestate_changed();
00500             }
00501         }
00502     } else {
00503         unit_location_ = map_location();
00504     }
00505 
00506     if (is_gamestate_changed()) {
00507         try {
00508             manager::raise_gamestate_changed();
00509         } catch (...) {
00510             is_ok(); //Silences "unchecked result" warning
00511             throw;
00512         }
00513     }
00514 }
00515 
00516 
00517 void move_result::do_init_for_execution()
00518 {
00519     move_spectator_.reset(*resources::units);
00520 }
00521 
00522 
00523 
00524 // recall_result
00525 recall_result::recall_result(side_number side,
00526         const std::string& unit_id, const map_location& where, const map_location& from)
00527     : action_result(side)
00528     , unit_id_(unit_id)
00529     , where_(where)
00530     , recall_location_(where)
00531     , recall_from_(from)
00532 {
00533 }
00534 
00535 bool recall_result::test_available_for_recalling(const team &my_team)
00536 {
00537     const std::vector<unit>::const_iterator rec = find_if_matches_id(my_team.recall_list(), unit_id_);
00538     if (rec == my_team.recall_list().end()) {
00539         set_error(E_NOT_AVAILABLE_FOR_RECALLING);
00540         return false;
00541     }
00542     return true;
00543 }
00544 
00545 
00546 bool recall_result::test_enough_gold(const team &my_team)
00547 {
00548     if (my_team.gold() < my_team.recall_cost() ) {
00549         set_error(E_NO_GOLD);
00550         return false;
00551     }
00552     return true;
00553 }
00554 
00555 const unit *recall_result::get_leader()
00556 {
00557     unit_map::const_iterator my_leader = resources::units->find_leader(get_side());
00558     if (my_leader == resources::units->end()){
00559         set_error(E_NO_LEADER);
00560         return NULL;
00561     }
00562     return &*my_leader;
00563 
00564 }
00565 
00566 bool recall_result::test_leader_on_keep(const unit &my_leader)
00567 {
00568     if (!resources::game_map->is_keep(my_leader.get_location())) {
00569         set_error(E_LEADER_NOT_ON_KEEP);
00570         return false;
00571     }
00572     return true;
00573 }
00574 
00575 bool recall_result::test_suitable_recall_location(const unit &my_leader)
00576 {
00577     recall_location_ = where_;
00578 
00579     //if we have not-on-board location, such as null_location, then the caller wants us to recall on 'any' possible tile.
00580     if (!resources::game_map->on_board(recall_location_)) {
00581         recall_location_ = pathfind::find_vacant_tile(*resources::game_map, *resources::units, my_leader.get_location(), pathfind::VACANT_CASTLE);
00582     }
00583 
00584     if (!can_recruit_on(*resources::game_map, my_leader.get_location(), recall_location_)) {
00585         set_error(E_BAD_RECALL_LOCATION);
00586         return false;
00587     }
00588     return true;
00589 }
00590 
00591 void recall_result::do_check_before()
00592 {
00593     LOG_AI_ACTIONS << " check_before " << *this << std::endl;
00594     const team& my_team = get_my_team();
00595 
00596     //Unit available for recalling?
00597     if ( !test_available_for_recalling(my_team)) {
00598         return;
00599     }
00600 
00601     //Enough gold?
00602     if (!test_enough_gold(my_team)) {
00603         return;
00604     }
00605 
00606     //Leader present?
00607     const unit *my_leader = get_leader();
00608 
00609     if (!my_leader) {
00610         return;
00611     }
00612 
00613     //Leader on keep?
00614     if (!test_leader_on_keep(*my_leader)) {
00615         return;
00616     }
00617 
00618     //Try to get suitable recall location. Is suitable location available ?
00619     if (!test_suitable_recall_location(*my_leader)) {
00620         return;
00621     }
00622 
00623 }
00624 
00625 
00626 void recall_result::do_check_after()
00627 {
00628     if (!resources::game_map->on_board(recall_location_)){
00629         set_error(AI_ACTION_FAILURE);
00630         return;
00631     }
00632 
00633     unit_map::const_iterator unit = resources::units->find(recall_location_);
00634     if (unit==resources::units->end()){
00635         set_error(AI_ACTION_FAILURE);
00636         return;
00637     }
00638     if (unit->side() != get_side()){
00639         set_error(AI_ACTION_FAILURE);
00640         return;
00641     }
00642 
00643 }
00644 
00645 std::string recall_result::do_describe() const
00646 {
00647     std::stringstream s;
00648     s << "recall by side ";
00649     s << get_side();
00650     s << " of unit id ["<<unit_id_;
00651     if (where_ != map_location::null_location){
00652         s << "] on location "<<where_;
00653     } else {
00654         s << "] on any suitable location";
00655     }
00656     s <<std::endl;
00657     return s.str();
00658 }
00659 
00660 
00661 void recall_result::do_execute()
00662 {
00663     LOG_AI_ACTIONS << "start of execution of: " << *this << std::endl;
00664     assert(is_success());
00665 
00666     team& my_team = get_my_team();
00667 
00668     const events::command_disabler disable_commands;
00669 
00670     std::vector<unit>::iterator rec = find_if_matches_id(my_team.recall_list(), unit_id_);
00671 
00672     assert(rec != my_team.recall_list().end());
00673 
00674     const std::string &err = find_recall_location(get_side(), recall_location_, recall_from_, *rec);
00675     if(!err.empty()) {
00676         set_error(AI_ACTION_FAILURE);
00677         return;
00678     } else {
00679 
00680         unit &un = *rec;
00681         recorder.add_recall(un.id(), recall_location_, recall_from_);
00682         place_recruit(un, recall_location_, recall_from_, true, true);
00683         statistics::recall_unit(un);
00684         my_team.spend_gold(my_team.recall_cost());
00685 
00686         my_team.recall_list().erase(rec);
00687         if (resources::screen!=NULL) {
00688             resources::screen->invalidate_game_status();
00689             resources::screen->invalidate_all();
00690         }
00691         recorder.add_checksum_check(recall_location_);
00692         set_gamestate_changed();
00693         try {
00694             manager::raise_gamestate_changed();
00695         } catch (...) {
00696             is_ok(); //Silences "unchecked result" warning
00697             throw;
00698         }
00699     }
00700 
00701 }
00702 
00703 
00704 void recall_result::do_init_for_execution()
00705 {
00706 }
00707 
00708 
00709 
00710 
00711 // recruit_result
00712 recruit_result::recruit_result(side_number side,
00713         const std::string& unit_name, const map_location& where, const map_location& from)
00714     : action_result(side)
00715     , unit_name_(unit_name)
00716     , where_(where)
00717     , recruit_location_(where)
00718     , recruit_from_(from)
00719     , num_(0)
00720 {
00721 }
00722 
00723 const std::string &recruit_result::get_available_for_recruiting(const team &my_team)
00724 {
00725     const std::set<std::string> &recruit_set = my_team.recruits();
00726     std::set<std::string>::const_iterator recruit = recruit_set.find(unit_name_);
00727     if (recruit == recruit_set.end()) {
00728         set_error(E_NOT_AVAILABLE_FOR_RECRUITING);
00729         static std::string dummy;
00730         return dummy;
00731     }
00732     num_ = std::distance(recruit_set.begin(),recruit);
00733     return *recruit;
00734 }
00735 
00736 const unit_type *recruit_result::get_unit_type_known(const std::string &recruit)
00737 {
00738     const unit_type *type = unit_types.find(recruit);
00739     if (!type) {
00740         set_error(E_UNKNOWN_OR_DUMMY_UNIT_TYPE);
00741         return NULL;
00742     }
00743     return type;
00744 }
00745 
00746 bool recruit_result::test_enough_gold(const team &my_team, const unit_type &type)
00747 {
00748     if (my_team.gold() < type.cost()) {
00749         set_error(E_NO_GOLD);
00750         return false;
00751     }
00752     return true;
00753 }
00754 
00755 const unit *recruit_result::get_leader()
00756 {
00757     unit_map::const_iterator my_leader = resources::units->find_leader(get_side());
00758     if (my_leader == resources::units->end()){
00759         set_error(E_NO_LEADER);
00760         return NULL;
00761     }
00762     return &*my_leader;
00763 
00764 }
00765 
00766 bool recruit_result::test_leader_on_keep(const unit &my_leader)
00767 {
00768     if (!resources::game_map->is_keep(my_leader.get_location())) {
00769         set_error(E_LEADER_NOT_ON_KEEP);
00770         return false;
00771     }
00772     return true;
00773 }
00774 
00775 bool recruit_result::test_suitable_recruit_location(const unit &my_leader)
00776 {
00777     recruit_location_ = where_;
00778 
00779     //if we have not-on-board location, such as null_location, then the caller wants us to recruit on 'any' possible tile.
00780     if (!resources::game_map->on_board(recruit_location_)) {
00781         recruit_location_ = pathfind::find_vacant_tile(*resources::game_map, *resources::units, my_leader.get_location(), pathfind::VACANT_CASTLE);
00782     }
00783 
00784     if (!can_recruit_on(*resources::game_map, my_leader.get_location(), recruit_location_)) {
00785         set_error(E_BAD_RECRUIT_LOCATION);
00786         return false;
00787     }
00788     return true;
00789 }
00790 
00791 void recruit_result::do_check_before()
00792 {
00793     LOG_AI_ACTIONS << " check_before " << *this << std::endl;
00794 
00795     const team& my_team = get_my_team();
00796 
00797     //Unit available for recruiting?
00798     const std::string &s_recruit = get_available_for_recruiting(my_team);
00799 
00800     if (s_recruit.empty()) {
00801         return;
00802     }
00803 
00804     //Unit type known ?
00805     const unit_type *s_type = get_unit_type_known(s_recruit);
00806     if (!s_type) {
00807         return;
00808     }
00809 
00810     //Enough gold?
00811     if (!test_enough_gold(my_team, *s_type)) {
00812         return;
00813     }
00814 
00815     //Leader present?
00816     const unit *my_leader = get_leader();
00817 
00818     if (!my_leader ) {
00819         return;
00820     }
00821 
00822     //Leader on keep?
00823 
00824     if (!test_leader_on_keep(*my_leader)) {
00825         return;
00826     }
00827 
00828     //Try to get suitable recruit location. Is suitable location available ?
00829     if (!test_suitable_recruit_location(*my_leader)) {
00830         return;
00831     }
00832 
00833 }
00834 
00835 
00836 void recruit_result::do_check_after()
00837 {
00838     if (!resources::game_map->on_board(recruit_location_)) {
00839         set_error(AI_ACTION_FAILURE);
00840         return;
00841     }
00842 
00843     unit_map::const_iterator unit = resources::units->find(recruit_location_);
00844     if (unit==resources::units->end()) {
00845         set_error(AI_ACTION_FAILURE);
00846         return;
00847     }
00848     if (unit->side() != get_side()) {
00849         set_error(AI_ACTION_FAILURE);
00850         return;
00851     }
00852 
00853 }
00854 
00855 std::string recruit_result::do_describe() const
00856 {
00857     std::stringstream s;
00858     s << "recruitment by side ";
00859     s << get_side();
00860     s << " of unit type ["<<unit_name_;
00861     if (where_ != map_location::null_location){
00862         s << "] on location "<<where_;
00863     } else {
00864         s << "] on any suitable location";
00865     }
00866     s <<std::endl;
00867     return s.str();
00868 }
00869 
00870 
00871 void recruit_result::do_execute()
00872 {
00873     LOG_AI_ACTIONS << "start of execution of: " << *this << std::endl;
00874     assert(is_success());
00875 
00876     // We have to add the recruit command now, because when the unit
00877     // is created it has to have the recruit command in the recorder
00878     // to be able to put random numbers into to generate unit traits.
00879     // However, we're not sure if the transaction will be successful,
00880     // so use a replay_undo object to cancel it if we don't get
00881     // a confirmation for the transaction.
00882     recorder.add_recruit(num_,recruit_location_,recruit_from_);
00883     replay_undo replay_guard(recorder);
00884     const unit_type *u = unit_types.find(unit_name_);
00885     const events::command_disabler disable_commands;
00886     const std::string recruit_err = find_recruit_location(get_side(), recruit_location_, recruit_from_, u->id());
00887     if(recruit_err.empty()) {
00888         const unit new_unit(u, get_side(), true);
00889         place_recruit(new_unit, recruit_location_, recruit_from_, false, preferences::show_ai_moves());
00890         statistics::recruit_unit(new_unit);
00891         get_my_team().spend_gold(u->cost());
00892         // Confirm the transaction - i.e. don't undo recruitment
00893         replay_guard.confirm_transaction();
00894         set_gamestate_changed();
00895         try {
00896             manager::raise_gamestate_changed();
00897         } catch (...) {
00898             is_ok(); //Silences "unchecked result" warning
00899             throw;
00900         }
00901     } else {
00902         set_error(AI_ACTION_FAILURE);
00903     }
00904 
00905 }
00906 
00907 
00908 void recruit_result::do_init_for_execution()
00909 {
00910 }
00911 
00912 
00913 
00914 
00915 
00916 // stopunit_result
00917 stopunit_result::stopunit_result( side_number side, const map_location& unit_location, bool remove_movement, bool remove_attacks)
00918     : action_result(side), unit_location_(unit_location), remove_movement_(remove_movement), remove_attacks_(remove_attacks)
00919 {
00920 }
00921 
00922 const unit *stopunit_result::get_unit()
00923 {
00924     unit_map::const_iterator un = resources::units->find(unit_location_);
00925     if (un==resources::units->end()){
00926         set_error(E_NO_UNIT);
00927         return NULL;
00928     }
00929     const unit *u = &*un;
00930     if (u->side() != get_side()) {
00931         set_error(E_NOT_OWN_UNIT);
00932         return NULL;
00933     }
00934     if (u->incapacitated()) {
00935         set_error(E_INCAPACITATED_UNIT);
00936         return NULL;
00937     }
00938     return u;
00939 }
00940 
00941 void stopunit_result::do_check_before()
00942 {
00943     LOG_AI_ACTIONS << " check_before " << *this << std::endl;
00944 
00945     if (!get_unit()) {
00946         return;
00947     }
00948 
00949 }
00950 
00951 
00952 void stopunit_result::do_check_after()
00953 {
00954     unit_map::const_iterator un = resources::units->find(unit_location_);
00955     if (un==resources::units->end()){
00956         set_error(AI_ACTION_FAILURE);
00957         return;
00958     }
00959     if (remove_movement_ && un->movement_left() != 0) {
00960         set_error(AI_ACTION_FAILURE);
00961         return;
00962     }
00963     if (remove_attacks_ && un->attacks_left() != 0) {
00964         set_error(AI_ACTION_FAILURE);
00965         return;
00966     }
00967 }
00968 
00969 std::string stopunit_result::do_describe() const
00970 {
00971     std::stringstream s;
00972     s <<" stopunit by side ";
00973     s << get_side();
00974     if (remove_movement_){
00975         s << " : remove movement ";
00976     }
00977     if (remove_movement_ && remove_attacks_){
00978         s << "and ";
00979     }
00980     if (remove_attacks_){
00981         s << " remove attacks ";
00982     }
00983     s << "from unit on location "<<unit_location_;
00984     s <<std::endl;
00985     return s.str();
00986 }
00987 
00988 void stopunit_result::do_execute()
00989 {
00990     LOG_AI_ACTIONS << "start of execution of: " << *this << std::endl;
00991     assert(is_success());
00992     unit_map::iterator un = resources::units->find(unit_location_);
00993     try {
00994         if (remove_movement_){
00995             un->remove_movement_ai();
00996             set_gamestate_changed();
00997             manager::raise_gamestate_changed();
00998         }
00999         if (remove_attacks_){
01000             un->remove_attacks_ai();
01001             set_gamestate_changed();
01002             manager::raise_gamestate_changed();//to be on the safe side
01003         }
01004     } catch (...) {
01005         is_ok(); //Silences "unchecked result" warning
01006         throw;
01007     }
01008 }
01009 
01010 
01011 void stopunit_result::do_init_for_execution()
01012 {
01013 }
01014 
01015 
01016 
01017 
01018 // =======================================================================
01019 // STATELESS INTERFACE TO AI ACTIONS
01020 // =======================================================================
01021 
01022 attack_result_ptr actions::execute_attack_action( side_number side,
01023     bool execute,
01024     const map_location& attacker_loc,
01025     const map_location& defender_loc,
01026     int attacker_weapon,
01027     double aggression)
01028 {
01029     attack_result_ptr action(new attack_result(side,attacker_loc,defender_loc,attacker_weapon,aggression));
01030     execute ? action->execute() : action->check_before();
01031     return action;
01032 }
01033 
01034 
01035 
01036 move_result_ptr actions::execute_move_action( side_number side,
01037     bool execute,
01038     const map_location& from,
01039     const map_location& to,
01040     bool remove_movement,
01041     bool unreach_is_ok)
01042 {
01043     move_result_ptr action(new move_result(side,from,to,remove_movement,unreach_is_ok));
01044     execute ? action->execute() : action->check_before();
01045     return action;
01046 
01047 }
01048 
01049 
01050 recall_result_ptr actions::execute_recall_action( side_number side,
01051     bool execute,
01052     const std::string& unit_id,
01053     const map_location& where,
01054     const map_location& from)
01055 {
01056     recall_result_ptr action(new recall_result(side,unit_id,where,from));
01057     execute ? action->execute() : action->check_before();
01058     return action;
01059 
01060 }
01061 
01062 
01063 recruit_result_ptr actions::execute_recruit_action( side_number side,
01064     bool execute,
01065     const std::string& unit_name,
01066     const map_location& where,
01067     const map_location& from)
01068 {
01069     recruit_result_ptr action(new recruit_result(side,unit_name,where,from));
01070     execute ? action->execute() : action->check_before();
01071     return action;
01072 
01073 }
01074 
01075 
01076 stopunit_result_ptr actions::execute_stopunit_action( side_number side,
01077     bool execute,
01078     const map_location& unit_location,
01079     bool remove_movement,
01080     bool remove_attacks)
01081 {
01082     stopunit_result_ptr action(new stopunit_result(side,unit_location,remove_movement,remove_attacks));
01083     execute ? action->execute() : action->check_before();
01084     return action;
01085 
01086 }
01087 
01088 
01089 const std::string& actions::get_error_name(int error_code)
01090 {
01091     if (error_names_.empty()){
01092         error_names_.insert(std::make_pair(action_result::AI_ACTION_SUCCESS,"action_result::AI_ACTION_SUCCESS"));
01093         error_names_.insert(std::make_pair(action_result::AI_ACTION_STARTED,"action_result::AI_ACTION_STARTED"));
01094         error_names_.insert(std::make_pair(action_result::AI_ACTION_FAILURE,"action_result::AI_ACTION_FAILURE"));
01095 
01096         error_names_.insert(std::make_pair(attack_result::E_EMPTY_ATTACKER,"attack_result::E_EMPTY_ATTACKER"));
01097         error_names_.insert(std::make_pair(attack_result::E_EMPTY_DEFENDER,"attack_result::E_EMPTY_DEFENDER"));
01098         error_names_.insert(std::make_pair(attack_result::E_INCAPACITATED_ATTACKER,"attack_result::E_INCAPACITATED_ATTACKER"));
01099         error_names_.insert(std::make_pair(attack_result::E_INCAPACITATED_DEFENDER,"attack_result::E_INCAPACITATED_DEFENDER"));
01100         error_names_.insert(std::make_pair(attack_result::E_NOT_OWN_ATTACKER,"attack_result::E_NOT_OWN_ATTACKER"));
01101         error_names_.insert(std::make_pair(attack_result::E_NOT_ENEMY_DEFENDER,"attack_result::E_NOT_ENEMY_DEFENDER"));
01102         error_names_.insert(std::make_pair(attack_result::E_NO_ATTACKS_LEFT,"attack_result::E_NO_ATTACKS_LEFT"));
01103         error_names_.insert(std::make_pair(attack_result::E_WRONG_ATTACKER_WEAPON,"attack_result::E_WRONG_ATTACKER_WEAPON"));
01104         error_names_.insert(std::make_pair(attack_result::E_UNABLE_TO_CHOOSE_ATTACKER_WEAPON,"attack_result::E_UNABLE_TO_CHOOSE_ATTACKER_WEAPON"));
01105         error_names_.insert(std::make_pair(attack_result::E_ATTACKER_AND_DEFENDER_NOT_ADJACENT,"attack_result::E_ATTACKER_AND_DEFENDER_NOT_ADJACENT"));
01106 
01107         error_names_.insert(std::make_pair(move_result::E_EMPTY_MOVE,"move_result::E_EMPTY_MOVE"));
01108         error_names_.insert(std::make_pair(move_result::E_NO_UNIT,"move_result::E_NO_UNIT"));
01109         error_names_.insert(std::make_pair(move_result::E_NOT_OWN_UNIT,"move_result::E_NOT_OWN_UNIT"));
01110         error_names_.insert(std::make_pair(move_result::E_INCAPACITATED_UNIT,"move_result::E_INCAPACITATED_UNIT"));
01111         error_names_.insert(std::make_pair(move_result::E_AMBUSHED,"E_AMBUSHED"));
01112         error_names_.insert(std::make_pair(move_result::E_FAILED_TELEPORT,"E_FAILED_TELEPORT"));
01113         error_names_.insert(std::make_pair(move_result::E_NOT_REACHED_DESTINATION,"E_NOT_REACHED_DESTINATION"));
01114         error_names_.insert(std::make_pair(move_result::E_NO_ROUTE,"E_NO_ROUTE"));
01115 
01116         error_names_.insert(std::make_pair(recall_result::E_NOT_AVAILABLE_FOR_RECALLING,"recall_result::E_NOT_AVAILABLE_FOR_RECALLING"));
01117         error_names_.insert(std::make_pair(recall_result::E_NO_GOLD,"recall_result::E_NO_GOLD"));
01118         error_names_.insert(std::make_pair(recall_result::E_NO_LEADER,"recall_result::E_NO_LEADER"));
01119         error_names_.insert(std::make_pair(recall_result::E_LEADER_NOT_ON_KEEP,"recall_result::E_LEADER_NOT_ON_KEEP"));
01120         error_names_.insert(std::make_pair(recall_result::E_BAD_RECALL_LOCATION,"recall_result::E_BAD_RECALL_LOCATION"));
01121 
01122         error_names_.insert(std::make_pair(recruit_result::E_NOT_AVAILABLE_FOR_RECRUITING,"recruit_result::E_NOT_AVAILABLE_FOR_RECRUITING"));
01123         error_names_.insert(std::make_pair(recruit_result::E_UNKNOWN_OR_DUMMY_UNIT_TYPE,"recruit_result::E_UNKNOWN_OR_DUMMY_UNIT_TYPE"));
01124         error_names_.insert(std::make_pair(recruit_result::E_NO_GOLD,"recruit_result::E_NO_GOLD"));
01125         error_names_.insert(std::make_pair(recruit_result::E_NO_LEADER,"recruit_result::E_NO_LEADER"));
01126         error_names_.insert(std::make_pair(recruit_result::E_LEADER_NOT_ON_KEEP,"recruit_result::E_LEADER_NOT_ON_KEEP"));
01127         error_names_.insert(std::make_pair(recruit_result::E_BAD_RECRUIT_LOCATION,"recruit_result::E_BAD_RECRUIT_LOCATION"));
01128 
01129         error_names_.insert(std::make_pair(stopunit_result::E_NO_UNIT,"stopunit_result::E_NO_UNIT"));
01130         error_names_.insert(std::make_pair(stopunit_result::E_NOT_OWN_UNIT,"stopunit_result::E_NOT_OWN_UNIT"));
01131         error_names_.insert(std::make_pair(stopunit_result::E_INCAPACITATED_UNIT,"stopunit_result::E_INCAPACITATED_UNIT"));
01132     }
01133     std::map<int,std::string>::iterator i = error_names_.find(error_code);
01134     if (i==error_names_.end()){
01135         ERR_AI_ACTIONS << "error name not available for error #"<<error_code << std::endl;
01136         i = error_names_.find(-1);
01137         assert(i != error_names_.end());
01138     }
01139     return i->second;
01140 }
01141 
01142 
01143 std::map<int,std::string> actions::error_names_;
01144 
01145 } //end of namespace ai
01146 
01147 
01148 std::ostream &operator<<(std::ostream &s, ai::attack_result const &r) {
01149         s << r.do_describe();
01150         return s;
01151 }
01152 
01153 
01154 std::ostream &operator<<(std::ostream &s, ai::move_result const &r) {
01155         s << r.do_describe();
01156         return s;
01157 }
01158 
01159 
01160 std::ostream &operator<<(std::ostream &s, ai::recall_result const &r) {
01161         s << r.do_describe();
01162         return s;
01163 }
01164 
01165 
01166 std::ostream &operator<<(std::ostream &s, ai::recruit_result const &r) {
01167         s << r.do_describe();
01168         return s;
01169 }
01170 
01171 
01172 std::ostream &operator<<(std::ostream &s, ai::stopunit_result const &r) {
01173         s << r.do_describe();
01174         return s;
01175 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Tue May 22 2012 01:03:34 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs