The Battle for Wesnoth  1.15.0-dev
actions.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2018 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 "log.hpp"
43 #include "map/map.hpp"
44 #include "mouse_handler_base.hpp"
45 #include "pathfind/teleport.hpp"
46 #include "play_controller.hpp"
48 #include "preferences/game.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/ptr.hpp"
55 #include "units/unit.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)
71  , side_(side)
72  , status_(AI_ACTION_SUCCESS)
73  , is_execution_(false)
74  , is_gamestate_changed_(false)
75 {
76 }
77 
79 {
81  // Demotes to DBG "unchecked result" warning
82  DBG_AI_ACTIONS << "Return value of AI ACTION was not checked." << std::endl;
83  }
84 }
85 
87 {
89 }
90 
92 {
94 }
95 
97 {
98  is_execution_ = true;
100  check_before();
101 
102  if(is_success()) {
103  try {
104  do_execute();
105  } catch(const return_to_play_side_exception&) {
106  if(!is_ok()) {
107  DBG_AI_ACTIONS << "Return value of AI ACTION was not checked." << std::endl;
108  } // Demotes to DBG "unchecked result" warning
109 
110  throw;
111  }
112  }
113 
114  if(is_success()) {
115  check_after();
116  }
117 
118  is_execution_ = false;
119 }
120 
122 {
123  return_value_checked_ = false;
124  is_gamestate_changed_ = false;
127 }
128 
130 {
131  return is_gamestate_changed_;
132 }
133 
135 {
136  return_value_checked_ = true;
137  return is_success();
138 }
139 
140 void action_result::set_error(int error_code, bool log_as_error)
141 {
142  status_ = error_code;
143  if(is_execution()) {
144  if(log_as_error) {
145  ERR_AI_ACTIONS << "Error #" << error_code << " (" << actions::get_error_name(error_code) << ") in " << do_describe();
146  } else {
147  LOG_AI_ACTIONS << "Error #" << error_code << " (" << actions::get_error_name(error_code) << ") in " << do_describe();
148  }
149  } else {
150  LOG_AI_ACTIONS << "Error #" << error_code << " (" << actions::get_error_name(error_code) << ") when checking " << do_describe();
151  }
152 }
153 
155 {
156  is_gamestate_changed_ = true;
157 }
158 
160 {
161  return status_;
162 }
163 
165 {
167 }
168 
170 {
171  return is_execution_;
172 }
173 
175 {
177 }
178 
180 {
182 }
183 
184 // attack_result
186  const map_location& attacker_loc,
187  const map_location& defender_loc,
188  int attacker_weapon,
189  double aggression,
190  const unit_advancements_aspect& advancements)
191  : action_result(side)
192  , attacker_loc_(attacker_loc)
193  , defender_loc_(defender_loc)
194  , attacker_weapon_(attacker_weapon)
195  , aggression_(aggression)
196  , advancements_(advancements)
197 {
198 }
199 
201 {
202  LOG_AI_ACTIONS << " check_before " << *this << std::endl;
205 
206  if(attacker == resources::gameboard->units().end()) {
207  LOG_AI_ACTIONS << "attempt to attack without attacker\n";
209  return;
210  }
211 
212  if(defender == resources::gameboard->units().end()) {
213  LOG_AI_ACTIONS << "attempt to attack without defender\n";
215  return;
216  }
217 
218  if(attacker->incapacitated()) {
219  LOG_AI_ACTIONS << "attempt to attack with unit that is petrified\n";
221  return;
222  }
223 
224  if(defender->incapacitated()) {
225  LOG_AI_ACTIONS << "attempt to attack unit that is petrified\n";
227  return;
228  }
229 
230  if(!attacker->attacks_left()) {
231  LOG_AI_ACTIONS << "attempt to attack with no attacks left\n";
233  return;
234  }
235 
236  if(attacker->side() != get_side()) {
237  LOG_AI_ACTIONS << "attempt to attack with not own unit\n";
239  return;
240  }
241 
242  if(!get_my_team().is_enemy(defender->side())) {
243  LOG_AI_ACTIONS << "attempt to attack unit that is not enemy\n";
245  return;
246  }
247 
248  if(attacker_weapon_ != -1) {
249  if((attacker_weapon_ < 0) || (attacker_weapon_ >= static_cast<int>(attacker->attacks().size()))) {
250  LOG_AI_ACTIONS << "invalid weapon selection for the attacker\n";
252  return;
253  }
254  }
255 
257  LOG_AI_ACTIONS << "attacker and defender not adjacent\n";
259  return;
260  }
261 }
262 
264 {
265 }
266 
267 std::string attack_result::do_describe() const
268 {
269  std::stringstream s;
270  s << "attack by side ";
271  s << get_side();
272  s << " from location " << attacker_loc_;
273  s << " to location " << defender_loc_;
274  s << " using weapon " << attacker_weapon_;
275  s << " with aggression " << aggression_;
276  s << std::endl;
277  return s.str();
278 }
279 
281 {
282  LOG_AI_ACTIONS << "start of execution of: " << *this << std::endl;
283 
284  // Stop the user from issuing any commands while the unit is attacking
285  const events::command_disabler disable_commands;
286 
287  // @note: yes, this is a decision done here. It's that way because we want to allow a simpler attack 'with whatever
288  // weapon is considered best', and because we want to allow the defender to pick it's weapon. That's why aggression
289  // is needed. a cleaner solution is needed.
291 
292  int attacker_weapon = bc.get_attacker_stats().attack_num;
293  int defender_weapon = bc.get_defender_stats().attack_num;
294 
295  if(attacker_weapon < 0) {
297  return;
298  }
299 
302 
304  bool gamestate_changed = simulated_attack(
309  );
310 
311  sim_gamestate_changed(this, gamestate_changed);
312 
313  return;
314  }
315 
316  // FIXME: find a way to 'ask' the ai which advancement should be chosen from synced_commands.cpp .
317  if(!synced_context::is_synced()) // RAII block for set_scontext_synced
318  {
319  wb::real_map rm;
320  // we don't use synced_context::run_in_synced_context because that wouldn't allow us to pass advancements_
325  attacker_weapon,
326  defender_weapon,
327  a_->type_id(),
328  d_->type_id(),
329  a_->level(),
330  d_->level(),
333  )
334  );
335 
336  set_scontext_synced sync;
337  attack_unit_and_advance(attacker_loc_, defender_loc_, attacker_weapon, defender_weapon, true, advancements_);
338 
341 
342  sync.do_final_checkup();
343  } else {
344  attack_unit_and_advance(attacker_loc_, defender_loc_, attacker_weapon, defender_weapon, true, advancements_);
345  }
346 
348 
349  // start of ugly hack. @todo 1.9 rework that via extended event system
350  // until event system is reworked, we note the attack this way
352  // end of ugly hack
353 
354  try {
356  } catch(...) {
357  if(!is_ok()) {
358  DBG_AI_ACTIONS << "Return value of AI ACTION was not checked." << std::endl;
359  } // Demotes to DBG "unchecked result" warning
360  throw;
361  }
362 }
363 
365 {
366 }
367 
368 // move_result
370  side_number side, const map_location& from, const map_location& to, bool remove_movement, bool unreach_is_ok)
371  : action_result(side)
372  , from_(from)
373  , to_(to)
374  , remove_movement_(remove_movement)
375  , route_()
376  , unit_location_(from)
377  , unreach_is_ok_(unreach_is_ok)
378  , has_ambusher_(false)
379  , has_interrupted_teleport_(false)
380 {
381 }
382 
384 {
386  if(un == resources::gameboard->units().end()) {
388  return nullptr;
389  }
390 
391  const unit* u = &*un;
392  if(u->side() != get_side()) {
394  return nullptr;
395  }
396 
397  if(u->incapacitated()) {
399  return nullptr;
400  }
401 
402  return u;
403 }
404 
406 {
407  if(from_ == to_) {
408  if(!remove_movement_ || (un.movement_left() == 0)) {
410  return false;
411  }
412 
413  return true;
414  }
415 
416  if(un.movement_left() == 0) {
418  return false;
419  }
420 
421  if(!to_.valid()) {
423  return false;
424  }
425 
426  team& my_team = get_my_team();
427  const pathfind::shortest_path_calculator calc(un, my_team, resources::gameboard->teams(), resources::gameboard->map());
428 
429  // allowed teleports
430  pathfind::teleport_map allowed_teleports =
431  pathfind::get_teleport_locations(un, my_team, true); ///@todo 1.9: see_all -> false
432 
433  // do an A*-search
434  route_.reset(new pathfind::plain_route(
435  pathfind::a_star_search(un.get_location(), to_, 10000.0, calc, resources::gameboard->map().w(), resources::gameboard->map().h(), &allowed_teleports)));
436 
437  if(route_->steps.empty()) {
439  return false;
440  }
441 
442  return true;
443 }
444 
446 {
447  LOG_AI_ACTIONS << " check_before " << *this << std::endl;
448  const unit* u = get_unit();
449  if(!u) {
450  return;
451  }
452 
453  if(!test_route(*u)) {
454  return;
455  }
456 }
457 
459 {
460  return unit_location_;
461 }
462 
464 {
465  if(has_ambusher_) {
466  set_error(E_AMBUSHED, false);
467  return;
468  }
469 
472  return;
473  }
474  ///@todo 1.9 add 'new units spotted' failure mode
475 
476  if(!unreach_is_ok_ && unit_location_ != to_) {
478  return;
479  }
480 }
481 
482 std::string move_result::do_describe() const
483 {
484  std::stringstream s;
485  if(remove_movement_) {
486  s << "full move by side ";
487  } else {
488  s << "partial move by side ";
489  }
490 
491  s << get_side();
492  s << " from location " << from_;
493  s << " to location " << to_;
494  s << std::endl;
495  return s.str();
496 }
497 
499 {
500  LOG_AI_ACTIONS << "start of execution of: " << *this << std::endl;
501  assert(is_success());
502 
504  bool gamestate_changed = false;
505  if(from_ != to_) {
506  int step = route_->steps.size();
507  gamestate_changed = simulated_move(get_side(), from_, to_, step, unit_location_);
508  } else {
509  assert(remove_movement_);
510  }
511 
513  if(remove_movement_ && un->movement_left() > 0 && unit_location_ == to_) {
514  gamestate_changed = simulated_stopunit(unit_location_, true, false);
515  }
516 
517  sim_gamestate_changed(this, gamestate_changed);
518 
519  return;
520  }
521 
522  ::actions::move_unit_spectator move_spectator(resources::gameboard->units());
523  move_spectator.set_unit(resources::gameboard->units().find(from_));
524 
525  if(from_ != to_) {
526  std::size_t num_steps = ::actions::move_unit_and_record(
527  /*std::vector<map_location> steps*/ route_->steps,
528  /*::actions::undo_list* undo_stack*/ nullptr,
529  /*bool continue_move*/ true, ///@todo 1.9 set to false after implementing interrupt awareness
530  /*bool show_move*/ !preferences::skip_ai_moves(),
531  /*bool* interrupted*/ nullptr,
532  /*::actions::move_unit_spectator* move_spectator*/ &move_spectator);
533 
534  if(num_steps > 0) {
536  } else if(move_spectator.get_ambusher().valid()) {
537  // Unlikely, but some types of strange WML (or bad pathfinding)
538  // could cause an ambusher to be found without moving.
540  }
541  } else {
542  assert(remove_movement_);
543  }
544 
545  if(move_spectator.get_unit().valid()) {
546  unit_location_ = move_spectator.get_unit()->get_location();
547  if(remove_movement_ && move_spectator.get_unit()->movement_left() > 0 && unit_location_ == to_) {
548  stopunit_result_ptr stopunit_res =
550 
551  if(!stopunit_res->is_ok()) {
552  set_error(stopunit_res->get_status());
553  }
554 
555  if(stopunit_res->is_gamestate_changed()) {
557  }
558  }
559  } else {
561  }
562 
563  has_ambusher_ = move_spectator.get_ambusher().valid();
565 
566  if(is_gamestate_changed()) {
567  try {
569  } catch(...) {
570  if(!is_ok()) {
571  DBG_AI_ACTIONS << "Return value of AI ACTION was not checked." << std::endl;
572  } // Demotes to DBG "unchecked result" warning
573 
574  throw;
575  }
576  }
577 }
578 
580 {
581 }
582 
583 // recall_result
585  side_number side, const std::string& unit_id, const map_location& where, const map_location& from)
586  : action_result(side)
587  , unit_id_(unit_id)
588  , where_(where)
589  , recall_location_(where)
590  , recall_from_(from)
591  , location_checked_(false)
592 {
593 }
594 
596 {
598  if(!rec) {
600  }
601 
602  return rec;
603 }
604 
606 {
607  if(my_team.gold() < my_team.recall_cost()) {
609  return false;
610  }
611 
612  return true;
613 }
614 
616 {
617  LOG_AI_ACTIONS << " check_before " << *this << std::endl;
618  const team& my_team = get_my_team();
619  const bool location_specified = recall_location_.valid();
620 
621  // Enough gold?
622  if(!test_enough_gold(my_team)) {
623  return;
624  }
625 
626  // Unit available for recalling?
627  const unit_const_ptr& to_recall = get_recall_unit(my_team);
628  if(!to_recall) {
629  return;
630  }
631 
632  // Leader available for recalling?
637  return;
638 
641  return;
642 
645  return;
646 
648  if(location_specified) {
650  return;
651  }
652  FALLTHROUGH; // If the location was not specified, this counts as "OK".
654  location_checked_ = true;
655  }
656 }
657 
659 {
660  if(!resources::gameboard->map().on_board(recall_location_)) {
662  return;
663  }
664 
666  if(unit == resources::gameboard->units().end()) {
668  return;
669  }
670 
671  if(unit->side() != get_side()) {
673  return;
674  }
675 }
676 
677 std::string recall_result::do_describe() const
678 {
679  std::stringstream s;
680  s << "recall by side ";
681  s << get_side();
682  s << " of unit id [" << unit_id_;
683 
685  s << "] on location " << where_;
686  } else {
687  s << "] on any suitable location";
688  }
689 
690  s << std::endl;
691  return s.str();
692 }
693 
695 {
696  LOG_AI_ACTIONS << "start of execution of: " << *this << std::endl;
697  assert(is_success());
698 
699  const events::command_disabler disable_commands;
700 
701  // Assert that recall_location_ has been validated.
702  // This should be implied by is_success() once check_before() has been
703  // called, so this is a guard against future breakage.
704  assert(location_checked_);
705 
707  bool gamestate_changed = simulated_recall(get_side(), unit_id_, recall_location_);
708 
709  sim_gamestate_changed(this, gamestate_changed);
710 
711  return;
712  }
713 
714  // Do the actual recalling.
715  // We ignore possible errors (=unit doesn't exist on the recall list)
716  // because that was the previous behavior.
720 
722  try {
724  } catch(...) {
725  if(!is_ok()) {
726  DBG_AI_ACTIONS << "Return value of AI ACTION was not checked." << std::endl;
727  } // Demotes to DBG "unchecked result" warning
728 
729  throw;
730  }
731 }
732 
734 {
735 }
736 
737 // recruit_result
739  side_number side, const std::string& unit_name, const map_location& where, const map_location& from)
740  : action_result(side)
741  , unit_name_(unit_name)
742  , where_(where)
743  , recruit_location_(where)
744  , recruit_from_(from)
745  , location_checked_(false)
746 {
747 }
748 
749 const unit_type* recruit_result::get_unit_type_known(const std::string& recruit)
750 {
751  const unit_type* type = unit_types.find(recruit);
752  if(!type) {
754  return nullptr;
755  }
756 
757  return type;
758 }
759 
761 {
762  if(my_team.gold() < type.cost()) {
764  return false;
765  }
766 
767  return true;
768 }
769 
771 {
772  LOG_AI_ACTIONS << " check_before " << *this << std::endl;
773  const team& my_team = get_my_team();
774  const bool location_specified = recruit_location_.valid();
775 
776  // Unit type known ?
777  const unit_type* s_type = get_unit_type_known(unit_name_);
778  if(!s_type) {
779  return;
780  }
781 
782  // Enough gold?
783  if(!test_enough_gold(my_team, *s_type)) {
784  return;
785  }
786 
787  // Leader available for recruiting?
792  return;
793 
796  return;
797 
800  return;
801 
803  if(location_specified) {
805  return;
806  }
807  FALLTHROUGH; // If the location was not specified, this counts as "OK".
809  location_checked_ = true;
810  }
811 }
812 
814 {
815  if(!resources::gameboard->map().on_board(recruit_location_)) {
817  return;
818  }
819 
821  if(unit == resources::gameboard->units().end()) {
823  return;
824  }
825 
826  if(unit->side() != get_side()) {
828  return;
829  }
830 }
831 
832 std::string recruit_result::do_describe() const
833 {
834  std::stringstream s;
835  s << "recruitment by side ";
836  s << get_side();
837  s << " of unit type [" << unit_name_;
838 
840  s << "] on location " << where_;
841  } else {
842  s << "] on any suitable location";
843  }
844 
845  s << std::endl;
846  return s.str();
847 }
848 
850 {
851  LOG_AI_ACTIONS << "start of execution of: " << *this << std::endl;
852  assert(is_success());
853 
854  const unit_type* u = unit_types.find(unit_name_);
855  const events::command_disabler disable_commands;
856 
857  // Assert that recruit_location_ has been validated.
858  // This should be implied by is_success() once check_before() has been
859  // called, so this is a guard against future breakage.
860  assert(location_checked_ && u != nullptr);
861 
863  bool gamestate_changed = simulated_recruit(get_side(), u, recruit_location_);
864 
865  sim_gamestate_changed(this, gamestate_changed);
866 
867  return;
868  }
869 
872 
873  // TODO: should we do something to pass use_undo = false in replays and ai moves ?
874  //::actions::recruit_unit(*u, get_side(), recruit_location_, recruit_from_,
875  // !preferences::skip_ai_moves(), false);
876 
878 
879  try {
881  } catch(...) {
882  if(!is_ok()) {
883  DBG_AI_ACTIONS << "Return value of AI ACTION was not checked." << std::endl;
884  } // Demotes to DBG "unchecked result" warning
885 
886  throw;
887  }
888 }
889 
891 {
892 }
893 
894 // stopunit_result
896  side_number side, const map_location& unit_location, bool remove_movement, bool remove_attacks)
897  : action_result(side)
898  , unit_location_(unit_location)
899  , remove_movement_(remove_movement)
900  , remove_attacks_(remove_attacks)
901 {
902 }
903 
905 {
907  if(un == resources::gameboard->units().end()) {
909  return nullptr;
910  }
911 
912  const unit* u = &*un;
913  if(u->side() != get_side()) {
915  return nullptr;
916  }
917 
918  if(u->incapacitated()) {
920  return nullptr;
921  }
922 
923  return u;
924 }
925 
927 {
928  LOG_AI_ACTIONS << " check_before " << *this << std::endl;
929 
930  if(!get_unit()) {
931  return;
932  }
933 }
934 
936 {
938  if(un == resources::gameboard->units().end()) {
940  return;
941  }
942 
943  if(remove_movement_ && un->movement_left() != 0) {
945  return;
946  }
947 
948  if(remove_attacks_ && un->attacks_left() != 0) {
950  return;
951  }
952 }
953 
954 std::string stopunit_result::do_describe() const
955 {
956  std::stringstream s;
957  s << " stopunit by side ";
958  s << get_side();
959 
960  if(remove_movement_) {
961  s << " : remove movement ";
962  }
963 
965  s << "and ";
966  }
967 
968  if(remove_attacks_) {
969  s << " remove attacks ";
970  }
971 
972  s << "from unit on location " << unit_location_;
973  s << std::endl;
974  return s.str();
975 }
976 
978 {
979  LOG_AI_ACTIONS << "start of execution of: " << *this << std::endl;
980  assert(is_success());
982 
985 
986  sim_gamestate_changed(this, gamestate_changed);
987 
988  return;
989  }
990 
991  try {
992  if(remove_movement_) {
993  un->remove_movement_ai();
996  }
997 
998  if(remove_attacks_) {
999  un->remove_attacks_ai();
1001  manager::get_singleton().raise_gamestate_changed(); // to be on the safe side
1002  }
1003  } catch(...) {
1004  if(!is_ok()) {
1005  DBG_AI_ACTIONS << "Return value of AI ACTION was not checked." << std::endl;
1006  } // Demotes to DBG "unchecked result" warning
1007 
1008  throw;
1009  }
1010 }
1011 
1013 {
1014 }
1015 
1016 // synced_command_result
1018  side_number side, const std::string& lua_code, const map_location& location)
1019  : action_result(side)
1020  , lua_code_(lua_code)
1021  , location_(location)
1022 {
1023 }
1024 
1026 {
1027  LOG_AI_ACTIONS << " check_before " << *this << std::endl;
1028 }
1029 
1031 {
1032 }
1033 
1035 {
1036  std::stringstream s;
1037  s << " synced_command by side ";
1038  s << get_side();
1039  s << std::endl;
1040  return s.str();
1041 }
1042 
1044 {
1046  bool gamestate_changed = simulated_synced_command();
1047 
1048  sim_gamestate_changed(this, gamestate_changed);
1049 
1050  return;
1051  }
1052 
1053  LOG_AI_ACTIONS << "start of execution of: " << *this << std::endl;
1054  assert(is_success());
1055 
1056  std::stringstream s;
1058  s << "local x1 = " << location_.wml_x() << " local y1 = " << location_.wml_y() << " ";
1059  }
1060 
1061  s << lua_code_;
1062 
1063  try {
1066  } catch(...) {
1067  if(!is_ok()) {
1068  DBG_AI_ACTIONS << "Return value of AI ACTION was not checked." << std::endl;
1069  } // Demotes to DBG "unchecked result" warning
1070 
1071  throw;
1072  }
1073 }
1074 
1076 {
1077 }
1078 
1079 // =======================================================================
1080 // STATELESS INTERFACE TO AI ACTIONS
1081 // =======================================================================
1082 
1083 static void execute_or_check(action_result& action, bool execute)
1084 {
1085  if(execute) {
1086  action.execute();
1087  } else {
1088  action.check_before();
1089  }
1090 }
1091 
1093  bool execute,
1094  const map_location& attacker_loc,
1095  const map_location& defender_loc,
1096  int attacker_weapon,
1097  double aggression,
1098  const unit_advancements_aspect& advancements)
1099 {
1100  attack_result_ptr action(new attack_result(side, attacker_loc, defender_loc, attacker_weapon, aggression, advancements));
1101  execute_or_check(*action, execute);
1102  return action;
1103 }
1104 
1106  bool execute,
1107  const map_location& from,
1108  const map_location& to,
1109  bool remove_movement,
1110  bool unreach_is_ok)
1111 {
1112  move_result_ptr action(new move_result(side, from, to, remove_movement, unreach_is_ok));
1113  execute_or_check(*action, execute);
1114  return action;
1115 }
1116 
1118  side_number side, bool execute, const std::string& unit_id, const map_location& where, const map_location& from)
1119 {
1120  recall_result_ptr action(new recall_result(side, unit_id, where, from));
1121  execute_or_check(*action, execute);
1122  return action;
1123 }
1124 
1126  bool execute,
1127  const std::string& unit_name,
1128  const map_location& where,
1129  const map_location& from)
1130 {
1131  recruit_result_ptr action(new recruit_result(side, unit_name, where, from));
1132  execute_or_check(*action, execute);
1133  return action;
1134 }
1135 
1137  side_number side, bool execute, const map_location& unit_location, bool remove_movement, bool remove_attacks)
1138 {
1139  stopunit_result_ptr action(new stopunit_result(side, unit_location, remove_movement, remove_attacks));
1140  execute_or_check(*action, execute);
1141  return action;
1142 }
1143 
1145  side_number side, bool execute, const std::string& lua_code, const map_location& location)
1146 {
1147  synced_command_result_ptr action(new synced_command_result(side, lua_code, location));
1148  execute_or_check(*action, execute);
1149  return action;
1150 }
1151 
1152 std::map<int, std::string> actions::error_names_ {
1153  { action_result::AI_ACTION_SUCCESS, "action_result::AI_ACTION_SUCCESS" },
1154  { action_result::AI_ACTION_STARTED, "action_result::AI_ACTION_STARTED" },
1155  { action_result::AI_ACTION_FAILURE, "action_result::AI_ACTION_FAILURE" },
1156  { attack_result::E_EMPTY_ATTACKER, "attack_result::E_EMPTY_ATTACKER" },
1157  { attack_result::E_EMPTY_DEFENDER, "attack_result::E_EMPTY_DEFENDER" },
1158  { attack_result::E_INCAPACITATED_ATTACKER, "attack_result::E_INCAPACITATED_ATTACKER" },
1159  { attack_result::E_INCAPACITATED_DEFENDER, "attack_result::E_INCAPACITATED_DEFENDER" },
1160  { attack_result::E_NOT_OWN_ATTACKER, "attack_result::E_NOT_OWN_ATTACKER" },
1161  { attack_result::E_NOT_ENEMY_DEFENDER, "attack_result::E_NOT_ENEMY_DEFENDER" },
1162  { attack_result::E_NO_ATTACKS_LEFT, "attack_result::E_NO_ATTACKS_LEFT" },
1163  { attack_result::E_WRONG_ATTACKER_WEAPON, "attack_result::E_WRONG_ATTACKER_WEAPON" },
1164  { attack_result::E_UNABLE_TO_CHOOSE_ATTACKER_WEAPON, "attack_result::E_UNABLE_TO_CHOOSE_ATTACKER_WEAPON" },
1165  { attack_result::E_ATTACKER_AND_DEFENDER_NOT_ADJACENT," attack_result::E_ATTACKER_AND_DEFENDER_NOT_ADJACE" },
1166  { move_result::E_EMPTY_MOVE, "move_result::E_EMPTY_MOVE" },
1167  { move_result::E_NO_UNIT, "move_result::E_NO_UNIT" },
1168  { move_result::E_NOT_OWN_UNIT, "move_result::E_NOT_OWN_UNIT" },
1169  { move_result::E_INCAPACITATED_UNIT, "move_result::E_INCAPACITATED_UNIT" },
1170  { move_result::E_AMBUSHED, "move_result::E_AMBUSHED" },
1171  { move_result::E_FAILED_TELEPORT, "move_result::E_FAILED_TELEPORT" },
1172  { move_result::E_NOT_REACHED_DESTINATION, "move_result::E_NOT_REACHED_DESTINATION" },
1173  { move_result::E_NO_ROUTE, "move_result::E_NO_ROUTE" },
1174  { recall_result::E_NOT_AVAILABLE_FOR_RECALLING, "recall_result::E_NOT_AVAILABLE_FOR_RECALLING" },
1175  { recall_result::E_NO_GOLD, "recall_result::E_NO_GOLD" },
1176  { recall_result::E_NO_LEADER," recall_result::E_NO_LEADER" },
1177  { recall_result::E_LEADER_NOT_ON_KEEP, "recall_result::E_LEADER_NOT_ON_KEEP" },
1178  { recall_result::E_BAD_RECALL_LOCATION, "recall_result::E_BAD_RECALL_LOCATION" },
1179  { recruit_result::E_NOT_AVAILABLE_FOR_RECRUITING, "recruit_result::E_NOT_AVAILABLE_FOR_RECRUITING" },
1180  { recruit_result::E_UNKNOWN_OR_DUMMY_UNIT_TYPE, "recruit_result::E_UNKNOWN_OR_DUMMY_UNIT_TYPE" },
1181  { recruit_result::E_NO_GOLD, "recruit_result::E_NO_GOLD" },
1182  { recruit_result::E_NO_LEADER, "recruit_result::E_NO_LEADER" },
1183  { recruit_result::E_LEADER_NOT_ON_KEEP, "recruit_result::E_LEADER_NOT_ON_KEEP" },
1184  { recruit_result::E_BAD_RECRUIT_LOCATION, "recruit_result::E_BAD_RECRUIT_LOCATION" },
1185  { stopunit_result::E_NO_UNIT, "stopunit_result::E_NO_UNIT" },
1186  { stopunit_result::E_NOT_OWN_UNIT, "stopunit_result::E_NOT_OWN_UNIT" },
1187  { stopunit_result::E_INCAPACITATED_UNIT, "stopunit_result::E_INCAPACITATED_UNIT" },
1188 };
1189 
1190 const std::string& actions::get_error_name(int error_code)
1191 {
1192  std::map<int, std::string>::iterator i = error_names_.find(error_code);
1193  if(i == error_names_.end()) {
1194  ERR_AI_ACTIONS << "error name not available for error #" << error_code << std::endl;
1195  i = error_names_.find(-1);
1196  assert(i != error_names_.end());
1197  }
1198 
1199  return i->second;
1200 }
1201 
1202 void sim_gamestate_changed(action_result* result, bool gamestate_changed)
1203 {
1204  if(gamestate_changed) {
1205  result->set_gamestate_changed();
1207  }
1208 }
1209 
1210 } // end of namespace ai
1211 
1212 std::ostream& operator<<(std::ostream& s, const ai::attack_result& r)
1213 {
1214  s << r.do_describe();
1215  return s;
1216 }
1217 
1218 std::ostream& operator<<(std::ostream& s, const ai::move_result& r)
1219 {
1220  s << r.do_describe();
1221  return s;
1222 }
1223 
1224 std::ostream& operator<<(std::ostream& s, const ai::recall_result& r)
1225 {
1226  s << r.do_describe();
1227  return s;
1228 }
1229 
1230 std::ostream& operator<<(std::ostream& s, const ai::recruit_result& r)
1231 {
1232  s << r.do_describe();
1233  return s;
1234 }
1235 
1236 std::ostream& operator<<(std::ostream& s, const ai::stopunit_result& r)
1237 {
1238  s << r.do_describe();
1239  return s;
1240 }
1241 
1242 std::ostream& operator<<(std::ostream& s, const ai::synced_command_result& r)
1243 {
1244  s << r.do_describe();
1245  return s;
1246 }
bool unreach_is_ok_
Definition: actions.hpp:209
play_controller * controller
Definition: resources.cpp:21
map_location recruit_location_
Definition: actions.hpp:274
boost::intrusive_ptr< const unit > unit_const_ptr
Definition: ptr.hpp:30
map_location recruit_from_
Definition: actions.hpp:275
virtual void do_init_for_execution()
Definition: actions.cpp:1012
virtual void do_check_after()
Definition: actions.cpp:935
bool simulated_synced_command()
::tod_manager * tod_manager
Definition: resources.cpp:29
bool has_ambusher_
Definition: actions.hpp:210
int h() const
Effective map height.
Definition: map.hpp:93
virtual void do_execute()
Definition: actions.cpp:280
void check_before()
Definition: actions.cpp:91
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:1275
game_info & get_info() const
Definition: actions.cpp:174
bool remove_movement_
Definition: actions.hpp:206
map_location recall_from_
Definition: actions.hpp:242
int get_status() const
Definition: actions.cpp:159
unit_const_ptr get_recall_unit(const team &my_team)
Definition: actions.cpp:595
bool is_success() const
Definition: actions.cpp:164
virtual const unit_map & units() const override
Definition: game_board.hpp:114
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:1092
virtual void do_check_before()
Definition: actions.cpp:445
This class represents a single unit of a specific type.
Definition: unit.hpp:99
const battle_context_unit_stats & get_defender_stats() const
This method returns the statistics of the defender.
Definition: attack.hpp:204
static std::map< int, std::string > error_names_
Definition: actions.hpp:444
No leaders exist.
Definition: create.hpp:38
void do_final_checkup(bool dont_throw=false)
const unit * get_unit()
Definition: actions.cpp:383
static manager & get_singleton()
Definition: manager.hpp:152
virtual void do_execute()=0
void raise_gamestate_changed()
Notifies all observers of &#39;ai_gamestate_changed&#39; event.
Definition: manager.cpp:431
map_location unit_location_
Definition: actions.hpp:208
const unit_map::const_iterator & get_unit() const
get new location of moved unit
Definition: move.cpp:95
virtual void do_init_for_execution()
Definition: actions.cpp:1075
Various functions that implement attacks and attack calculations.
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:446
virtual void do_check_after()
Definition: actions.cpp:463
bool test_route(const unit &un)
Definition: actions.cpp:405
Managing the AI-Game interaction - AI actions and their results.
map_location recall_location_
Definition: actions.hpp:241
game_info & get_active_ai_info_for_side(side_number side)
Gets AI info for active AI of the given side.
Definition: manager.cpp:732
virtual void do_init_for_execution()
Definition: actions.cpp:579
const map_location & where_
Definition: actions.hpp:273
const std::string & unit_id_
Definition: actions.hpp:239
double average_hp(unsigned int healing=0) const
What&#39;s the average hp (weighted average of hp_dist).
const unit_type * get_unit_type_known(const std::string &recruit)
Definition: actions.cpp:749
virtual std::string do_describe() const
Definition: actions.cpp:677
virtual void do_init_for_execution()
Definition: actions.cpp:890
virtual const gamemap & map() const override
Definition: game_board.hpp:109
int wml_x() const
Definition: location.hpp:157
virtual std::string do_describe() const
Definition: actions.cpp:1034
virtual void do_check_before()
Definition: actions.cpp:926
unit_type_data unit_types
Definition: types.cpp:1452
Recruitment OK, but not at the specified location.
Definition: create.hpp:42
std::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:1243
const unit_advancements_aspect & advancements_
Definition: actions.hpp:169
int get_side() const
Definition: actions.hpp:89
virtual void do_execute()
Definition: actions.cpp:498
const map_location from_
Definition: actions.hpp:204
void check_victory()
Checks to see if a side has won.
virtual const map_location & get_unit_location() const
Definition: actions.cpp:458
static config unit_name(const unit *u)
Definition: reports.cpp:129
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:319
virtual std::string do_describe() const
Definition: actions.cpp:482
void init_for_execution()
Definition: actions.cpp:121
const unit_map::const_iterator & get_failed_teleport() const
get the location of a failed teleport
Definition: move.cpp:77
void add_synced_command(const std::string &name, const config &command)
Definition: replay.cpp:244
bool has_interrupted_teleport_
Definition: actions.hpp:211
std::shared_ptr< pathfind::plain_route > route_
Definition: actions.hpp:207
std::ostream & operator<<(std::ostream &s, const ai::candidate_action &ca)
Definition: rca.cpp:123
const unit_map::const_iterator & get_ambusher() const
get the location of an ambusher
Definition: move.cpp:71
A single unit type that the player may recruit.
Definition: types.hpp:42
int gold() const
Definition: team.hpp:188
const combatant & get_defender_combatant(const combatant *prev_def=nullptr)
Definition: attack.cpp:461
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:44
A small explanation about what&#39;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:104
virtual void do_init_for_execution()
Definition: actions.cpp:733
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:1117
bool simulated_recall(int side, const std::string &unit_id, const map_location &recall_location)
unit_ptr find_if_matches_id(const std::string &unit_id)
Find a unit by id.
int cost() const
Definition: types.hpp:158
static lg::log_domain log_ai_actions("ai/actions")
virtual void do_check_after()
Definition: actions.cpp:813
#define ERR_AI_ACTIONS
Definition: actions.cpp:64
Structure which holds a single route between one location and another.
Definition: pathfind.hpp:131
bool return_value_checked_
Definition: actions.hpp:119
int wml_y() const
Definition: location.hpp:158
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.
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
bool valid() const
Definition: location.hpp:93
virtual void do_check_after()
Definition: actions.cpp:658
virtual std::string do_describe() const
Definition: actions.cpp:832
game_board * gameboard
Definition: resources.cpp:20
virtual void do_execute()
Definition: actions.cpp:1043
bool location_checked_
Definition: actions.hpp:243
bool test_enough_gold(const team &my_team, const unit_type &type)
Definition: actions.cpp:760
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:1017
std::unique_ptr< recruit_result > recruit_result_ptr
Definition: game_info.hpp:83
std::unique_ptr< recall_result > recall_result_ptr
Definition: game_info.hpp:82
const std::string & id() const
The id for this unit_type.
Definition: types.hpp:139
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:168
replay * recorder
Definition: resources.cpp:28
virtual void do_check_after()
Definition: actions.cpp:263
virtual void do_execute()
Definition: actions.cpp:694
const bool remove_movement_
Definition: actions.hpp:301
friend void sim_gamestate_changed(action_result *result, bool gamestate_changed)
Definition: actions.cpp:1202
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:185
const std::string & unit_name_
Definition: actions.hpp:272
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)
virtual void do_check_before()
Definition: actions.cpp:770
int recall_cost() const
Definition: team.hpp:192
Encapsulates the map of the game.
Definition: location.hpp:42
const bool remove_attacks_
Definition: actions.hpp:302
virtual void do_check_before()
Definition: actions.cpp:615
unit_iterator find(std::size_t id)
Definition: map.cpp:311
virtual std::string do_describe() const
Definition: actions.cpp:954
Various functions related to the creation of units (recruits, recalls, and placed units)...
bool tiles_adjacent(const map_location &a, const map_location &b)
Function which tells if two locations are adjacent.
Definition: location.cpp:557
const map_location & defender_loc_
Definition: actions.hpp:166
int w() const
Effective map width.
Definition: map.hpp:90
void check_after()
Definition: actions.cpp:86
std::size_t i
Definition: function.cpp:933
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:267
No able leaders are on a keep.
Definition: create.hpp:40
bool simulated_stopunit(const map_location &unit_location, bool remove_movement, bool remove_attacks)
std::unique_ptr< synced_command_result > synced_command_result_ptr
Definition: game_info.hpp:87
std::unique_ptr< attack_result > attack_result_ptr
Definition: game_info.hpp:81
static map_location::DIRECTION s
std::unique_ptr< stopunit_result > stopunit_result_ptr
Definition: game_info.hpp:86
bool test_enough_gold(const team &my_team)
Definition: actions.cpp:605
std::set< map_location > recent_attacks
hack.
Definition: game_info.hpp:115
void set_gamestate_changed()
Definition: actions.cpp:154
virtual void do_check_before()
Definition: actions.cpp:1025
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:1125
stopunit_result(side_number side, const map_location &unit_location, bool remove_movement, bool remove_attacks)
Definition: actions.cpp:895
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:1144
bool is_gamestate_changed_
Definition: actions.hpp:130
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:404
bool simulation_
Definition: resources.cpp:35
virtual void do_check_before()
Definition: actions.cpp:200
variant a_
Definition: function.cpp:722
const map_location & unit_location_
Definition: actions.hpp:300
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:1136
void set_error(int error_code, bool log_as_error=true)
Definition: actions.cpp:140
#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
team & get_my_team() const
Definition: actions.cpp:179
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:205
static void execute_or_check(action_result &action, bool execute)
Definition: actions.cpp:1083
bool is_execution() const
Definition: actions.cpp:169
void maybe_throw_return_to_play_side() const
bool simulated_move(int side, const map_location &from, const map_location &to, int steps, map_location &unit_location)
bool is_gamestate_changed() const
Definition: actions.cpp:129
const map_location & get_location() const
The current map location this unit is at.
Definition: unit.hpp:1174
#define LOG_AI_ACTIONS
Definition: actions.cpp:62
std::unique_ptr< move_result > move_result_ptr
Definition: game_info.hpp:84
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
static const map_location & null_location()
Definition: location.hpp:85
virtual void do_execute()
Definition: actions.cpp:977
virtual std::string do_describe() const =0
bool incapacitated() const
Check if the unit has been petrified.
Definition: unit.hpp:784
static config get_recruit(const std::string &type_id, const map_location &loc, const map_location &from)
int turn() const
int side() const
The side this unit belongs to.
Definition: unit.hpp:265
int side_number
Definition: game_info.hpp:39
virtual std::string do_describe() const
Definition: actions.cpp:267
recall_result(side_number side, const std::string &unit_id, const map_location &where, const map_location &from)
Definition: actions.cpp:584
const map_location where_
Definition: actions.hpp:240
Ensures that the real unit map is active for the duration of the struct&#39;s life.
Definition: manager.hpp:282
const unit * get_unit()
Definition: actions.cpp:904
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)
bool valid() const
Definition: map.hpp:276
const map_location & attacker_loc_
Definition: actions.hpp:165
move_result(side_number side, const map_location &from, const map_location &to, bool remove_movement, bool unreach_is_ok)
Definition: actions.cpp:369
static bool is_synced()
virtual void do_check_after()
Definition: actions.cpp:1030
const battle_context_unit_stats & get_attacker_stats() const
This method returns the statistics of the attacker.
Definition: attack.hpp:198
std::string::const_iterator iterator
Definition: tokenizer.hpp:24
int movement_left() const
Gets how far a unit can move, considering the incapacitated flag.
Definition: unit.hpp:1100
recruit_result(side_number side, const std::string &unit_name, const map_location &where, const map_location &from)
Definition: actions.cpp:738
virtual ~action_result()
Definition: actions.cpp:78
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 &#39;from&#39; to location &#39;to&#39;, optionally - doing a partial mov...
Definition: actions.cpp:1105
virtual void do_init_for_execution()
Definition: actions.cpp:364
bool skip_ai_moves()
Definition: game.cpp:749
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:1610
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:1190
const std::vector< map_location > & route_
Definition: move.cpp:289
const map_location & location_
Definition: actions.hpp:320
virtual void do_execute()
Definition: actions.cpp:849
Implement simulated actions.