The Battle for Wesnoth  1.19.7+dev
actions.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2024
3  by Yurii Chernyi <terraninfo@terraninfo.net>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 /**
17  * Managing the AI-Game interaction - AI actions and their results
18  * @file
19  */
20 
21 /**
22  * A small explanation about what's going on here:
23  * Each action has access to two game_info objects
24  * First is 'info' - real information
25  * Second is 'subjective info' - AIs perception of what's going on
26  * So, when we check_before action, we use 'subjective info' and don't
27  * touch real 'info' at all.
28  * But when we actually want to execute an action, we firstly check
29  * 'subjective info' and then (if subjective check is ok) do the same
30  * check on real 'info'. There's a caveat: if we fail an action based
31  * on real 'info', then we NEED to update AIs knowledge to avoid the ai
32  * doing the same thing again.
33  * So far the use of 'subjective info' is stubbed out.
34  */
35 
36 #include "actions/undo.hpp"
37 #include "ai/actions.hpp"
38 #include "ai/manager.hpp"
39 #include "ai/simulated_actions.hpp"
40 
41 #include "actions/attack.hpp"
42 #include "actions/create.hpp"
43 #include "attack_prediction.hpp"
45 #include "log.hpp"
46 #include "map/map.hpp"
47 #include "mouse_handler_base.hpp"
48 #include "pathfind/teleport.hpp"
49 #include "play_controller.hpp"
50 #include "recall_list_manager.hpp"
51 #include "replay_helper.hpp"
52 #include "resources.hpp"
53 #include "synced_context.hpp"
54 #include "team.hpp"
55 #include "units/unit.hpp"
56 #include "units/ptr.hpp"
57 #include "units/types.hpp"
58 
59 namespace ai {
60 
61 static lg::log_domain log_ai_actions("ai/actions");
62 #define DBG_AI_ACTIONS LOG_STREAM(debug, log_ai_actions)
63 #define LOG_AI_ACTIONS LOG_STREAM(info, log_ai_actions)
64 #define WRN_AI_ACTIONS LOG_STREAM(warn, log_ai_actions)
65 #define ERR_AI_ACTIONS LOG_STREAM(err, log_ai_actions)
66 
67 // =======================================================================
68 // AI ACTIONS
69 // =======================================================================
71  : return_value_checked_(true),side_(side),status_(AI_ACTION_SUCCESS),is_execution_(false),is_gamestate_changed_(false)
72 {
73 }
74 
76 {
77  if (!return_value_checked_) {
78  DBG_AI_ACTIONS << "Return value of AI ACTION was not checked."; //Demotes to DBG "unchecked result" warning
79  }
80 }
81 
83 {
85 }
86 
88 {
90 }
91 
93 {
94  is_execution_ = true;
96  check_before();
97  if (is_success()){
98  try {
99  do_execute();
100  } catch (const return_to_play_side_exception&) {
101  if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked."; } //Demotes to DBG "unchecked result" warning
102  throw;
103  }
104  }
105  if (is_success()){
106  check_after();
107  }
108  is_execution_ = false;
109 }
110 
112 {
113  return_value_checked_ = false;
114  is_gamestate_changed_ = false;
117 }
118 
120 {
121  return is_gamestate_changed_;
122 }
123 
125 {
126  return_value_checked_ = true;
127  return is_success();
128 }
129 
130 void action_result::set_error(int error_code, bool log_as_error){
131  status_ = error_code;
132  if (is_execution()) {
133  if (log_as_error) {
134  ERR_AI_ACTIONS << "Error #"<<error_code<<" ("<<actions::get_error_name(error_code)<<") in "<< do_describe();
135  } else {
136  LOG_AI_ACTIONS << "Error #"<<error_code<<" ("<<actions::get_error_name(error_code)<<") in "<< do_describe();
137  }
138  } else {
139  LOG_AI_ACTIONS << "Error #"<<error_code<<" ("<<actions::get_error_name(error_code)<<") when checking "<< do_describe();
140  }
141 }
142 
144 {
145  is_gamestate_changed_ = true;
146 }
147 
149 {
150  return status_;
151 }
152 
154 {
156 }
157 
159 {
160  return is_execution_;
161 }
162 
164 {
166 }
167 
169 {
171 }
172 
173 
174 // attack_result
175 attack_result::attack_result( side_number side, const map_location& attacker_loc, const map_location& defender_loc, int attacker_weapon, double aggression)
176  : action_result(side), attacker_loc_(attacker_loc), defender_loc_(defender_loc), attacker_weapon_(attacker_weapon), aggression_(aggression)
177 {
178 }
179 
181 {
182  LOG_AI_ACTIONS << " check_before " << *this;
185 
186  if(attacker==resources::gameboard->units().end())
187  {
188  LOG_AI_ACTIONS << "attempt to attack without attacker";
190  return;
191  }
192 
193  if (defender==resources::gameboard->units().end())
194  {
195  LOG_AI_ACTIONS << "attempt to attack without defender";
197  return;
198  }
199 
200  if(attacker->incapacitated()) {
201  LOG_AI_ACTIONS << "attempt to attack with unit that is petrified";
203  return;
204  }
205 
206  if(defender->incapacitated()) {
207  LOG_AI_ACTIONS << "attempt to attack unit that is petrified";
209  return;
210  }
211 
212  if(!attacker->attacks_left()) {
213  LOG_AI_ACTIONS << "attempt to attack with no attacks left";
215  return;
216  }
217 
218  if(attacker->side()!=get_side()) {
219  LOG_AI_ACTIONS << "attempt to attack with not own unit";
221  return;
222  }
223 
224  if(!get_my_team().is_enemy(defender->side())) {
225  LOG_AI_ACTIONS << "attempt to attack unit that is not enemy";
227  return;
228  }
229 
230  if (attacker_weapon_!=-1) {
231  if ((attacker_weapon_<0)||(attacker_weapon_ >= static_cast<int>(attacker->attacks().size()))) {
232  LOG_AI_ACTIONS << "invalid weapon selection for the attacker";
234  return;
235  }
236  }
237 
239  LOG_AI_ACTIONS << "attacker and defender not adjacent";
241  return;
242  }
243 }
244 
246 {
247 }
248 
249 std::string attack_result::do_describe() const
250 {
251  std::stringstream s;
252  s << "attack by side ";
253  s << get_side();
254  s << " from location "<<attacker_loc_;
255  s << " to location "<<defender_loc_;
256  s << " using weapon "<< attacker_weapon_;
257  s << " with aggression "<< aggression_;
258  s <<std::endl;
259  return s.str();
260 }
261 
263 {
264  LOG_AI_ACTIONS << "start of execution of: "<< *this;
265  // Stop the user from issuing any commands while the unit is attacking
266  const events::command_disabler disable_commands;
267  //@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.
270 
271  int attacker_weapon = bc.get_attacker_stats().attack_num;
272  int defender_weapon = bc.get_defender_stats().attack_num;
273 
274  if(attacker_weapon < 0) {
276  return;
277  }
278 
281 
284 
285  sim_gamestate_changed(this, gamestate_changed);
286 
287  return;
288  }
289 
294  attacker_weapon,
295  defender_weapon,
296  a_->type_id(),
297  d_->type_id(),
298  a_->level(),
299  d_->level(),
302  )
303  );
304 
305 
308  //end of ugly hack
309  try {
311  } catch (...) {
312  if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked."; } //Demotes to DBG "unchecked result" warning
313  throw;
314  }
315 }
316 
318 {
319 }
320 
321 
322 // move_result
324  const map_location& to, bool remove_movement, bool unreach_is_ok)
325  : action_result(side)
326  , from_(from)
327  , to_(to)
328  , remove_movement_(remove_movement)
329  , route_()
330  , unit_location_(from)
331  , unreach_is_ok_(unreach_is_ok)
332  , has_ambusher_(false)
333  , has_interrupted_teleport_(false)
334 {
335 }
336 
338 {
340  if (un==resources::gameboard->units().end()){
342  return nullptr;
343  }
344  const unit *u = &*un;
345  if (u->side() != get_side()) {
347  return nullptr;
348  }
349  if (u->incapacitated()) {
351  return nullptr;
352  }
353  return u;
354 }
355 
357 {
358  if (from_== to_) {
359  if (!remove_movement_ || (un.movement_left() == 0) ) {
361  return false;
362  }
363  return true;
364  }
365 
366  if (un.movement_left() == 0 ) {
368  return false;
369  }
370 
371  if (!to_.valid() || !resources::gameboard->map().on_board(to_)) {
373  return false;
374  }
375 
376  team &my_team = get_my_team();
377  const pathfind::shortest_path_calculator calc(un, my_team, resources::gameboard->teams(), resources::gameboard->map());
378 
379  //allowed teleports
380  pathfind::teleport_map allowed_teleports = pathfind::get_teleport_locations(un, my_team, true);
381 
382  //do an A*-search
383  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)));
384  if (route_->steps.empty()) {
386  return false;
387  }
388  return true;
389 }
390 
392 {
393  LOG_AI_ACTIONS << " check_before " << *this;
394  const unit *u = get_unit();
395  if (!u) {
396  return;
397  }
398  if (!test_route(*u)) {
399  return;
400  }
401 }
402 
404 {
405  return unit_location_;
406 }
407 
409 {
410  if (has_ambusher_) {
411  set_error(E_AMBUSHED,false);
412  return;
413  }
416  return;
417  }
418 
419  if (!unreach_is_ok_ && unit_location_!=to_) {
420  DBG_AI_ACTIONS << "Unit did not reach destination in " << do_describe(); //Demotes to DBG "not reached destination" warning
421  return;
422  }
423 }
424 
425 std::string move_result::do_describe() const
426 {
427  std::stringstream s;
428  if (remove_movement_){
429  s << "full move by side ";
430  } else {
431  s << "partial move by side ";
432  }
433  s << get_side();
434  s << " from location "<<from_;
435  s << " to location "<<to_;
436  s <<std::endl;
437  return s.str();
438 }
439 
441 {
442  LOG_AI_ACTIONS << "start of execution of: "<< *this;
443  assert(is_success());
444 
446  bool gamestate_changed = false;
447  if(from_ != to_){
448  int step = route_->steps.size();
449  gamestate_changed = simulated_move(get_side(), from_, to_, step, unit_location_);
450  } else {
451  assert(remove_movement_);
452  }
453 
455  if(remove_movement_ && un->movement_left() > 0 && unit_location_ == to_){
456  gamestate_changed = simulated_stopunit(unit_location_, true, false);
457  }
458 
459  sim_gamestate_changed(this, gamestate_changed);
460 
461  return;
462  }
463 
464  ::actions::move_unit_spectator move_spectator(resources::gameboard->units());
465  move_spectator.set_unit(resources::gameboard->units().find(from_));
466  move_spectator.set_ai_move(true);
467 
468  if (from_ != to_) {
470  /*std::vector<map_location> steps*/ route_->steps,
471  /*bool continue_move*/ true,
472  /*::actions::move_unit_spectator* move_spectator*/ move_spectator);
473 
474  if( move_spectator.get_tiles_entered() > 0) {
476  } else if ( move_spectator.get_ambusher().valid() ) {
477  // Unlikely, but some types of strange WML (or bad pathfinding)
478  // could cause an ambusher to be found without moving.
480  }
481  } else {
482  assert(remove_movement_);
483  }
484 
485  if (move_spectator.get_unit().valid()){
486  unit_location_ = move_spectator.get_unit()->get_location();
487  if (remove_movement_ && move_spectator.get_unit()->movement_left() > 0 && unit_location_ == to_)
488  {
490  if (!stopunit_res->is_ok()) {
491  set_error(stopunit_res->get_status());
492  }
493  if (stopunit_res->is_gamestate_changed()) {
495  }
496  }
497  } else {
499  }
500 
501  has_ambusher_ = move_spectator.get_ambusher().valid();
503 
504  if (is_gamestate_changed()) {
505  try {
507  } catch (...) {
508  if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked."; } //Demotes to DBG "unchecked result" warning
509  throw;
510  }
511  }
512 }
513 
515 {
516 }
517 
518 
519 // recall_result
521  const std::string& unit_id, const map_location& where, const map_location& from)
522  : action_result(side)
523  , unit_id_(unit_id)
524  , where_(where)
525  , recall_location_(where)
526  , recall_from_(from)
527  , location_checked_(false)
528 {
529 }
530 
532 {
534  if (!rec) {
536  }
537  return rec;
538 }
539 
541 {
542  if (my_team.gold() < my_team.recall_cost() ) {
544  return false;
545  }
546  return true;
547 }
548 
550 {
551  LOG_AI_ACTIONS << " check_before " << *this;
552  const team& my_team = get_my_team();
553  const bool location_specified = recall_location_.valid();
554 
555  //Enough gold?
556  if (!test_enough_gold(my_team)) {
557  return;
558  }
559 
560  //Unit available for recalling?
561  const unit_const_ptr & to_recall = get_recall_unit(my_team);
562  if ( !to_recall ) {
563  return;
564  }
565 
566  // Leader available for recalling?
571  return;
572 
575  return;
576 
579  return;
580 
582  if(location_specified) {
584  return;
585  }
586  [[fallthrough]]; // If the location was not specified, this counts as "OK".
588  location_checked_ = true;
589  }
590 }
591 
593 {
594  if (!resources::gameboard->map().on_board(recall_location_)){
596  return;
597  }
598 
600  if (unit==resources::gameboard->units().end()){
602  return;
603  }
604  if (unit->side() != get_side()){
606  return;
607  }
608 }
609 
610 std::string recall_result::do_describe() const
611 {
612  std::stringstream s;
613  s << "recall by side ";
614  s << get_side();
615  s << " of unit id ["<<unit_id_;
617  s << "] on location "<<where_;
618  } else {
619  s << "] on any suitable location";
620  }
621  s <<std::endl;
622  return s.str();
623 }
624 
626 {
627  LOG_AI_ACTIONS << "start of execution of: " << *this;
628  assert(is_success());
629 
630  const events::command_disabler disable_commands;
631 
632  // Assert that recall_location_ has been validated.
633  // This should be implied by is_success() once check_before() has been
634  // called, so this is a guard against future breakage.
635  assert(location_checked_);
636 
638  bool gamestate_changed = simulated_recall(get_side(), unit_id_, recall_location_);
639 
640  sim_gamestate_changed(this, gamestate_changed);
641 
642  return;
643  }
644 
645  // Do the actual recalling.
649 
651  try {
653  } catch (...) {
654  if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked."; } //Demotes to DBG "unchecked result" warning
655  throw;
656  }
657 }
658 
660 {
661 }
662 
663 
664 // recruit_result
666  const std::string& unit_name, const map_location& where, const map_location& from)
667  : action_result(side)
668  , unit_name_(unit_name)
669  , where_(where)
670  , recruit_location_(where)
671  , recruit_from_(from)
672  , location_checked_(false)
673 {
674 }
675 
676 const unit_type *recruit_result::get_unit_type_known(const std::string &recruit)
677 {
678  const unit_type *type = unit_types.find(recruit);
679  if (!type) {
681  return nullptr;
682  }
683  return type;
684 }
685 
687 {
688  if (my_team.gold() < type.cost()) {
690  return false;
691  }
692  return true;
693 }
694 
696 {
697  LOG_AI_ACTIONS << " check_before " << *this;
698  const team& my_team = get_my_team();
699  const bool location_specified = recruit_location_.valid();
700 
701  //Unit type known ?
702  const unit_type *s_type = get_unit_type_known(unit_name_);
703  if (!s_type) {
704  return;
705  }
706 
707  //Enough gold?
708  if (!test_enough_gold(my_team, *s_type)) {
709  return;
710  }
711 
712  // Leader available for recruiting?
717  return;
718 
721  return;
722 
725  return;
726 
728  if(location_specified) {
730  return;
731  }
732  [[fallthrough]]; // If the location was not specified, this counts as "OK".
734  location_checked_ = true;
735  }
736 }
737 
739 {
740  if (!resources::gameboard->map().on_board(recruit_location_)) {
742  return;
743  }
744 
746  if (unit==resources::gameboard->units().end()) {
748  return;
749  }
750  if (unit->side() != get_side()) {
752  return;
753  }
754 }
755 
756 std::string recruit_result::do_describe() const
757 {
758  std::stringstream s;
759  s << "recruitment by side ";
760  s << get_side();
761  s << " of unit type ["<<unit_name_;
763  s << "] on location "<<where_;
764  } else {
765  s << "] on any suitable location";
766  }
767  s <<std::endl;
768  return s.str();
769 }
770 
772 {
773  LOG_AI_ACTIONS << "start of execution of: " << *this;
774  assert(is_success());
775 
776  const unit_type *u = unit_types.find(unit_name_);
777  const events::command_disabler disable_commands;
778 
779  // Assert that recruit_location_ has been validated.
780  // This should be implied by is_success() once check_before() has been
781  // called, so this is a guard against future breakage.
782  assert(location_checked_ && u != nullptr);
783 
785  bool gamestate_changed = simulated_recruit(get_side(), u, recruit_location_);
786 
787  sim_gamestate_changed(this, gamestate_changed);
788 
789  return;
790  }
791 
794 
796  try {
798  } catch (...) {
799  if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked."; } //Demotes to DBG "unchecked result" warning
800  throw;
801  }
802 }
803 
805 {
806 }
807 
808 
809 // stopunit_result
810 stopunit_result::stopunit_result( side_number side, const map_location& unit_location, bool remove_movement, bool remove_attacks)
811  : action_result(side), unit_location_(unit_location), remove_movement_(remove_movement), remove_attacks_(remove_attacks)
812 {
813 }
814 
816 {
818  if (un==resources::gameboard->units().end()){
820  return nullptr;
821  }
822  const unit *u = &*un;
823  if (u->side() != get_side()) {
825  return nullptr;
826  }
827  if (u->incapacitated()) {
829  return nullptr;
830  }
831  return u;
832 }
833 
835 {
836  LOG_AI_ACTIONS << " check_before " << *this;
837 
838  if (!get_unit()) {
839  return;
840  }
841 }
842 
844 {
846  if (un==resources::gameboard->units().end()){
848  return;
849  }
850  if (remove_movement_ && un->movement_left() != 0) {
852  return;
853  }
854  if (remove_attacks_ && un->attacks_left() != 0) {
856  return;
857  }
858 }
859 
860 std::string stopunit_result::do_describe() const
861 {
862  std::stringstream s;
863  s <<" stopunit by side ";
864  s << get_side();
865  if (remove_movement_){
866  s << " : remove movement ";
867  }
869  s << "and ";
870  }
871  if (remove_attacks_){
872  s << " remove attacks ";
873  }
874  s << "from unit on location "<<unit_location_;
875  s <<std::endl;
876  return s.str();
877 }
878 
880 {
881  LOG_AI_ACTIONS << "start of execution of: " << *this;
882  assert(is_success());
884 
887 
888  sim_gamestate_changed(this, gamestate_changed);
889 
890  return;
891  }
892 
893  try {
894  // Don't mark the game state as changed if unit already has no moves or attacks.
895  // Doing so can cause infinite candidate action loops.
896  if (remove_movement_ && un->movement_left() != 0) {
897  un->remove_movement_ai();
900  }
901  if (remove_attacks_ && un->attacks_left() != 0){
902  un->remove_attacks_ai();
904  manager::get_singleton().raise_gamestate_changed();//to be on the safe side
905  }
906  } catch (...) {
907  if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked."; } //Demotes to DBG "unchecked result" warning
908  throw;
909  }
910 }
911 
913 {
914 }
915 
916 
917 // synced_command_result
918 synced_command_result::synced_command_result( side_number side, const std::string& lua_code, const map_location& location )
919  : action_result(side), lua_code_(lua_code), location_(location)
920 {
921 }
922 
924 {
925  LOG_AI_ACTIONS << " check_before " << *this;
926 }
927 
929 {
930 }
931 
933 {
934  std::stringstream s;
935  s <<" synced_command by side ";
936  s << get_side();
937  s <<std::endl;
938  return s.str();
939 }
940 
942 {
944  bool gamestate_changed = simulated_synced_command();
945 
946  sim_gamestate_changed(this, gamestate_changed);
947 
948  return;
949  }
950 
951  LOG_AI_ACTIONS << "start of execution of: " << *this;
952  assert(is_success());
953 
954  std::stringstream s;
956  s << "local x1 = " << location_.wml_x() << " local y1 = " << location_.wml_y() << " ";
957  }
958  s << lua_code_;
959 
960  try {
963  } catch (...) {
964  if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked."; } //Demotes to DBG "unchecked result" warning
965  throw;
966  }
967 }
968 
970 {
971 }
972 
973 
974 // =======================================================================
975 // STATELESS INTERFACE TO AI ACTIONS
976 // =======================================================================
977 
978 static void execute_or_check(action_result& action, bool execute)
979 {
980  if(execute) {
981  action.execute();
982  } else {
983  action.check_before();
984  }
985 }
986 
987 const std::map<int, std::string> actions::error_names_ {
988  {action_result::AI_ACTION_SUCCESS, "action_result::AI_ACTION_SUCCESS"},
989  {action_result::AI_ACTION_STARTED, "action_result::AI_ACTION_STARTED"},
990  {action_result::AI_ACTION_FAILURE, "action_result::AI_ACTION_FAILURE"},
991 
992  {attack_result::E_EMPTY_ATTACKER, "attack_result::E_EMPTY_ATTACKER"},
993  {attack_result::E_EMPTY_DEFENDER, "attack_result::E_EMPTY_DEFENDER"},
994  {attack_result::E_INCAPACITATED_ATTACKER, "attack_result::E_INCAPACITATED_ATTACKER"},
995  {attack_result::E_INCAPACITATED_DEFENDER, "attack_result::E_INCAPACITATED_DEFENDER"},
996  {attack_result::E_NOT_OWN_ATTACKER, "attack_result::E_NOT_OWN_ATTACKER"},
997  {attack_result::E_NOT_ENEMY_DEFENDER, "attack_result::E_NOT_ENEMY_DEFENDER"},
998  {attack_result::E_NO_ATTACKS_LEFT, "attack_result::E_NO_ATTACKS_LEFT"},
999  {attack_result::E_WRONG_ATTACKER_WEAPON, "attack_result::E_WRONG_ATTACKER_WEAPON"},
1000  {attack_result::E_UNABLE_TO_CHOOSE_ATTACKER_WEAPON, "attack_result::E_UNABLE_TO_CHOOSE_ATTACKER_WEAPON"},
1001  {attack_result::E_ATTACKER_AND_DEFENDER_NOT_ADJACENT," attack_result::E_ATTACKER_AND_DEFENDER_NOT_ADJACENT"},
1002 
1003  {move_result::E_EMPTY_MOVE, "move_result::E_EMPTY_MOVE"},
1004  {move_result::E_NO_UNIT, "move_result::E_NO_UNIT"},
1005  {move_result::E_NOT_OWN_UNIT, "move_result::E_NOT_OWN_UNIT"},
1006  {move_result::E_INCAPACITATED_UNIT, "move_result::E_INCAPACITATED_UNIT"},
1007  {move_result::E_AMBUSHED, "move_result::E_AMBUSHED"},
1008  {move_result::E_FAILED_TELEPORT, "move_result::E_FAILED_TELEPORT"},
1009  {move_result::E_NO_ROUTE, "move_result::E_NO_ROUTE"},
1010  {move_result::E_OFF_MAP, "move_result::E_OFF_MAP"},
1011 
1012  {recall_result::E_NOT_AVAILABLE_FOR_RECALLING, "recall_result::E_NOT_AVAILABLE_FOR_RECALLING"},
1013  {recall_result::E_NO_GOLD, "recall_result::E_NO_GOLD"},
1014  {recall_result::E_NO_LEADER," recall_result::E_NO_LEADER"},
1015  {recall_result::E_LEADER_NOT_ON_KEEP, "recall_result::E_LEADER_NOT_ON_KEEP"},
1016  {recall_result::E_BAD_RECALL_LOCATION, "recall_result::E_BAD_RECALL_LOCATION"},
1017 
1018  {recruit_result::E_NOT_AVAILABLE_FOR_RECRUITING, "recruit_result::E_NOT_AVAILABLE_FOR_RECRUITING"},
1019  {recruit_result::E_UNKNOWN_OR_DUMMY_UNIT_TYPE, "recruit_result::E_UNKNOWN_OR_DUMMY_UNIT_TYPE"},
1020  {recruit_result::E_NO_GOLD, "recruit_result::E_NO_GOLD"},
1021  {recruit_result::E_NO_LEADER, "recruit_result::E_NO_LEADER"},
1022  {recruit_result::E_LEADER_NOT_ON_KEEP, "recruit_result::E_LEADER_NOT_ON_KEEP"},
1023  {recruit_result::E_BAD_RECRUIT_LOCATION, "recruit_result::E_BAD_RECRUIT_LOCATION"},
1024 
1025  {stopunit_result::E_NO_UNIT, "stopunit_result::E_NO_UNIT"},
1026  {stopunit_result::E_NOT_OWN_UNIT, "stopunit_result::E_NOT_OWN_UNIT"},
1027  {stopunit_result::E_INCAPACITATED_UNIT, "stopunit_result::E_INCAPACITATED_UNIT"},
1028 };
1029 
1031  bool execute,
1032  const map_location& attacker_loc,
1033  const map_location& defender_loc,
1034  int attacker_weapon,
1035  double aggression)
1036 {
1037  auto action = std::make_shared<attack_result>(side, attacker_loc, defender_loc, attacker_weapon, aggression);
1038  execute_or_check(*action, execute);
1039  return action;
1040 }
1041 
1043  bool execute,
1044  const map_location& from,
1045  const map_location& to,
1046  bool remove_movement,
1047  bool unreach_is_ok)
1048 {
1049  auto action = std::make_shared<move_result>(side, from, to, remove_movement, unreach_is_ok);
1050  execute_or_check(*action, execute);
1051  return action;
1052 }
1053 
1055  bool execute,
1056  const std::string& unit_id,
1057  const map_location& where,
1058  const map_location& from)
1059 {
1060  auto action = std::make_shared<recall_result>(side, unit_id, where, from);
1061  execute_or_check(*action, execute);
1062  return action;
1063 }
1064 
1066  bool execute,
1067  const std::string& unit_name,
1068  const map_location& where,
1069  const map_location& from)
1070 {
1071  auto action = std::make_shared<recruit_result>(side, unit_name, where, from);
1072  execute_or_check(*action, execute);
1073  return action;
1074 }
1075 
1077  bool execute,
1078  const map_location& unit_location,
1079  bool remove_movement,
1080  bool remove_attacks)
1081 {
1082  auto action = std::make_shared<stopunit_result>(side, unit_location, remove_movement, remove_attacks);
1083  execute_or_check(*action, execute);
1084  return action;
1085 }
1086 
1088  bool execute,
1089  const std::string& lua_code,
1090  const map_location& location)
1091 {
1092  auto action = std::make_shared<synced_command_result>(side, lua_code, location);
1093  execute_or_check(*action, execute);
1094  return action;
1095 }
1096 
1097 const std::string& actions::get_error_name(int error_code)
1098 {
1099  auto i = error_names_.find(error_code);
1100  if (i==error_names_.end()){
1101  ERR_AI_ACTIONS << "error name not available for error #"<<error_code;
1102  i = error_names_.find(-1);
1103  assert(i != error_names_.end());
1104  }
1105  return i->second;
1106 }
1107 
1108 void sim_gamestate_changed(action_result *result, bool gamestate_changed){
1109  if(gamestate_changed){
1110  result->set_gamestate_changed();
1112  }
1113 }
1114 
1115 } //end of namespace ai
1116 
1117 
1118 std::ostream &operator<<(std::ostream &s, const ai::attack_result& r) {
1119  s << r.do_describe();
1120  return s;
1121 }
1122 
1123 std::ostream &operator<<(std::ostream &s, const ai::move_result& r) {
1124  s << r.do_describe();
1125  return s;
1126 }
1127 
1128 std::ostream &operator<<(std::ostream &s, const ai::recall_result& r) {
1129  s << r.do_describe();
1130  return s;
1131 }
1132 
1133 std::ostream &operator<<(std::ostream &s, const ai::recruit_result& r) {
1134  s << r.do_describe();
1135  return s;
1136 }
1137 
1138 std::ostream &operator<<(std::ostream &s, const ai::stopunit_result& r) {
1139  s << r.do_describe();
1140  return s;
1141 }
1142 
1143 std::ostream &operator<<(std::ostream &s, const ai::synced_command_result& r) {
1144  s << r.do_describe();
1145  return s;
1146 }
Various functions that implement attacks and attack calculations.
const std::vector< map_location > & route_
Definition: move.cpp:384
#define ERR_AI_ACTIONS
Definition: actions.cpp:65
std::ostream & operator<<(std::ostream &s, const ai::attack_result &r)
Definition: actions.cpp:1118
#define LOG_AI_ACTIONS
Definition: actions.cpp:63
#define DBG_AI_ACTIONS
Definition: actions.cpp:62
Managing the AI-Game interaction - AI actions and their results.
Managing the AIs lifecycle - headers TODO: Refactor history handling and internal commands.
const unit_map::const_iterator & get_ambusher() const
get the location of an ambusher
Definition: move.cpp:72
const unit_map::const_iterator & get_unit() const
get new location of moved unit
Definition: move.cpp:96
std::size_t get_tiles_entered() const
Definition: move.hpp:100
void set_unit(const unit_map::const_iterator &u)
set the iterator to moved unit
Definition: move.cpp:157
void set_ai_move(bool ai_move=true)
Definition: move.hpp:93
const unit_map::const_iterator & get_failed_teleport() const
get the location of a failed teleport
Definition: move.cpp:78
void clear()
Clears the stack of undoable (and redoable) actions.
Definition: undo.cpp:131
virtual void do_check_before()=0
void init_for_execution()
Definition: actions.cpp:111
virtual ~action_result()
Definition: actions.cpp:75
int get_status() const
Definition: actions.cpp:148
virtual void do_execute()=0
void check_before()
Definition: actions.cpp:87
bool return_value_checked_
Definition: actions.hpp:114
void set_gamestate_changed()
Definition: actions.cpp:143
game_info & get_info() const
Definition: actions.cpp:163
virtual void do_init_for_execution()=0
void check_after()
Definition: actions.cpp:82
action_result(side_number side)
Definition: actions.cpp:70
bool is_gamestate_changed() const
Definition: actions.cpp:119
friend void sim_gamestate_changed(action_result *result, bool gamestate_changed)
Definition: actions.cpp:1108
int get_side() const
Definition: actions.hpp:87
virtual void do_check_after()=0
bool is_success() const
Definition: actions.cpp:153
bool is_gamestate_changed_
Definition: actions.hpp:125
team & get_my_team() const
Definition: actions.cpp:168
virtual std::string do_describe() const =0
bool is_execution() const
Definition: actions.cpp:158
void set_error(int error_code, bool log_as_error=true)
Definition: actions.cpp:130
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:1054
static const std::map< int, std::string > error_names_
Definition: actions.hpp:428
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:1087
static const std::string & get_error_name(int error_code)
get human-readable name of the error by code.
Definition: actions.cpp:1097
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)
Ask the game to attack an enemy defender using our unit attacker from attackers current location,...
Definition: actions.cpp:1030
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:1042
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:1065
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:1076
double aggression_
Definition: actions.hpp:160
virtual void do_execute()
Definition: actions.cpp:262
attack_result(side_number side, const map_location &attacker_loc, const map_location &defender_loc, int attacker_weapon, double aggression)
Definition: actions.cpp:175
virtual std::string do_describe() const
Definition: actions.cpp:249
virtual void do_check_before()
Definition: actions.cpp:180
const map_location & attacker_loc_
Definition: actions.hpp:157
const map_location & defender_loc_
Definition: actions.hpp:158
virtual void do_init_for_execution()
Definition: actions.cpp:317
@ E_UNABLE_TO_CHOOSE_ATTACKER_WEAPON
Definition: actions.hpp:146
@ E_ATTACKER_AND_DEFENDER_NOT_ADJACENT
Definition: actions.hpp:147
virtual void do_check_after()
Definition: actions.cpp:245
std::set< map_location > recent_attacks
Definition: game_info.hpp:115
void raise_gamestate_changed()
Notifies all observers of 'ai_gamestate_changed' event.
Definition: manager.cpp:448
game_info & get_active_ai_info_for_side(side_number side)
Gets AI info for active AI of the given side.
Definition: manager.cpp:712
static manager & get_singleton()
Definition: manager.hpp:140
virtual const map_location & get_unit_location() const
Definition: actions.cpp:403
bool has_interrupted_teleport_
Definition: actions.hpp:199
bool test_route(const unit &un)
Definition: actions.cpp:356
const map_location to_
Definition: actions.hpp:193
const map_location from_
Definition: actions.hpp:192
virtual void do_check_after()
Definition: actions.cpp:408
virtual void do_execute()
Definition: actions.cpp:440
virtual void do_check_before()
Definition: actions.cpp:391
std::shared_ptr< pathfind::plain_route > route_
Definition: actions.hpp:195
const unit * get_unit()
Definition: actions.cpp:337
move_result(side_number side, const map_location &from, const map_location &to, bool remove_movement, bool unreach_is_ok)
Definition: actions.cpp:323
bool remove_movement_
Definition: actions.hpp:194
virtual void do_init_for_execution()
Definition: actions.cpp:514
bool has_ambusher_
Definition: actions.hpp:198
map_location unit_location_
Definition: actions.hpp:196
virtual std::string do_describe() const
Definition: actions.cpp:425
bool unreach_is_ok_
Definition: actions.hpp:197
recall_result(side_number side, const std::string &unit_id, const map_location &where, const map_location &from)
Definition: actions.cpp:520
virtual std::string do_describe() const
Definition: actions.cpp:610
virtual void do_init_for_execution()
Definition: actions.cpp:659
map_location recall_location_
Definition: actions.hpp:229
const std::string & unit_id_
Definition: actions.hpp:227
virtual void do_check_after()
Definition: actions.cpp:592
bool test_enough_gold(const team &my_team)
Definition: actions.cpp:540
@ E_NOT_AVAILABLE_FOR_RECALLING
Definition: actions.hpp:208
unit_const_ptr get_recall_unit(const team &my_team)
Definition: actions.cpp:531
bool location_checked_
Definition: actions.hpp:231
virtual void do_check_before()
Definition: actions.cpp:549
map_location recall_from_
Definition: actions.hpp:230
virtual void do_execute()
Definition: actions.cpp:625
const map_location where_
Definition: actions.hpp:228
virtual void do_check_before()
Definition: actions.cpp:695
virtual void do_execute()
Definition: actions.cpp:771
map_location recruit_from_
Definition: actions.hpp:263
const unit_type * get_unit_type_known(const std::string &recruit)
Definition: actions.cpp:676
map_location recruit_location_
Definition: actions.hpp:262
virtual std::string do_describe() const
Definition: actions.cpp:756
bool test_enough_gold(const team &my_team, const unit_type &type)
Definition: actions.cpp:686
const map_location & where_
Definition: actions.hpp:261
virtual void do_check_after()
Definition: actions.cpp:738
@ E_NOT_AVAILABLE_FOR_RECRUITING
Definition: actions.hpp:239
@ E_UNKNOWN_OR_DUMMY_UNIT_TYPE
Definition: actions.hpp:240
recruit_result(side_number side, const std::string &unit_name, const map_location &where, const map_location &from)
Definition: actions.cpp:665
virtual void do_init_for_execution()
Definition: actions.cpp:804
const std::string & unit_name_
Definition: actions.hpp:260
virtual void do_init_for_execution()
Definition: actions.cpp:912
virtual void do_execute()
Definition: actions.cpp:879
virtual void do_check_before()
Definition: actions.cpp:834
virtual std::string do_describe() const
Definition: actions.cpp:860
const bool remove_movement_
Definition: actions.hpp:289
const bool remove_attacks_
Definition: actions.hpp:290
const unit * get_unit()
Definition: actions.cpp:815
stopunit_result(side_number side, const map_location &unit_location, bool remove_movement, bool remove_attacks)
Definition: actions.cpp:810
const map_location & unit_location_
Definition: actions.hpp:288
virtual void do_check_after()
Definition: actions.cpp:843
virtual std::string do_describe() const
Definition: actions.cpp:932
virtual void do_check_after()
Definition: actions.cpp:928
const map_location & location_
Definition: actions.hpp:307
virtual void do_init_for_execution()
Definition: actions.cpp:969
synced_command_result(side_number side, const std::string &lua_code, const map_location &location)
Definition: actions.cpp:918
virtual void do_check_before()
Definition: actions.cpp:923
const std::string & lua_code_
Definition: actions.hpp:306
virtual void do_execute()
Definition: actions.cpp:941
Computes the statistics of a battle between an attacker and a defender unit.
Definition: attack.hpp:167
const battle_context_unit_stats & get_defender_stats() const
This method returns the statistics of the defender.
Definition: attack.hpp:199
const combatant & get_attacker_combatant(const combatant *prev_def=nullptr)
Get the simulation results.
Definition: attack.cpp:441
const battle_context_unit_stats & get_attacker_stats() const
This method returns the statistics of the attacker.
Definition: attack.hpp:193
const combatant & get_defender_combatant(const combatant *prev_def=nullptr)
Definition: attack.cpp:448
team & get_team(int i)
Definition: game_board.hpp:92
virtual const unit_map & units() const override
Definition: game_board.hpp:107
virtual const gamemap & map() const override
Definition: game_board.hpp:97
int w() const
Effective map width.
Definition: map.hpp:50
int h() const
Effective map height.
Definition: map.hpp:53
bool on_board(const map_location &loc) const
Tell if a location is on the map.
Definition: map.cpp:385
unit_ptr find_if_matches_id(const std::string &unit_id)
Find a unit by id.
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 std::size_t turn, const time_of_day &t)
static config get_recall(const std::string &unit_id, const map_location &loc, const map_location &from)
static config get_recruit(const std::string &type_id, const map_location &loc, const map_location &from)
Exception used to escape form the ai or ui code to playsingle_controller::play_side.
static bool run_in_synced_context_if_not_already(const std::string &commandname, const config &data, action_spectator &spectator=get_default_spectator())
Checks whether we are currently running in a synced context, and if not we enters it.
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:75
int recall_cost() const
Definition: team.hpp:180
int gold() const
Definition: team.hpp:176
recall_list_manager & recall_list()
Definition: team.hpp:201
int turn() const
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:56
unit_iterator find(std::size_t id)
Definition: map.cpp:302
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:1265
A single unit type that the player may recruit.
Definition: types.hpp:43
const std::string & id() const
The id for this unit_type.
Definition: types.hpp:141
This class represents a single unit of a specific type.
Definition: unit.hpp:133
Various functions related to the creation of units (recruits, recalls, and placed units).
variant a_
Definition: function.cpp:818
std::size_t i
Definition: function.cpp:1029
bool incapacitated() const
Check if the unit has been petrified.
Definition: unit.hpp:911
int side() const
The side this unit belongs to.
Definition: unit.hpp:343
const map_location & get_location() const
The current map location this unit is at.
Definition: unit.hpp:1404
int movement_left() const
Gets how far a unit can move, considering the incapacitated flag.
Definition: unit.hpp:1329
bool tiles_adjacent(const map_location &a, const map_location &b)
Function which tells if two locations are adjacent.
Definition: location.cpp:507
Standard logging facilities (interface).
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:278
@ RECRUIT_OK
Recruitment OK, but not at the specified location.
Definition: create.hpp:43
@ RECRUIT_NO_VACANCY
No able leaders are on a keep.
Definition: create.hpp:41
@ RECRUIT_NO_ABLE_LEADER
No leaders exist.
Definition: create.hpp:39
@ RECRUIT_ALTERNATE_LOCATION
No vacant castle tiles around a leader on a keep.
Definition: create.hpp:42
@ RECRUIT_NO_KEEP_LEADER
No leaders able to recall/recruit the given unit/type.
Definition: create.hpp:40
@ RECRUIT_NO_LEADER
Definition: create.hpp:38
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:408
std::size_t move_unit_and_record(const std::vector< map_location > &steps, bool continued_move, bool *interrupted)
Wrapper around the other overload.
Definition: move.cpp:1429
A small explanation about what's going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:59
bool simulated_attack(const map_location &attacker_loc, const map_location &defender_loc, double attacker_hp, double defender_hp)
bool simulated_stopunit(const map_location &unit_location, bool remove_movement, bool remove_attacks)
std::shared_ptr< recruit_result > recruit_result_ptr
Definition: game_info.hpp:84
bool simulated_move(int side, const map_location &from, const map_location &to, int steps, map_location &unit_location)
bool simulated_recruit(int side, const unit_type *u, const map_location &recruit_location)
void sim_gamestate_changed(action_result *result, bool gamestate_changed)
Definition: actions.cpp:1108
bool simulated_synced_command()
static lg::log_domain log_ai_actions("ai/actions")
std::shared_ptr< attack_result > attack_result_ptr
Definition: game_info.hpp:82
std::shared_ptr< stopunit_result > stopunit_result_ptr
Definition: game_info.hpp:87
std::shared_ptr< synced_command_result > synced_command_result_ptr
Definition: game_info.hpp:88
int side_number
Definition: game_info.hpp:40
std::shared_ptr< move_result > move_result_ptr
Definition: game_info.hpp:85
std::shared_ptr< recall_result > recall_result_ptr
Definition: game_info.hpp:83
bool simulated_recall(int side, const std::string &unit_id, const map_location &recall_location)
static void execute_or_check(action_result &action, bool execute)
Definition: actions.cpp:978
plain_route a_star_search(const map_location &src, const map_location &dst, double stop_at, const cost_calculator &calc, const std::size_t width, const std::size_t height, const teleport_map *teleports, bool border)
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:251
::tod_manager * tod_manager
Definition: resources.cpp:29
game_board * gameboard
Definition: resources.cpp:20
bool simulation_
Definition: resources.cpp:35
actions::undo_list * undo_stack
Definition: resources.cpp:32
auto * find(Container &container, const Value &value)
Convenience wrapper for using find on a container without needing to comare to end()
Definition: general.hpp:140
std::shared_ptr< const unit > unit_const_ptr
Definition: ptr.hpp:27
static config unit_name(const unit *u)
Definition: reports.cpp:161
Implement simulated actions.
int attack_num
Index into unit->attacks() or -1 for none.
Definition: attack.hpp:53
double average_hp(unsigned int healing=0) const
What's the average hp (weighted average of hp_dist).
Encapsulates the map of the game.
Definition: location.hpp:45
bool valid() const
Definition: location.hpp:110
int wml_y() const
Definition: location.hpp:184
static const map_location & null_location()
Definition: location.hpp:102
int wml_x() const
Definition: location.hpp:183
Structure which holds a single route between one location and another.
Definition: pathfind.hpp:133
bool valid() const
Definition: map.hpp:273
static map_location::direction s
unit_type_data unit_types
Definition: types.cpp:1500
Various functions that implement the undoing (and redoing) of in-game commands.