The Battle for Wesnoth  1.13.10+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
actions.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2017 by Yurii Chernyi <terraninfo@terraninfo.net>
3  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 /**
16  * Managing the AI-Game interaction - AI actions and their results
17  * @file
18  */
19 
20 /**
21  * A small explanation about what's going on here:
22  * Each action has access to two game_info objects
23  * First is 'info' - real information
24  * Second is 'subjective info' - AIs perception of what's going on
25  * So, when we check_before action, we use 'subjective info' and don't
26  * touch real 'info' at all.
27  * But when we actually want to execute an action, we firstly check
28  * 'subjective info' and then (if subjective check is ok) do the same
29  * check on real 'info'. There's a caveat: if we fail an action based
30  * on real 'info', then we NEED to update AIs knowledge to avoid the ai
31  * doing the same thing again.
32  * So far the use of 'subjective info' is stubbed out.
33  */
34 
35 #include "ai/actions.hpp"
36 #include "ai/manager.hpp"
37 #include "ai/simulated_actions.hpp"
38 
39 #include "actions/attack.hpp"
40 #include "actions/create.hpp"
41 #include "attack_prediction.hpp"
42 #include "preferences/game.hpp"
43 #include "log.hpp"
44 #include "map/map.hpp"
45 #include "mouse_handler_base.hpp"
46 #include "pathfind/teleport.hpp"
47 #include "play_controller.hpp"
49 #include "recall_list_manager.hpp"
50 #include "replay_helper.hpp"
51 #include "resources.hpp"
52 #include "synced_context.hpp"
53 #include "team.hpp"
54 #include "units/unit.hpp"
55 #include "units/ptr.hpp"
56 #include "whiteboard/manager.hpp"
57 
58 namespace ai {
59 
60 static lg::log_domain log_ai_actions("ai/actions");
61 #define DBG_AI_ACTIONS LOG_STREAM(debug, log_ai_actions)
62 #define LOG_AI_ACTIONS LOG_STREAM(info, log_ai_actions)
63 #define WRN_AI_ACTIONS LOG_STREAM(warn, log_ai_actions)
64 #define ERR_AI_ACTIONS LOG_STREAM(err, log_ai_actions)
65 
66 // =======================================================================
67 // AI ACTIONS
68 // =======================================================================
70  : return_value_checked_(true),side_(side),status_(AI_ACTION_SUCCESS),is_execution_(false),is_gamestate_changed_(false)
71 {
72 }
73 
75 {
76  if (!return_value_checked_) {
77  DBG_AI_ACTIONS << "Return value of AI ACTION was not checked." << std::endl; //Demotes to DBG "unchecked result" warning
78  }
79 }
80 
82 {
84 }
85 
87 {
89 }
90 
92 {
93  is_execution_ = true;
95  check_before();
96  if (is_success()){
97  try {
98  do_execute();
99  } catch (return_to_play_side_exception&) {
100  if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked." << std::endl; } //Demotes to DBG "unchecked result" warning
101  throw;
102  }
103  }
104  if (is_success()){
105  check_after();
106  }
107  is_execution_ = false;
108 }
109 
111 {
112  return_value_checked_ = false;
113  is_gamestate_changed_ = false;
116 }
117 
119 {
120  return is_gamestate_changed_;
121 }
122 
124 {
125  return_value_checked_ = true;
126  return is_success();
127 }
128 
129 void action_result::set_error(int error_code, bool log_as_error){
130  status_ = error_code;
131  if (is_execution()) {
132  if (log_as_error) {
133  ERR_AI_ACTIONS << "Error #"<<error_code<<" ("<<actions::get_error_name(error_code)<<") in "<< do_describe();
134  } else {
135  LOG_AI_ACTIONS << "Error #"<<error_code<<" ("<<actions::get_error_name(error_code)<<") in "<< do_describe();
136  }
137  } else {
138  LOG_AI_ACTIONS << "Error #"<<error_code<<" ("<<actions::get_error_name(error_code)<<") when checking "<< do_describe();
139  }
140 }
141 
143 {
144  is_gamestate_changed_ = true;
145 }
146 
148 {
149  return status_;
150 }
151 
153 {
155 }
156 
158 {
159  return is_execution_;
160 }
161 
163 {
165 }
166 
168 {
170 }
171 
172 
173 // attack_result
174 attack_result::attack_result( side_number side, const map_location& attacker_loc, const map_location& defender_loc, int attacker_weapon, double aggression, const unit_advancements_aspect& advancements)
175  : action_result(side), attacker_loc_(attacker_loc), defender_loc_(defender_loc), attacker_weapon_(attacker_weapon), aggression_(aggression), advancements_(advancements){
176 }
177 
179 {
180  LOG_AI_ACTIONS << " check_before " << *this << std::endl;
183 
184  if(attacker==resources::gameboard->units().end())
185  {
186  LOG_AI_ACTIONS << "attempt to attack without attacker\n";
188  return;
189  }
190 
191  if (defender==resources::gameboard->units().end())
192  {
193  LOG_AI_ACTIONS << "attempt to attack without defender\n";
195  return;
196  }
197 
198  if(attacker->incapacitated()) {
199  LOG_AI_ACTIONS << "attempt to attack with unit that is petrified\n";
201  return;
202  }
203 
204  if(defender->incapacitated()) {
205  LOG_AI_ACTIONS << "attempt to attack unit that is petrified\n";
207  return;
208  }
209 
210  if(!attacker->attacks_left()) {
211  LOG_AI_ACTIONS << "attempt to attack with no attacks left\n";
213  return;
214  }
215 
216  if(attacker->side()!=get_side()) {
217  LOG_AI_ACTIONS << "attempt to attack with not own unit\n";
219  return;
220  }
221 
222  if(!get_my_team().is_enemy(defender->side())) {
223  LOG_AI_ACTIONS << "attempt to attack unit that is not enemy\n";
225  return;
226  }
227 
228  if (attacker_weapon_!=-1) {
229  if ((attacker_weapon_<0)||(attacker_weapon_ >= static_cast<int>(attacker->attacks().size()))) {
230  LOG_AI_ACTIONS << "invalid weapon selection for the attacker\n";
232  return;
233  }
234  }
235 
237  LOG_AI_ACTIONS << "attacker and defender not adjacent\n";
239  return;
240  }
241 }
242 
244 {
245 }
246 
248 {
249  std::stringstream s;
250  s << "attack by side ";
251  s << get_side();
252  s << " from location "<<attacker_loc_;
253  s << " to location "<<defender_loc_;
254  s << " using weapon "<< attacker_weapon_;
255  s << " with aggression "<< aggression_;
256  s <<std::endl;
257  return s.str();
258 }
259 
261 {
262  LOG_AI_ACTIONS << "start of execution of: "<< *this << std::endl;
263  // Stop the user from issuing any commands while the unit is attacking
264  const events::command_disabler disable_commands;
265  //@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.
268 
269  int attacker_weapon = bc.get_attacker_stats().attack_num;
270  int defender_weapon = bc.get_defender_stats().attack_num;
271 
272  if(attacker_weapon < 0) {
274  return;
275  }
276 
279 
282 
283  sim_gamestate_changed(this, gamestate_changed);
284 
285  return;
286  }
287 
288  //FIXME: find a way to 'ask' the ai which advancement should be chosen from synced_commands.cpp .
289  if(!synced_context::is_synced()) //RAII block for set_scontext_synced
290  {
291  wb::real_map rm;
292  //we don't use synced_context::run_in_synced_context because that wouldn't allow us to pass advancements_
293  resources::recorder->add_synced_command("attack", replay_helper::get_attack(attacker_loc_, defender_loc_, attacker_weapon, defender_weapon, a_->type_id(),
294  d_->type_id(), a_->level(), d_->level(), resources::tod_manager->turn(),
296  set_scontext_synced sync;
297  attack_unit_and_advance(attacker_loc_, defender_loc_, attacker_weapon, defender_weapon, true, advancements_);
300  sync.do_final_checkup();
301  }
302  else
303  {
304  attack_unit_and_advance(attacker_loc_, defender_loc_, attacker_weapon, defender_weapon, true, advancements_);
305  }
306 
307 
309  //start of ugly hack. @todo 1.9 rework that via extended event system
310  //until event system is reworked, we note the attack this way
312  //end of ugly hack
313  try {
315  } catch (...) {
316  if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked." << std::endl; } //Demotes to DBG "unchecked result" warning
317  throw;
318  }
319 }
320 
322 {
323 }
324 
325 
326 // move_result
328  const map_location& to, bool remove_movement, bool unreach_is_ok)
329  : action_result(side)
330  , from_(from)
331  , to_(to)
332  , remove_movement_(remove_movement)
333  , route_()
334  , unit_location_(from)
335  , unreach_is_ok_(unreach_is_ok)
336  , has_ambusher_(false)
337  , has_interrupted_teleport_(false)
338 {
339 }
340 
342 {
344  if (un==resources::gameboard->units().end()){
346  return nullptr;
347  }
348  const unit *u = &*un;
349  if (u->side() != get_side()) {
351  return nullptr;
352  }
353  if (u->incapacitated()) {
355  return nullptr;
356  }
357  return u;
358 }
359 
361 {
362  if (from_== to_) {
363  if (!remove_movement_ || (un.movement_left() == 0) ) {
365  return false;
366  }
367  return true;
368  }
369 
370  if (un.movement_left() == 0 ) {
372  return false;
373  }
374 
375  if (!to_.valid()) {
377  return false;
378  }
379 
380  team &my_team = get_my_team();
381  const pathfind::shortest_path_calculator calc(un, my_team, resources::gameboard->teams(), resources::gameboard->map());
382 
383  //allowed teleports
384  pathfind::teleport_map allowed_teleports = pathfind::get_teleport_locations(un, my_team, true);///@todo 1.9: see_all -> false
385 
386  //do an A*-search
387  route_.reset(new pathfind::plain_route(pathfind::a_star_search(un.get_location(), to_, 10000.0, calc, resources::gameboard->map().w(), resources::gameboard->map().h(), &allowed_teleports)));
388  if (route_->steps.empty()) {
390  return false;
391  }
392  return true;
393 }
394 
396 {
397  LOG_AI_ACTIONS << " check_before " << *this << std::endl;
398  const unit *u = get_unit();
399  if (!u) {
400  return;
401  }
402  if (!test_route(*u)) {
403  return;
404  }
405 }
406 
408 {
409  return unit_location_;
410 }
411 
413 {
414  if (has_ambusher_) {
415  set_error(E_AMBUSHED,false);
416  return;
417  }
420  return;
421  }
422  ///@todo 1.9 add 'new units spotted' failure mode
423 
424  if (!unreach_is_ok_ && unit_location_!=to_) {
426  return;
427  }
428 }
429 
431 {
432  std::stringstream s;
433  if (remove_movement_){
434  s << "full move by side ";
435  } else {
436  s << "partial move by side ";
437  }
438  s << get_side();
439  s << " from location "<<from_;
440  s << " to location "<<to_;
441  s <<std::endl;
442  return s.str();
443 }
444 
446 {
447  LOG_AI_ACTIONS << "start of execution of: "<< *this << std::endl;
448  assert(is_success());
449 
451  bool gamestate_changed = false;
452  if(from_ != to_){
453  int step = route_->steps.size();
454  gamestate_changed = simulated_move(get_side(), from_, to_, step, unit_location_);
455  } else {
456  assert(remove_movement_);
457  }
458 
460  if(remove_movement_ && un->movement_left() > 0 && unit_location_ == to_){
461  gamestate_changed = simulated_stopunit(unit_location_, true, false);
462  }
463 
464  sim_gamestate_changed(this, gamestate_changed);
465 
466  return;
467  }
468 
470  move_spectator.set_unit(resources::gameboard->units().find(from_));
471 
472  if (from_ != to_) {
473  size_t num_steps = ::actions::move_unit_and_record(
474  /*std::vector<map_location> steps*/ route_->steps,
475  /*::actions::undo_list* undo_stack*/ nullptr,
476  /*bool continue_move*/ true, ///@todo 1.9 set to false after implemeting interrupt awareness
477  /*bool show_move*/ !preferences::skip_ai_moves(),
478  /*bool* interrupted*/ nullptr,
479  /*::actions::move_unit_spectator* move_spectator*/ &move_spectator);
480 
481  if ( num_steps > 0 ) {
483  } else if ( move_spectator.get_ambusher().valid() ) {
484  // Unlikely, but some types of strange WML (or bad pathfinding)
485  // could cause an ambusher to be found without moving.
487  }
488  } else {
489  assert(remove_movement_);
490  }
491 
492  if (move_spectator.get_unit().valid()){
493  unit_location_ = move_spectator.get_unit()->get_location();
494  if (remove_movement_ && move_spectator.get_unit()->movement_left() > 0 && unit_location_ == to_)
495  {
497  if (!stopunit_res->is_ok()) {
498  set_error(stopunit_res->get_status());
499  }
500  if (stopunit_res->is_gamestate_changed()) {
502  }
503  }
504  } else {
506  }
507 
508  has_ambusher_ = move_spectator.get_ambusher().valid();
510 
511  if (is_gamestate_changed()) {
512  try {
514  } catch (...) {
515  if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked." << std::endl; } //Demotes to DBG "unchecked result" warning
516  throw;
517  }
518  }
519 }
520 
522 {
523 }
524 
525 
526 // recall_result
528  const std::string& unit_id, const map_location& where, const map_location& from)
529  : action_result(side)
530  , unit_id_(unit_id)
531  , where_(where)
532  , recall_location_(where)
533  , recall_from_(from)
534  , location_checked_(false)
535 {
536 }
537 
539 {
541  if (!rec) {
543  }
544  return rec;
545 }
546 
548 {
549  if (my_team.gold() < my_team.recall_cost() ) {
551  return false;
552  }
553  return true;
554 }
555 
557 {
558  LOG_AI_ACTIONS << " check_before " << *this << std::endl;
559  const team& my_team = get_my_team();
560  const bool location_specified = recall_location_.valid();
561 
562  //Enough gold?
563  if (!test_enough_gold(my_team)) {
564  return;
565  }
566 
567  //Unit available for recalling?
568  const unit_const_ptr & to_recall = get_recall_unit(my_team);
569  if ( !to_recall ) {
570  return;
571  }
572 
573  // Leader available for recalling?
578  return;
579 
582  return;
583 
586  return;
587 
589  if(location_specified) {
591  return;
592  }
593  FALLTHROUGH; // If the location was not specified, this counts as "OK".
595  location_checked_ = true;
596  }
597 }
598 
600 {
601  if (!resources::gameboard->map().on_board(recall_location_)){
603  return;
604  }
605 
607  if (unit==resources::gameboard->units().end()){
609  return;
610  }
611  if (unit->side() != get_side()){
613  return;
614  }
615 }
616 
618 {
619  std::stringstream s;
620  s << "recall by side ";
621  s << get_side();
622  s << " of unit id ["<<unit_id_;
624  s << "] on location "<<where_;
625  } else {
626  s << "] on any suitable location";
627  }
628  s <<std::endl;
629  return s.str();
630 }
631 
633 {
634  LOG_AI_ACTIONS << "start of execution of: " << *this << std::endl;
635  assert(is_success());
636 
637  const events::command_disabler disable_commands;
638 
639  // Assert that recall_location_ has been validated.
640  // This should be implied by is_success() once check_before() has been
641  // called, so this is a guard against future breakage.
642  assert(location_checked_);
643 
645  bool gamestate_changed = simulated_recall(get_side(), unit_id_, recall_location_);
646 
647  sim_gamestate_changed(this, gamestate_changed);
648 
649  return;
650  }
651 
652  // Do the actual recalling.
653  // We ignore possible errors (=unit doesn't exist on the recall list)
654  // because that was the previous behavior.
657  false,
660 
662  try {
664  } catch (...) {
665  if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked." << std::endl; } //Demotes to DBG "unchecked result" warning
666  throw;
667  }
668 }
669 
671 {
672 }
673 
674 
675 // recruit_result
677  const std::string& unit_name, const map_location& where, const map_location& from)
678  : action_result(side)
679  , unit_name_(unit_name)
680  , where_(where)
681  , recruit_location_(where)
682  , recruit_from_(from)
683  , location_checked_(false)
684 {
685 }
686 
688 {
689  const unit_type *type = unit_types.find(recruit);
690  if (!type) {
692  return nullptr;
693  }
694  return type;
695 }
696 
698 {
699  if (my_team.gold() < type.cost()) {
701  return false;
702  }
703  return true;
704 }
705 
707 {
708  LOG_AI_ACTIONS << " check_before " << *this << std::endl;
709  const team& my_team = get_my_team();
710  const bool location_specified = recruit_location_.valid();
711 
712  //Unit type known ?
713  const unit_type *s_type = get_unit_type_known(unit_name_);
714  if (!s_type) {
715  return;
716  }
717 
718  //Enough gold?
719  if (!test_enough_gold(my_team, *s_type)) {
720  return;
721  }
722 
723  // Leader available for recruiting?
728  return;
729 
732  return;
733 
736  return;
737 
739  if(location_specified) {
741  return;
742  }
743  FALLTHROUGH; // If the location was not specified, this counts as "OK".
745  location_checked_ = true;
746  }
747 }
748 
750 {
751  if (!resources::gameboard->map().on_board(recruit_location_)) {
753  return;
754  }
755 
757  if (unit==resources::gameboard->units().end()) {
759  return;
760  }
761  if (unit->side() != get_side()) {
763  return;
764  }
765 }
766 
768 {
769  std::stringstream s;
770  s << "recruitment by side ";
771  s << get_side();
772  s << " of unit type ["<<unit_name_;
774  s << "] on location "<<where_;
775  } else {
776  s << "] on any suitable location";
777  }
778  s <<std::endl;
779  return s.str();
780 }
781 
783 {
784  LOG_AI_ACTIONS << "start of execution of: " << *this << std::endl;
785  assert(is_success());
786 
787  const unit_type *u = unit_types.find(unit_name_);
788  const events::command_disabler disable_commands;
789 
790  // Assert that recruit_location_ has been validated.
791  // This should be implied by is_success() once check_before() has been
792  // called, so this is a guard against future breakage.
793  assert(location_checked_ && u != nullptr);
794 
796  bool gamestate_changed = simulated_recruit(get_side(), u, recruit_location_);
797 
798  sim_gamestate_changed(this, gamestate_changed);
799 
800  return;
801  }
802 
804  //TODO: should we do something to pass use_undo = false in replays and ai moves ?
805  //::actions::recruit_unit(*u, get_side(), recruit_location_, recruit_from_,
806  // !preferences::skip_ai_moves(), false);
807 
809  try {
811  } catch (...) {
812  if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked." << std::endl; } //Demotes to DBG "unchecked result" warning
813  throw;
814  }
815 }
816 
818 {
819 }
820 
821 
822 // stopunit_result
823 stopunit_result::stopunit_result( side_number side, const map_location& unit_location, bool remove_movement, bool remove_attacks)
824  : action_result(side), unit_location_(unit_location), remove_movement_(remove_movement), remove_attacks_(remove_attacks)
825 {
826 }
827 
829 {
831  if (un==resources::gameboard->units().end()){
833  return nullptr;
834  }
835  const unit *u = &*un;
836  if (u->side() != get_side()) {
838  return nullptr;
839  }
840  if (u->incapacitated()) {
842  return nullptr;
843  }
844  return u;
845 }
846 
848 {
849  LOG_AI_ACTIONS << " check_before " << *this << std::endl;
850 
851  if (!get_unit()) {
852  return;
853  }
854 }
855 
857 {
859  if (un==resources::gameboard->units().end()){
861  return;
862  }
863  if (remove_movement_ && un->movement_left() != 0) {
865  return;
866  }
867  if (remove_attacks_ && un->attacks_left() != 0) {
869  return;
870  }
871 }
872 
874 {
875  std::stringstream s;
876  s <<" stopunit by side ";
877  s << get_side();
878  if (remove_movement_){
879  s << " : remove movement ";
880  }
882  s << "and ";
883  }
884  if (remove_attacks_){
885  s << " remove attacks ";
886  }
887  s << "from unit on location "<<unit_location_;
888  s <<std::endl;
889  return s.str();
890 }
891 
893 {
894  LOG_AI_ACTIONS << "start of execution of: " << *this << std::endl;
895  assert(is_success());
897 
900 
901  sim_gamestate_changed(this, gamestate_changed);
902 
903  return;
904  }
905 
906  try {
907  if (remove_movement_){
908  un->remove_movement_ai();
911  }
912  if (remove_attacks_){
913  un->remove_attacks_ai();
915  manager::raise_gamestate_changed();//to be on the safe side
916  }
917  } catch (...) {
918  if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked." << std::endl; } //Demotes to DBG "unchecked result" warning
919  throw;
920  }
921 }
922 
924 {
925 }
926 
927 
928 // synced_command_result
930  : action_result(side), lua_code_(lua_code), location_(location)
931 {
932 }
933 
935 {
936  LOG_AI_ACTIONS << " check_before " << *this << std::endl;
937 }
938 
940 {
941 }
942 
944 {
945  std::stringstream s;
946  s <<" synced_command by side ";
947  s << get_side();
948  s <<std::endl;
949  return s.str();
950 }
951 
953 {
955  bool gamestate_changed = simulated_synced_command();
956 
957  sim_gamestate_changed(this, gamestate_changed);
958 
959  return;
960  }
961 
962  LOG_AI_ACTIONS << "start of execution of: " << *this << std::endl;
963  assert(is_success());
964 
965  std::stringstream s;
967  s << "local x1 = " << location_.wml_x() << " local y1 = " << location_.wml_y() << " ";
968  }
969  s << lua_code_;
970 
972 
973  try {
976  } catch (...) {
977  if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked." << std::endl; } //Demotes to DBG "unchecked result" warning
978  throw;
979  }
980 }
981 
983 {
984 }
985 
986 
987 // =======================================================================
988 // STATELESS INTERFACE TO AI ACTIONS
989 // =======================================================================
990 
992  bool execute,
993  const map_location& attacker_loc,
994  const map_location& defender_loc,
995  int attacker_weapon,
996  double aggression,
997  const unit_advancements_aspect& advancements)
998 {
999  attack_result_ptr action(new attack_result(side,attacker_loc,defender_loc,attacker_weapon,aggression,advancements));
1000  execute ? action->execute() : action->check_before();
1001  return action;
1002 }
1003 
1005  bool execute,
1006  const map_location& from,
1007  const map_location& to,
1008  bool remove_movement,
1009  bool unreach_is_ok)
1010 {
1011  move_result_ptr action(new move_result(side,from,to,remove_movement,unreach_is_ok));
1012  execute ? action->execute() : action->check_before();
1013  return action;
1014 }
1015 
1017  bool execute,
1018  const std::string& unit_id,
1019  const map_location& where,
1020  const map_location& from)
1021 {
1022  recall_result_ptr action(new recall_result(side,unit_id,where,from));
1023  execute ? action->execute() : action->check_before();
1024  return action;
1025 }
1026 
1028  bool execute,
1029  const std::string& unit_name,
1030  const map_location& where,
1031  const map_location& from)
1032 {
1033  recruit_result_ptr action(new recruit_result(side,unit_name,where,from));
1034  execute ? action->execute() : action->check_before();
1035  return action;
1036 }
1037 
1039  bool execute,
1040  const map_location& unit_location,
1041  bool remove_movement,
1042  bool remove_attacks)
1043 {
1044  stopunit_result_ptr action(new stopunit_result(side,unit_location,remove_movement,remove_attacks));
1045  execute ? action->execute() : action->check_before();
1046  return action;
1047 }
1048 
1050  bool execute,
1051  const std::string& lua_code,
1052  const map_location& location)
1053 {
1054  synced_command_result_ptr action(new synced_command_result(side,lua_code,location));
1055  execute ? action->execute() : action->check_before();
1056  return action;
1057 }
1058 
1059 const std::string& actions::get_error_name(int error_code)
1060 {
1061  if (error_names_.empty()){
1062  error_names_.emplace(action_result::AI_ACTION_SUCCESS, "action_result::AI_ACTION_SUCCESS");
1063  error_names_.emplace(action_result::AI_ACTION_STARTED, "action_result::AI_ACTION_STARTED");
1064  error_names_.emplace(action_result::AI_ACTION_FAILURE, "action_result::AI_ACTION_FAILURE");
1065 
1066  error_names_.emplace(attack_result::E_EMPTY_ATTACKER, "attack_result::E_EMPTY_ATTACKER");
1067  error_names_.emplace(attack_result::E_EMPTY_DEFENDER, "attack_result::E_EMPTY_DEFENDER");
1068  error_names_.emplace(attack_result::E_INCAPACITATED_ATTACKER, "attack_result::E_INCAPACITATED_ATTACKER");
1069  error_names_.emplace(attack_result::E_INCAPACITATED_DEFENDER, "attack_result::E_INCAPACITATED_DEFENDER");
1070  error_names_.emplace(attack_result::E_NOT_OWN_ATTACKER, "attack_result::E_NOT_OWN_ATTACKER");
1071  error_names_.emplace(attack_result::E_NOT_ENEMY_DEFENDER, "attack_result::E_NOT_ENEMY_DEFENDER");
1072  error_names_.emplace(attack_result::E_NO_ATTACKS_LEFT, "attack_result::E_NO_ATTACKS_LEFT");
1073  error_names_.emplace(attack_result::E_WRONG_ATTACKER_WEAPON, "attack_result::E_WRONG_ATTACKER_WEAPON");
1074  error_names_.emplace(attack_result::E_UNABLE_TO_CHOOSE_ATTACKER_WEAPON, "attack_result::E_UNABLE_TO_CHOOSE_ATTACKER_WEAPON");
1075  error_names_.emplace(attack_result::E_ATTACKER_AND_DEFENDER_NOT_ADJACENT," attack_result::E_ATTACKER_AND_DEFENDER_NOT_ADJACENT");
1076 
1077  error_names_.emplace(move_result::E_EMPTY_MOVE, "move_result::E_EMPTY_MOVE");
1078  error_names_.emplace(move_result::E_NO_UNIT, "move_result::E_NO_UNIT");
1079  error_names_.emplace(move_result::E_NOT_OWN_UNIT, "move_result::E_NOT_OWN_UNIT");
1080  error_names_.emplace(move_result::E_INCAPACITATED_UNIT, "move_result::E_INCAPACITATED_UNIT");
1081  error_names_.emplace(move_result::E_AMBUSHED, "move_result::E_AMBUSHED");
1082  error_names_.emplace(move_result::E_FAILED_TELEPORT, "move_result::E_FAILED_TELEPORT");
1083  error_names_.emplace(move_result::E_NOT_REACHED_DESTINATION, "move_result::E_NOT_REACHED_DESTINATION");
1084  error_names_.emplace(move_result::E_NO_ROUTE, "move_result::E_NO_ROUTE");
1085 
1086  error_names_.emplace(recall_result::E_NOT_AVAILABLE_FOR_RECALLING, "recall_result::E_NOT_AVAILABLE_FOR_RECALLING");
1087  error_names_.emplace(recall_result::E_NO_GOLD, "recall_result::E_NO_GOLD");
1088  error_names_.emplace(recall_result::E_NO_LEADER," recall_result::E_NO_LEADER");
1089  error_names_.emplace(recall_result::E_LEADER_NOT_ON_KEEP, "recall_result::E_LEADER_NOT_ON_KEEP");
1090  error_names_.emplace(recall_result::E_BAD_RECALL_LOCATION, "recall_result::E_BAD_RECALL_LOCATION");
1091 
1092  error_names_.emplace(recruit_result::E_NOT_AVAILABLE_FOR_RECRUITING, "recruit_result::E_NOT_AVAILABLE_FOR_RECRUITING");
1093  error_names_.emplace(recruit_result::E_UNKNOWN_OR_DUMMY_UNIT_TYPE, "recruit_result::E_UNKNOWN_OR_DUMMY_UNIT_TYPE");
1094  error_names_.emplace(recruit_result::E_NO_GOLD, "recruit_result::E_NO_GOLD");
1095  error_names_.emplace(recruit_result::E_NO_LEADER, "recruit_result::E_NO_LEADER");
1096  error_names_.emplace(recruit_result::E_LEADER_NOT_ON_KEEP, "recruit_result::E_LEADER_NOT_ON_KEEP");
1097  error_names_.emplace(recruit_result::E_BAD_RECRUIT_LOCATION, "recruit_result::E_BAD_RECRUIT_LOCATION");
1098 
1099  error_names_.emplace(stopunit_result::E_NO_UNIT, "stopunit_result::E_NO_UNIT");
1100  error_names_.emplace(stopunit_result::E_NOT_OWN_UNIT, "stopunit_result::E_NOT_OWN_UNIT");
1101  error_names_.emplace(stopunit_result::E_INCAPACITATED_UNIT, "stopunit_result::E_INCAPACITATED_UNIT");
1102  }
1104  if (i==error_names_.end()){
1105  ERR_AI_ACTIONS << "error name not available for error #"<<error_code << std::endl;
1106  i = error_names_.find(-1);
1107  assert(i != error_names_.end());
1108  }
1109  return i->second;
1110 }
1111 
1112 std::map<int,std::string> actions::error_names_;
1113 
1114 void sim_gamestate_changed(action_result *result, bool gamestate_changed){
1115  if(gamestate_changed){
1116  result->set_gamestate_changed();
1118  }
1119 }
1120 
1121 } //end of namespace ai
1122 
1123 
1124 std::ostream &operator<<(std::ostream &s, ai::attack_result const &r) {
1125  s << r.do_describe();
1126  return s;
1127 }
1128 
1129 std::ostream &operator<<(std::ostream &s, ai::move_result const &r) {
1130  s << r.do_describe();
1131  return s;
1132 }
1133 
1134 std::ostream &operator<<(std::ostream &s, ai::recall_result const &r) {
1135  s << r.do_describe();
1136  return s;
1137 }
1138 
1139 std::ostream &operator<<(std::ostream &s, ai::recruit_result const &r) {
1140  s << r.do_describe();
1141  return s;
1142 }
1143 
1144 std::ostream &operator<<(std::ostream &s, ai::stopunit_result const &r) {
1145  s << r.do_describe();
1146  return s;
1147 }
1148 
1149 std::ostream &operator<<(std::ostream &s, ai::synced_command_result const &r) {
1150  s << r.do_describe();
1151  return s;
1152 }
static config get_lua_ai(const std::string &lua_code)
bool unreach_is_ok_
Definition: actions.hpp:199
play_controller * controller
Definition: resources.cpp:21
map_location recruit_location_
Definition: actions.hpp:264
boost::intrusive_ptr< const unit > unit_const_ptr
Definition: ptr.hpp:30
map_location recruit_from_
Definition: actions.hpp:265
virtual void do_init_for_execution()
Definition: actions.cpp:923
virtual void do_check_after()
Definition: actions.cpp:856
bool simulated_synced_command()
::tod_manager * tod_manager
Definition: resources.cpp:30
bool has_ambusher_
Definition: actions.hpp:200
virtual const unit_map & units() const
Definition: game_board.hpp:97
std::vector< char_t > string
bool incapacitated() const
Check if the unit has been petrified.
Definition: unit.hpp:763
virtual void do_execute()
Definition: actions.cpp:260
void check_before()
Definition: actions.cpp:86
bool remove_movement_
Definition: actions.hpp:196
map_location recall_from_
Definition: actions.hpp:232
std::shared_ptr< stopunit_result > stopunit_result_ptr
Definition: game_info.hpp:96
unit_const_ptr get_recall_unit(const team &my_team)
Definition: actions.cpp:538
size_t move_unit_and_record(const std::vector< map_location > &steps, undo_list *undo_stack, bool continued_move, bool show_move, bool *interrupted, move_unit_spectator *move_spectator)
Moves a unit across the board.
Definition: move.cpp:1251
static attack_result_ptr execute_attack_action(side_number side, bool execute, const map_location &attacker_loc, const map_location &defender_loc, int attacker_weapon, double aggression, const unit_advancements_aspect &advancements=unit_advancements_aspect())
Ask the game to attack an enemy defender using our unit attacker from attackers current location...
Definition: actions.cpp:991
virtual void do_check_before()
Definition: actions.cpp:395
This class represents a single unit of a specific type.
Definition: unit.hpp:100
static std::map< int, std::string > error_names_
Definition: actions.hpp:446
team & get_my_team() const
Definition: actions.cpp:167
No leaders exist.
Definition: create.hpp:38
void do_final_checkup(bool dont_throw=false)
const unit * get_unit()
Definition: actions.cpp:341
virtual void do_execute()=0
static void raise_gamestate_changed()
Notifies all observers of 'ai_gamestate_changed' event.
Definition: manager.cpp:433
map_location unit_location_
Definition: actions.hpp:198
virtual void do_init_for_execution()
Definition: actions.cpp:982
Various functions that implement attacks and attack calculations.
bool is_success() const
Definition: actions.cpp:152
static config get_recall(const std::string &unit_id, const map_location &loc, const map_location &from)
const combatant & get_attacker_combatant(const combatant *prev_def=nullptr)
Get the simulation results.
Definition: attack.cpp:452
virtual void do_check_after()
Definition: actions.cpp:412
bool test_route(const unit &un)
Definition: actions.cpp:360
Managing the AI-Game interaction - AI actions and their results.
map_location recall_location_
Definition: actions.hpp:231
static game_info & get_active_ai_info_for_side(side_number side)
Gets AI info for active AI of the given side.
Definition: manager.cpp:740
const time_of_day & get_time_of_day(int for_turn=0) const
Returns global time of day for the passed turn.
Definition: tod_manager.hpp:54
int movement_left() const
Gets how far a unit can move, considering the incapacitated flag.
Definition: unit.hpp:1067
virtual void do_init_for_execution()
Definition: actions.cpp:521
const map_location & where_
Definition: actions.hpp:263
const std::string & unit_id_
Definition: actions.hpp:229
const unit_type * get_unit_type_known(const std::string &recruit)
Definition: actions.cpp:687
virtual std::string do_describe() const
Definition: actions.cpp:767
std::shared_ptr< move_result > move_result_ptr
Definition: game_info.hpp:94
virtual void do_init_for_execution()
Definition: actions.cpp:817
virtual std::string do_describe() const
Definition: actions.cpp:247
virtual std::string do_describe() const
Definition: actions.cpp:617
virtual void do_check_before()
Definition: actions.cpp:847
int get_status() const
Definition: actions.cpp:147
unit_type_data unit_types
Definition: types.cpp:1455
Recruitment OK, but not at the specified location.
Definition: create.hpp:42
const unit_advancements_aspect & advancements_
Definition: actions.hpp:162
int wml_y() const
Definition: location.hpp:116
virtual void do_execute()
Definition: actions.cpp:445
std::shared_ptr< recruit_result > recruit_result_ptr
Definition: game_info.hpp:93
const map_location from_
Definition: actions.hpp:194
void check_victory()
Checks to see if a side has won.
static config unit_name(const unit *u)
Definition: reports.cpp:132
action_result(side_number side)
Definition: actions.cpp:69
bool simulated_attack(const map_location &attacker_loc, const map_location &defender_loc, double attacker_hp, double defender_hp)
virtual void do_check_after()=0
-file sdl_utils.hpp
const std::string & lua_code_
Definition: actions.hpp:308
std::shared_ptr< attack_result > attack_result_ptr
Definition: game_info.hpp:91
void init_for_execution()
Definition: actions.cpp:110
void add_synced_command(const std::string &name, const config &command)
Definition: replay.cpp:237
bool has_interrupted_teleport_
Definition: actions.hpp:201
std::shared_ptr< pathfind::plain_route > route_
Definition: actions.hpp:197
A single unit type that the player may recruit.
Definition: types.hpp:43
const unit_map::const_iterator & get_failed_teleport() const
get the location of a failed teleport
Definition: move.cpp:77
const combatant & get_defender_combatant(const combatant *prev_def=nullptr)
Definition: attack.cpp:467
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:44
A small explanation about what's going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:58
team & get_team(int i)
Definition: game_board.hpp:94
virtual void do_init_for_execution()
Definition: actions.cpp:670
int recall_cost() const
Definition: team.hpp:192
static recall_result_ptr execute_recall_action(side_number side, bool execute, const std::string &unit_id, const map_location &where, const map_location &from)
Ask the game to recall a unit for us on specified location.
Definition: actions.cpp:1016
bool simulated_recall(int side, const std::string &unit_id, const map_location &recall_location)
std::shared_ptr< recall_result > recall_result_ptr
Definition: game_info.hpp:92
std::ostream & operator<<(std::ostream &s, ai::attack_result const &r)
Definition: actions.cpp:1124
unit_ptr find_if_matches_id(const std::string &unit_id)
Find a unit by id. Null pointer if not found.
int wml_x() const
Definition: location.hpp:115
static lg::log_domain log_ai_actions("ai/actions")
bool tiles_adjacent(const map_location &a, const map_location &b)
Function which tells if two locations are adjacent.
Definition: location.hpp:339
virtual void do_check_after()
Definition: actions.cpp:749
#define ERR_AI_ACTIONS
Definition: actions.cpp:64
Structure which holds a single route between one location and another.
Definition: pathfind.hpp:131
bool valid() const
Definition: location.hpp:72
const map_location & get_location() const
The current map location this unit is at.
Definition: unit.hpp:1141
bool return_value_checked_
Definition: actions.hpp:114
bool is_execution() const
Definition: actions.cpp:157
static void ignore_error_function(const std::string &message, bool heavy)
a function to be passed to run_in_synced_context to ignore the error.
virtual void do_check_after()
Definition: actions.cpp:599
int w() const
Effective map width.
Definition: map.hpp:90
game_board * gameboard
Definition: resources.cpp:20
virtual void do_execute()
Definition: actions.cpp:952
bool location_checked_
Definition: actions.hpp:233
const battle_context_unit_stats & get_defender_stats() const
This method returns the statistics of the defender.
Definition: attack.hpp:202
bool test_enough_gold(const team &my_team, const unit_type &type)
Definition: actions.cpp:697
Computes the statistics of a battle between an attacker and a defender unit.
Definition: attack.hpp:170
synced_command_result(side_number side, const std::string &lua_code, const map_location &location)
Definition: actions.cpp:929
Managing the AIs lifecycle - headers.
int attack_num
Index into unit->attacks() or -1 for none.
Definition: attack.hpp:50
double aggression_
Definition: actions.hpp:161
replay * recorder
Definition: resources.cpp:29
virtual void do_check_after()
Definition: actions.cpp:243
virtual void do_execute()
Definition: actions.cpp:632
static const map_location & null_location()
Definition: location.hpp:220
const bool remove_movement_
Definition: actions.hpp:291
friend void sim_gamestate_changed(action_result *result, bool gamestate_changed)
Definition: actions.cpp:1114
No vacant castle tiles around a leader on a keep.
Definition: create.hpp:41
virtual void do_check_before()=0
virtual void do_init_for_execution()=0
attack_result(side_number side, const map_location &attacker_loc, const map_location &defender_loc, int attacker_weapon, double aggression, const unit_advancements_aspect &advancements=unit_advancements_aspect())
Definition: actions.cpp:174
const std::string & unit_name_
Definition: actions.hpp:262
virtual std::string do_describe() const
Definition: actions.cpp:430
void sim_gamestate_changed(action_result *result, bool gamestate_changed)
Definition: actions.cpp:1114
std::shared_ptr< synced_command_result > synced_command_result_ptr
Definition: game_info.hpp:97
virtual void do_check_before()
Definition: actions.cpp:706
int cost() const
Definition: types.hpp:159
Encapsulates the map of the game.
Definition: location.hpp:40
plain_route a_star_search(const map_location &src, const map_location &dst, double stop_at, const cost_calculator &calc, const size_t width, const size_t height, const teleport_map *teleports, bool border)
const bool remove_attacks_
Definition: actions.hpp:292
virtual void do_check_before()
Definition: actions.cpp:556
Various functions related to the creation of units (recruits, recalls, and placed units)...
const map_location & defender_loc_
Definition: actions.hpp:159
void check_after()
Definition: actions.cpp:81
RECRUIT_CHECK check_recall_location(const int side, map_location &recall_location, map_location &recall_from, const unit &unit_recall)
Checks if there is a location on which to recall unit_recall.
Definition: create.cpp:276
const unit_map::const_iterator & get_ambusher() const
get the location of an ambusher
Definition: move.cpp:71
int h() const
Effective map height.
Definition: map.hpp:93
No able leaders are on a keep.
Definition: create.hpp:40
virtual std::string do_describe() const
Definition: actions.cpp:943
bool simulated_stopunit(const map_location &unit_location, bool remove_movement, bool remove_attacks)
virtual const map_location & get_unit_location() const
Definition: actions.cpp:407
static map_location::DIRECTION s
void maybe_throw_return_to_play_side() const
bool test_enough_gold(const team &my_team)
Definition: actions.cpp:547
int turn() const
std::set< map_location > recent_attacks
hack.
Definition: game_info.hpp:117
void set_gamestate_changed()
Definition: actions.cpp:142
virtual void do_check_before()
Definition: actions.cpp:934
static recruit_result_ptr execute_recruit_action(side_number side, bool execute, const std::string &unit_name, const map_location &where, const map_location &from)
Ask the game to recruit a unit for us on specified location.
Definition: actions.cpp:1027
const unit_map::const_iterator & get_unit() const
get new location of moved unit
Definition: move.cpp:95
size_t i
Definition: function.cpp:933
stopunit_result(side_number side, const map_location &unit_location, bool remove_movement, bool remove_attacks)
Definition: actions.cpp:823
static synced_command_result_ptr execute_synced_command_action(side_number side, bool execute, const std::string &lua_code, const map_location &location)
Ask the game to run Lua code.
Definition: actions.cpp:1049
bool is_gamestate_changed_
Definition: actions.hpp:125
RECRUIT_CHECK check_recruit_location(const int side, map_location &recruit_location, map_location &recruited_from, const std::string &unit_type)
Checks if there is a location on which to place a recruited unit.
Definition: create.cpp:406
bool simulation_
Definition: resources.cpp:37
virtual std::string do_describe() const
Definition: actions.cpp:873
virtual void do_check_before()
Definition: actions.cpp:178
variant a_
Definition: function.cpp:722
const map_location & unit_location_
Definition: actions.hpp:290
static stopunit_result_ptr execute_stopunit_action(side_number side, bool execute, const map_location &unit_location, bool remove_movement, bool remove_attacks)
Ask the game to remove unit movements and/or attack.
Definition: actions.cpp:1038
void set_error(int error_code, bool log_as_error=true)
Definition: actions.cpp:129
virtual const gamemap & map() const
Definition: game_board.hpp:96
#define DBG_AI_ACTIONS
Definition: actions.cpp:61
void set_unit(const unit_map::const_iterator &u)
set the iterator to moved unit
Definition: move.cpp:133
bool is_gamestate_changed() const
Definition: actions.cpp:118
int gold() const
Definition: team.hpp:188
int get_side() const
Definition: actions.hpp:87
static bool run_in_synced_context_if_not_already(const std::string &commandname, const config &data, bool use_undo=true, bool show=true, synced_command::error_handler_function error_handler=default_error_function)
checks whether we are currently running in a synced context, and if not we enters it...
const map_location to_
Definition: actions.hpp:195
bool find(E event, F functor)
Tests whether an event handler is available.
bool simulated_move(int side, const map_location &from, const map_location &to, int steps, map_location &unit_location)
#define LOG_AI_ACTIONS
Definition: actions.cpp:62
bool simulated_recruit(int side, const unit_type *u, const map_location &recruit_location)
Standard logging facilities (interface).
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:259
recall_list_manager & recall_list()
Definition: team.hpp:214
virtual void do_execute()
Definition: actions.cpp:892
virtual std::string do_describe() const =0
const unit_type * find(const std::string &key, unit_type::BUILD_STATUS status=unit_type::FULL) const
Finds a unit_type by its id() and makes sure it is built to the specified level.
Definition: types.cpp:1273
double average_hp(unsigned int healing=0) const
What's the average hp (weighted average of hp_dist).
static config get_recruit(const std::string &type_id, const map_location &loc, const map_location &from)
const battle_context_unit_stats & get_attacker_stats() const
This method returns the statistics of the attacker.
Definition: attack.hpp:196
int side_number
Definition: game_info.hpp:36
unit_iterator find(size_t id)
Definition: map.cpp:311
bool valid() const
Definition: map.hpp:276
recall_result(side_number side, const std::string &unit_id, const map_location &where, const map_location &from)
Definition: actions.cpp:527
const map_location where_
Definition: actions.hpp:230
Ensures that the real unit map is active for the duration of the struct's life.
Definition: manager.hpp:282
const unit * get_unit()
Definition: actions.cpp:828
static config get_attack(const map_location &a, const map_location &b, int att_weapon, int def_weapon, const std::string &attacker_type_id, const std::string &defender_type_id, int attacker_lvl, int defender_lvl, const size_t turn, const time_of_day &t)
const map_location & attacker_loc_
Definition: actions.hpp:158
move_result(side_number side, const map_location &from, const map_location &to, bool remove_movement, bool unreach_is_ok)
Definition: actions.cpp:327
static bool is_synced()
virtual void do_check_after()
Definition: actions.cpp:939
std::string::const_iterator iterator
Definition: tokenizer.hpp:24
unit_map * units
Definition: resources.cpp:34
int side() const
The side this unit belongs to.
Definition: unit.hpp:244
recruit_result(side_number side, const std::string &unit_name, const map_location &where, const map_location &from)
Definition: actions.cpp:676
virtual ~action_result()
Definition: actions.cpp:74
static move_result_ptr execute_move_action(side_number side, bool execute, const map_location &from, const map_location &to, bool remove_movement, bool unreach_is_ok=false)
Ask the game to move our unit from location 'from' to location 'to', optionally - doing a partial mov...
Definition: actions.cpp:1004
virtual void do_init_for_execution()
Definition: actions.cpp:321
bool skip_ai_moves()
Definition: game.cpp:750
void attack_unit_and_advance(const map_location &attacker, const map_location &defender, int attack_with, int defend_with, bool update_display, const ai::unit_advancements_aspect &ai_advancement)
Performs an attack, and advanced the units afterwards.
Definition: attack.cpp:1547
const std::string & id() const
The id for this unit_type.
Definition: types.hpp:140
No leaders able to recall/recruit the given unit/type.
Definition: create.hpp:39
static const std::string & get_error_name(int error_code)
get human-readable name of the error by code.
Definition: actions.cpp:1059
const std::vector< map_location > & route_
Definition: move.cpp:292
const map_location & location_
Definition: actions.hpp:309
virtual void do_execute()
Definition: actions.cpp:782
game_info & get_info() const
Definition: actions.cpp:162
Implement simulated actions.