The Battle for Wesnoth  1.19.0-dev
function_table.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2024
3  by Bartosz Waresiak <dragonking@o2.pl>
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 #include <queue>
17 #include <set>
18 #include <utility>
19 #include <vector>
20 
21 #include "ai/formula/ai.hpp"
24 
25 #include "ai/default/contexts.hpp"
26 
28 #include "attack_prediction.hpp"
29 #include "filesystem.hpp"
30 #include "game_board.hpp"
31 #include "global.hpp"
32 #include "display.hpp"
33 #include "log.hpp"
34 #include "map/label.hpp"
35 #include "map/map.hpp"
36 #include "pathfind/teleport.hpp"
37 #include "replay.hpp"
38 #include "resources.hpp"
39 #include "color.hpp"
40 #include "terrain/filter.hpp"
41 #include "units/unit.hpp"
42 #include "units/types.hpp"
43 #include "pathfind/pathfind.hpp"
44 
45 static lg::log_domain log_formula_ai("ai/engine/fai");
46 #define LOG_AI LOG_STREAM(info, log_formula_ai)
47 #define WRN_AI LOG_STREAM(warn, log_formula_ai)
48 #define ERR_AI LOG_STREAM(err, log_formula_ai)
49 
50 namespace wfl {
51 using ai::formula_ai;
52 
53 namespace {
54 
55 /*
56  * unit adapters let us treat unit and unit_type the same if we want to get access to attacks or movement cost
57  */
58 class unit_adapter {
59  public:
60  unit_adapter(const variant& arg) : unit_type_(), unit_() {
61  auto unit = arg.try_convert<unit_callable>();
62 
63  if (unit) {
64  unit_ = &unit->get_unit();
65  } else {
66  unit_type_ = &(arg.convert_to<unit_type_callable>()->get_unit_type());
67  }
68  }
69 
70  /**
71  * Estimates the damage this unit or unit_type would take from a single strike of an attack.
72  *
73  * Many details aren't taken into account here, for example abilities that are only active
74  * on offense, or on particular terrain. For unit_type, abilities aren't considered at all.
75  */
76  int damage_from(const attack_type& attack) const {
77  if(unit_type_ != nullptr) {
78  std::pair<std::string, std::string> types = attack.damage_type();
79  int res = unit_type_->movement_type().resistance_against(types.first);
80  if(!(types.second).empty()){
81  // max not min, resistance_against() returns the percentage taken, so higher means more damage
82  res = std::max(res, unit_type_->movement_type().resistance_against(types.second));
83  }
84  return res;
85  } else {
86  return unit_->damage_from(attack, false, map_location());
87  }
88  }
89 
90  const_attack_itors attacks() const {
91  if(unit_type_ != nullptr) {
92  return unit_type_->attacks();
93  } else {
94  return unit_->attacks();
95  }
96  }
97 
98  int movement_cost(const t_translation::terrain_code & terrain) const {
99  if(unit_type_ != nullptr) {
100  return unit_type_->movement_type().movement_cost(terrain);
101  } else {
102  return unit_->movement_cost(terrain);
103  }
104  }
105 
106  private:
108  const unit* unit_;
109 };
110 
111 #define DEFINE_FAI_FUNCTION(name, min_args, max_args) \
112  class name##_function : public function_expression \
113  { \
114  public: \
115  explicit name##_function(const args_list& args, const formula_ai& ai) \
116  : function_expression(#name, args, min_args, max_args), ai_(ai) \
117  { \
118  } \
119  \
120  private: \
121  const formula_ai& ai_; \
122  variant execute(const formula_callable& variables, formula_debugger* fdb) const; \
123  }; \
124  \
125  variant name##_function::execute(const formula_callable& variables, formula_debugger* fdb) const
126 
127 DEFINE_FAI_FUNCTION(distance_to_nearest_unowned_village, 1, 1)
128 {
129  const map_location loc = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "distance_to_nearest_unowned_village:location")).convert_to<location_callable>()->loc();
130  int best = 1000000;
131  const std::vector<map_location>& villages = resources::gameboard->map().villages();
132  const std::set<map_location>& my_villages = ai_.current_team().villages();
133  for(std::vector<map_location>::const_iterator i = villages.begin(); i != villages.end(); ++i) {
134  int distance = distance_between(loc, *i);
135  if(distance < best) {
136  if(my_villages.count(*i) == 0) {
137  best = distance;
138  }
139  }
140  }
141 
142  return variant(best);
143 }
144 
145 static unsigned search_counter;
146 
147 namespace {
148  struct indexer {
149  int w, h;
150  indexer(int a, int b) : w(a), h(b) { }
151  int operator()(const map_location& loc) const {
152  return loc.y * w + loc.x;
153  }
154  };
155 
156  struct node {
159 
160  /**
161  * If equal to search_counter, the node is off the list.
162  * If equal to search_counter + 1, the node is on the list.
163  * Otherwise it is outdated.
164  */
165  unsigned in;
166 
167  node(int moves, const map_location &loc)
168  : movement_cost_(moves)
169  , loc_(loc)
170  , in(0)
171  {
172  }
173 
174  node()
175  : movement_cost_(0)
176  , loc_()
177  , in(0)
178  {
179  }
180 
181  bool operator<(const node& o) const {
182  return movement_cost_ < o.movement_cost_;
183  }
184  };
185 
186  struct comp {
187  const std::vector<node>& nodes;
188  comp(const std::vector<node>& n) : nodes(n) { }
189  bool operator()(int l, int r) const {
190  return nodes[r] < nodes[l];
191  }
192  };
193 
194  void find_movemap(const unit_adapter& u, const map_location& loc,
195  std::vector<int>& scores, bool allow_teleport, const formula_ai& ai_)
196  {
197  const std::set<map_location>& teleports = allow_teleport ? ai_.current_team().villages() : std::set<map_location>();
198 
199  const gamemap& map = resources::gameboard->map();
200 
201  std::vector<map_location> locs(6 + teleports.size());
202  std::copy(teleports.begin(), teleports.end(), locs.begin() + 6);
203 
204  search_counter += 2;
205  if (search_counter == 0) search_counter = 2;
206 
207  static std::vector<node> nodes;
208  nodes.resize(map.w() * map.h());
209 
210  indexer index(map.w(), map.h());
211  comp node_comp(nodes);
212 
213  nodes[index(loc)] = node(0, loc);
214  std::vector<int> pq;
215  pq.push_back(index(loc));
216  while (!pq.empty()) {
217  node& n = nodes[pq.front()];
218  std::pop_heap(pq.begin(), pq.end(), node_comp);
219  pq.pop_back();
220  n.in = search_counter;
221 
222  get_adjacent_tiles(n.loc_, locs.data());
223  for (int i = teleports.count(n.loc_) ? locs.size() : 6; i-- > 0;) {
224  if (!locs[i].valid(map.w(), map.h())) continue;
225 
226  node& next = nodes[index(locs[i])];
227  bool next_visited = next.in - search_counter <= 1u;
228 
229  // test if the current path to locs[i] is better than this one could possibly be.
230  // we do this a couple more times below
231  if (next_visited && !(n < next)) continue;
232  const int move_cost = u.movement_cost(map[locs[i]]);
233 
234  node t = node(n.movement_cost_ + move_cost, locs[i]);
235 
236  if (next_visited && !(t < next)) continue;
237 
238  bool in_list = next.in == search_counter + 1;
239  t.in = search_counter + 1;
240  next = t;
241 
242  // if already in the priority queue then we just update it, else push it.
243  if (in_list) {
244  std::push_heap(pq.begin(), std::find(pq.begin(), pq.end(), index(locs[i])) + 1, node_comp);
245  }
246  else {
247  pq.push_back(index(locs[i]));
248  std::push_heap(pq.begin(), pq.end(), node_comp);
249  }
250  }
251  }
252 
253  for (int x = 0; x < map.w(); ++x) {
254  for (int y = 0; y < map.h(); ++y)
255  {
256  int i = y * map.w() + x;
257  const node &n = nodes[i];
258  scores[i] = scores[i] + n.movement_cost_;
259  //std::cout << x << "," << y << ":" << n.movement_cost << std::endl;
260  }
261  }
262  }
263 }
264 
265 DEFINE_FAI_FUNCTION(calculate_map_ownership, 2, 5)
266 {
267  int w = resources::gameboard->map().w();
268  int h = resources::gameboard->map().h();
269 
270  const variant units_input = args()[0]->evaluate(variables,fdb);
271  const variant leaders_input = args()[1]->evaluate(variables,fdb);
272 
273  int enemy_tolerance = 3;
274  if( args().size() > 2 )
275  enemy_tolerance = args()[2]->evaluate(variables,fdb).as_int();
276 
277  int enemy_border_tolerance = 5;
278  if( args().size() > 3 )
279  enemy_border_tolerance = args()[3]->evaluate(variables,fdb).as_int();
280 
281  int ally_tolerance = 3;
282  if( args().size() > 4 )
283  ally_tolerance = args()[4]->evaluate(variables,fdb).as_int();
284 
285  if( !units_input.is_list() )
286  return variant();
287 
288  std::size_t number_of_teams = units_input.num_elements();
289 
290  std::vector< std::vector<int>> scores( number_of_teams );
291 
292  for( std::size_t i = 0; i< number_of_teams; ++i)
293  scores[i].resize(w*h);
294 
295  /* // TODO: Do we need this?
296  for(unit_map::const_iterator i = resources::gameboard->units().begin(); i != resources::gameboard->units().end(); ++i) {
297  unit_counter[i->second.side()-1]++;
298  unit_adapter unit(i->second);
299  find_movemap( resources::gameboard->map(), resources::gameboard->units(), unit, i->first, scores[i->second.side()-1], ai_.resources::gameboard->teams() , true );
300  }
301  */
302 
303  for(std::size_t side = 0 ; side < units_input.num_elements() ; ++side) {
304  if( leaders_input[side].is_empty() )
305  continue;
306 
307  const map_location loc = leaders_input[side][0].convert_to<location_callable>()->loc();
308  const variant units_of_side = units_input[side];
309 
310  for(std::size_t unit_it = 0 ; unit_it < units_of_side.num_elements() ; ++unit_it) {
311  unit_adapter unit(units_of_side[unit_it]);
312  find_movemap( unit, loc, scores[side], true, ai_ );
313  }
314  }
315 
316  std::size_t index = 0;
317  for( std::vector< std::vector<int>>::iterator i = scores.begin() ; i != scores.end() ; ++i) {
318  for( std::vector<int>::iterator j = i->begin() ; j != i->end() ; ++j ) {
319  if(units_input[index].num_elements() != 0) {
320  *j /= units_input[index].num_elements();
321  } else {
322  *j = 0;
323  }
324  }
325 
326  ++index;
327  }
328  //std::vector<variant> res;
329  std::map<variant, variant> res;
330 
331  std::size_t current_side = ai_.get_side() - 1 ;
332 
333  std::vector<int> enemies;
334  std::vector<int> allies;
335 
336  for(std::size_t side = 0 ; side < units_input.num_elements() ; ++side) {
337  if( side == current_side)
338  continue;
339 
340  if( ai_.current_team().is_enemy(side+1) ) {
341  if( !leaders_input[side].is_empty() )
342  enemies.push_back(side);
343  } else {
344  if( !leaders_input[side].is_empty() )
345  allies.push_back(side);
346  }
347  }
348 
349  //calculate_map_ownership( recruits_of_side, map(units_of_side, 'units', map( filter(units, leader), loc) ) )
350  //map(, debug_label(key,value))
351  for (int x = 0; x < w; ++x) {
352  for (int y = 0; y < h; ++y)
353  {
354  int i = y * w + x;
355  bool valid = true;
356  bool enemy_border = false;
357 
358  if( scores[current_side][i] > 98 )
359  continue;
360 
361  for (int side : enemies) {
362  int diff = scores[current_side][i] - scores[side][i];
363  if ( diff > enemy_tolerance) {
364  valid = false;
365  break;
366  } else if( std::abs(diff) < enemy_border_tolerance )
367  enemy_border = true;
368  }
369 
370  if( valid ) {
371  for (int side : allies) {
372  if ( scores[current_side][i] - scores[side][i] > ally_tolerance ) {
373  valid = false;
374  break;
375  }
376  }
377  }
378 
379  if( valid ) {
380  if( enemy_border )
381  res.emplace(variant(std::make_shared<location_callable>(map_location(x, y))), variant(scores[0][i] + 10000));
382  else
383  res.emplace(variant(std::make_shared<location_callable>(map_location(x, y))), variant(scores[0][i]));
384  }
385  }
386  }
387  return variant(res);
388 }
389 
390 DEFINE_WFL_FUNCTION(nearest_loc, 2, 2)
391 {
392  const map_location loc = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "nearest_loc:location")).convert_to<location_callable>()->loc();
393  variant items = args()[1]->evaluate(variables,add_debug_info(fdb,1,"nearest_loc:locations"));
394  int best = 1000000;
395  int best_i = -1;
396 
397  for(std::size_t i = 0; i < items.num_elements(); ++i) {
398 
399  const map_location move_loc = items[i].convert_to<location_callable>()->loc();
400  int distance = distance_between(loc, move_loc);
401 
402  if(distance < best) {
403  best = distance;
404  best_i = i;
405  }
406  }
407 
408  if( best_i != -1)
409  return variant(std::make_shared<location_callable>(items[best_i].convert_to<location_callable>()->loc()));
410  else
411  return variant();
412 }
413 
414 /** FormulaAI function to run fai script from file. Usable from in-game console.
415 * arguments[0] - required file name, follows the usual wml convention
416 */
417 DEFINE_FAI_FUNCTION(run_file, 1, 1)
418 {
419  const args_list& arguments = args();
420  const variant var0 = arguments[0]->evaluate(variables,add_debug_info(fdb,0,"run_file:file"));
421  const std::string filename = var0.string_cast();
422 
423  //NOTE: get_wml_location also filters file path to ensure it doesn't contain things like "../../top/secret"
424  std::string path = filesystem::get_wml_location(filename);
425  if(path.empty()) {
426  ERR_AI << "run_file : not found [" << filename <<"]";
427  return variant(); //no suitable file
428  }
429 
430  std::string formula_string = filesystem::read_file(path);
431  //need to get function_table from somewhere or delegate to someone who has access to it
432  formula_ptr parsed_formula = ai_.create_optional_formula(formula_string);
433  if(parsed_formula == formula_ptr()) {
434  ERR_AI << "run_file : unable to create formula";
435  return variant(); //was unable to create a formula from file
436  }
437  return parsed_formula->evaluate(variables,add_debug_info(fdb,-1,"run_file:formula_from_file"));
438 }
439 
440 DEFINE_WFL_FUNCTION(castle_locs, 1, 1)
441 {
442  const map_location starting_loc = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "castle_locs:location")).convert_to<location_callable>()->loc();
443 
444  //looks like reimplementing a generic graph search algorithm to me
445  std::set< map_location > visited_locs;
446  std::queue< map_location > queued_locs;
447 
448  queued_locs.push(starting_loc);
449 
450  while( !queued_locs.empty() ) {
451  const map_location loc = queued_locs.front();
452  queued_locs.pop();
453 
454  if ( visited_locs.find( loc ) != visited_locs.end() )
455  continue;
456 
457  visited_locs.insert(loc);
458 
459  for(const map_location& adj : get_adjacent_tiles(loc)) {
460  if (resources::gameboard->map().on_board(adj) && visited_locs.find( adj ) == visited_locs.end() ) {
461  if (resources::gameboard->map().get_terrain_info(adj).is_keep() ||
462  resources::gameboard->map().get_terrain_info(adj).is_castle() ) {
463  queued_locs.push(adj);
464  }
465  }
466  }
467  }
468 
469  if ( !resources::gameboard->map().get_terrain_info(starting_loc).is_keep() &&
470  !resources::gameboard->map().get_terrain_info(starting_loc).is_castle() )
471  visited_locs.erase(starting_loc);
472 
473  std::vector<variant> res;
474  for (const map_location& ml : visited_locs) {
475  res.emplace_back(std::make_shared<location_callable>( ml ));
476  }
477 
478  return variant(res);
479 }
480 
481 /**
482  * timeofday_modifer formula function. Returns combat modifier, taking
483  * alignment, illuminate, time of day and fearless trait into account.
484  * 'leadership' and 'slowed' are not taken into account.
485  * arguments[0] - unit
486  * arguments[1] - location (optional, defaults to unit's current location.
487  */
488 DEFINE_WFL_FUNCTION(timeofday_modifier, 1, 2)
489 {
490  variant u = args()[0]->evaluate(variables,add_debug_info(fdb,0,"timeofday_modifier:unit"));
491 
492  if( u.is_null() ) {
493  return variant();
494  }
495 
496  auto u_call = u.try_convert<unit_callable>();
497 
498  if(!u_call) {
499  return variant();
500  }
501 
502  const unit& un = u_call->get_unit();
503 
504  map_location loc;
505 
506  if(args().size()==2) {
507  loc = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "timeofday_modifier:location")).convert_to<location_callable>()->loc();
508  }
509 
510  if(!loc.valid()) {
511  loc = u_call->get_location();
512  }
513 
514  return variant(combat_modifier(resources::gameboard->units(), resources::gameboard->map(), loc, un.alignment(), un.is_fearless()));
515 }
516 
517 DEFINE_FAI_FUNCTION(nearest_keep, 1, 1)
518 {
519  const map_location loc = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "nearest_keep:location")).convert_to<location_callable>()->loc();
520  int best = 1000000;
521  int best_i = -1;
522 
523  ai_.get_keeps();
525 
526  for( int i = 0 ; i < size; ++i) {
527  int distance = distance_between(loc, ai_.get_keeps_cache()[i].convert_to<location_callable>()->loc() );
528  if(distance < best)
529  {
530  best = distance;
531  best_i = i;
532  }
533  }
534 
535  if( best_i != -1)
536  return variant(std::make_shared<location_callable>(ai_.get_keeps_cache()[best_i].convert_to<location_callable>()->loc()));
537  else
538  return variant();
539 }
540 
541 /**
542 * Find suitable keep for unit at location
543 * arguments[0] - location for unit on which the suitable keep is to be found
544 */
545 DEFINE_FAI_FUNCTION(suitable_keep, 1, 1)
546 {
547  const map_location loc = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "suitable_keep:location")).convert_to<location_callable>()->loc();
548  const unit_map& units = resources::gameboard->units();
549  const unit_map::const_iterator u = units.find(loc);
550  if (u == units.end()){
551  return variant();
552  }
553  const pathfind::paths unit_paths(*u, false, true, ai_.current_team());
554  return variant(std::make_shared<location_callable>(ai_.suitable_keep(loc,unit_paths)));
555 }
556 
557 DEFINE_FAI_FUNCTION(find_shroud, 0, 1)
558 {
559  std::vector<variant> vars;
560  int w,h;
561 
562  if(args().size()==1) {
563  const gamemap& m = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "find_shroud:gamemap")).convert_to<gamemap_callable>()->get_gamemap();
564  w = m.w();
565  h = m.h();
566  } else {
567  w = resources::gameboard->map().w();
568  h = resources::gameboard->map().h();
569  }
570 
571  for(int i = 0; i < w; ++i)
572  for(int j = 0; j < h; ++j) {
574  vars.emplace_back(std::make_shared<location_callable>(map_location(i, j)));
575  }
576 
577  return variant(vars);
578 }
579 
580 DEFINE_FAI_FUNCTION(close_enemies, 2, 2)
581 {
582  std::vector<variant> vars;
583  const map_location loc = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "close_enemies:location")).convert_to<location_callable>()->loc();
584  int range_s = args()[1]->evaluate(variables,add_debug_info(fdb,1,"close_enemies:distance")).as_int();
585  if (range_s < 0) {
586  WRN_AI << "close_enemies_function: range is negative (" << range_s << ")";
587  range_s = 0;
588  }
589  std::size_t range = static_cast<std::size_t>(range_s);
592  while (un != end) {
593  if (distance_between(loc, un->get_location()) <= range) {
594  if (un->side() != ai_.get_side()) {//fixme: ignores allied units
595  vars.emplace_back(std::make_shared<unit_callable>(*un));
596  }
597  }
598  ++un;
599  }
600  return variant(vars);
601 }
602 
603 DEFINE_WFL_FUNCTION(calculate_outcome, 3, 4)
604 {
605  std::vector<variant> vars;
606  int weapon;
607  if (args().size() > 3) weapon = args()[3]->evaluate(variables,add_debug_info(fdb,3,"calculate_outcome:weapon")).as_int();
608  else weapon = -1;
609 
610  const unit_map& units = resources::gameboard->units();
611  map_location attacker_location =
612  args()[0]->evaluate(variables, add_debug_info(fdb, 0, "calculate_outcome:attacker_current_location")).convert_to<location_callable>()->loc();
613  if(units.count(attacker_location) == 0) {
614  ERR_AI << "Performing calculate_outcome() with non-existent attacker at (" <<
615  attacker_location.wml_x() << "," << attacker_location.wml_y() << ")";
616  return variant();
617  }
618 
619  map_location defender_location =
620  args()[2]->evaluate(variables,add_debug_info(fdb, 2, "calculate_outcome:defender_location")).convert_to<location_callable>()->loc();
621  if(units.count(defender_location) == 0) {
622  ERR_AI << "Performing calculate_outcome() with non-existent defender at (" <<
623  defender_location.wml_x() << "," << defender_location.wml_y() << ")";
624  return variant();
625  }
626 
627  battle_context bc(units, args()[1]->evaluate(variables, add_debug_info(fdb, 1, "calculate_outcome:attacker_attack_location")).convert_to<location_callable>()->loc(),
628  defender_location, weapon, -1, 1.0, nullptr, units.find(attacker_location).get_shared_ptr());
629  std::vector<double> hp_dist = bc.get_attacker_combatant().hp_dist;
630  std::vector<double>::iterator it = hp_dist.begin();
631  int i = 0;
632  std::vector<variant> hitLeft;
633  std::vector<variant> prob;
634  while (it != hp_dist.end()) {
635  if (*it != 0) {
636  hitLeft.emplace_back(i);
637  prob.emplace_back(static_cast<int>(*it*10000));
638  }
639  ++it;
640  ++i;
641  }
642  std::vector<variant> status;
643  if (bc.get_attacker_combatant().poisoned != 0)
644  status.emplace_back("Poisoned");
645  if (bc.get_attacker_combatant().slowed != 0)
646  status.emplace_back("Slowed");
647  if (bc.get_defender_stats().petrifies && static_cast<unsigned int>(hitLeft[0].as_int()) != bc.get_attacker_stats().hp)
648  status.emplace_back("Stoned");
649  if (bc.get_defender_stats().plagues && hitLeft[0].as_int() == 0)
650  status.emplace_back("Zombiefied");
651  vars.emplace_back(std::make_shared<outcome_callable>(hitLeft, prob, status));
652  hitLeft.clear();
653  prob.clear();
654  status.clear();
655  hp_dist = bc.get_defender_combatant().hp_dist;
656  it = hp_dist.begin();
657  i = 0;
658  while (it != hp_dist.end()) {
659  if (*it != 0) {
660  hitLeft.emplace_back(i);
661  prob.emplace_back(static_cast<int>(*it*10000));
662  }
663  ++it;
664  ++i;
665  }
666  if (bc.get_defender_combatant().poisoned != 0)
667  status.emplace_back("Poisoned");
668  if (bc.get_defender_combatant().slowed != 0)
669  status.emplace_back("Slowed");
670  if (bc.get_attacker_stats().petrifies && static_cast<unsigned int>(hitLeft[0].as_int()) != bc.get_defender_stats().hp)
671  status.emplace_back("Stoned");
672  if (bc.get_attacker_stats().plagues && hitLeft[0].as_int() == 0)
673  status.emplace_back("Zombiefied");
674  vars.emplace_back(std::make_shared<outcome_callable>(hitLeft, prob, status));
675  return variant(vars);
676 }
677 
678 DEFINE_WFL_FUNCTION(outcomes, 1, 1)
679 {
680  variant attack = args()[0]->evaluate(variables,add_debug_info(fdb,0,"outcomes:attack"));
681  auto analysis = attack.convert_to<ai::attack_analysis>();
682  //unit_map units_with_moves(resources::gameboard->units());
683  //typedef std::pair<map_location, map_location> mv;
684  //for(const mv &m : analysis->movements) {
685  // units_with_moves.move(m.first, m.second);
686  //}
687 
688  std::vector<variant> vars;
689  if(analysis->chance_to_kill > 0.0) {
690  //unit_map units(units_with_moves);
691  //units.erase(analysis->target);
692  vars.emplace_back(std::make_shared<position_callable>(/*&units,*/ static_cast<int>(analysis->chance_to_kill*100)));
693 
694  }
695 
696  if(analysis->chance_to_kill < 1.0) {
697  //unit_map units(units_with_moves);
698  vars.emplace_back(std::make_shared<position_callable>(/*&units,*/ static_cast<int>(100 - analysis->chance_to_kill*100)));
699  }
700 
701  return variant(vars);
702 }
703 
704 DEFINE_FAI_FUNCTION(rate_action, 1, 1)
705 {
706  variant act = args()[0]->evaluate(variables,add_debug_info(fdb,0,"rate_action:action"));
707  auto analysis = act.convert_to<ai::attack_analysis>();
708 
709  return variant(analysis->rating(ai_.get_aggression(),ai_)*1000,variant::DECIMAL_VARIANT);
710 }
711 
712 DEFINE_WFL_FUNCTION(recall, 1, 2)
713 {
714  const std::string id = args()[0]->evaluate(variables,add_debug_info(fdb,0,"recall:id")).as_string();
715  map_location loc;
716  if(args().size() >= 2) {
717  loc = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "recall:location")).convert_to<location_callable>()->loc();
718  }
719 
720  return variant(std::make_shared<recall_callable>(loc, id));
721 }
722 
723 DEFINE_WFL_FUNCTION(recruit, 1, 2)
724 {
725  const std::string type = args()[0]->evaluate(variables,add_debug_info(fdb,0,"recruit:type")).as_string();
726  map_location loc;
727  if(args().size() >= 2) {
728  loc = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "recruit:location")).convert_to<location_callable>()->loc();
729  }
730 
731  return variant(std::make_shared<recruit_callable>(loc, type));
732 }
733 
734 DEFINE_FAI_FUNCTION(shortest_path, 2, 3)
735 {
736 
737  std::vector<variant> locations;
738 
739  const map_location src = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "shortest_path:src")).convert_to<location_callable>()->loc();
740  const map_location dst = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "shortest_path:dst")).convert_to<location_callable>()->loc();
741  map_location unit_loc;
742 
743  if( src == dst )
744  return variant(locations);
745 
746  if(args().size() > 2)
747  unit_loc = args()[2]->evaluate(variables,add_debug_info(fdb,2,"shortest_path:unit_location")).convert_to<location_callable>()->loc();
748  else
749  unit_loc = src;
750 
751  unit_map::iterator unit_it = resources::gameboard->units().find(unit_loc);
752 
753  if( unit_it == resources::gameboard->units().end() ) {
754  std::ostringstream str;
755  str << "shortest_path function: expected unit at location (" << (unit_loc.wml_x()) << "," << (unit_loc.wml_y()) << ")";
756  throw formula_error( str.str(), "", "", 0);
757  }
758 
759  pathfind::teleport_map allowed_teleports = ai_.get_allowed_teleports(unit_it);
760 
761  pathfind::plain_route route = ai_.shortest_path_calculator( src, dst, unit_it, allowed_teleports );
762 
763  if( route.steps.size() < 2 ) {
764  return variant(locations);
765  }
766 
767  for (std::vector<map_location>::const_iterator loc_iter = route.steps.begin() + 1 ; loc_iter !=route.steps.end(); ++loc_iter) {
768  locations.emplace_back(std::make_shared<location_callable>(*loc_iter));
769  }
770 
771  return variant(locations);
772 }
773 
774 DEFINE_FAI_FUNCTION(simplest_path, 2, 3)
775 {
776  std::vector<variant> locations;
777 
778  const map_location src = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "simplest_path:src")).convert_to<location_callable>()->loc();
779  const map_location dst = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "simplest_path:dst")).convert_to<location_callable>()->loc();
780  map_location unit_loc;
781 
782  if( src == dst )
783  return variant(locations);
784 
785  if(args().size() > 2)
786  unit_loc = args()[2]->evaluate(variables, add_debug_info(fdb, 2, "simplest_path:unit_location")).convert_to<location_callable>()->loc();
787  else
788  unit_loc = src;
789 
790  unit_map::iterator unit_it = resources::gameboard->units().find(unit_loc);
791 
792  if( unit_it == resources::gameboard->units().end() ) {
793  std::ostringstream str;
794  str << "simplest_path function: expected unit at location (" << (unit_loc.wml_x()) << "," << (unit_loc.wml_y()) << ")";
795  throw formula_error( str.str(), "", "", 0);
796  }
797 
798  pathfind::teleport_map allowed_teleports = ai_.get_allowed_teleports(unit_it);
799 
801 
802  pathfind::plain_route route = pathfind::a_star_search(src, dst, 1000.0, em_calc, resources::gameboard->map().w(), resources::gameboard->map().h(), &allowed_teleports);
803 
804  if( route.steps.size() < 2 ) {
805  return variant(locations);
806  }
807 
808  for (std::vector<map_location>::const_iterator loc_iter = route.steps.begin() + 1 ; loc_iter !=route.steps.end(); ++loc_iter) {
809  if(unit_it->movement_cost(static_cast<const game_board*>(resources::gameboard)->map()[*loc_iter]) < movetype::UNREACHABLE) {
810  locations.emplace_back(std::make_shared<location_callable>(*loc_iter));
811  } else {
812  break;
813  }
814  }
815 
816  return variant(locations);
817 }
818 
819 DEFINE_FAI_FUNCTION(next_hop, 2, 3)
820 {
821 
822  const map_location src = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "next_hop:src")).convert_to<location_callable>()->loc();
823  const map_location dst = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "next_hop:dst")).convert_to<location_callable>()->loc();
824  map_location unit_loc;
825 
826  if( src == dst )
827  return variant();
828 
829  if(args().size() > 2)
830  unit_loc = args()[2]->evaluate(variables, add_debug_info(fdb, 2, "next_hop:unit_location")).convert_to<location_callable>()->loc();
831  else
832  unit_loc = src;
833 
834  unit_map::iterator unit_it = resources::gameboard->units().find(unit_loc);
835 
836  if( unit_it == resources::gameboard->units().end() ) {
837  std::ostringstream str;
838  str << "next_hop function: expected unit at location (" << (unit_loc.wml_x()) << "," << (unit_loc.wml_y()) << ")";
839  throw formula_error( str.str(), "", "", 0);
840  }
841 
842  pathfind::teleport_map allowed_teleports = ai_.get_allowed_teleports(unit_it);
843 
844  pathfind::plain_route route = ai_.shortest_path_calculator( src, dst, unit_it, allowed_teleports );
845 
846  if( route.steps.size() < 2 ) {
847  return variant();
848  }
849 
851  const ai::moves_map &possible_moves = ai_.get_possible_moves();
852  const ai::moves_map::const_iterator& p_it = possible_moves.find(unit_loc);
853  if (p_it==possible_moves.end() ) {
854  return variant();
855  }
856 
857  for (std::vector<map_location>::const_iterator loc_iter = route.steps.begin() + 1 ; loc_iter !=route.steps.end(); ++loc_iter) {
858 
859  if (p_it->second.destinations.find(*loc_iter) != p_it->second.destinations.end() ) {
860  loc = *loc_iter;
861  } else {
862  break;
863  }
864  }
865  if (loc==map_location::null_location()) {
866  return variant();
867  }
868  return variant(std::make_shared<location_callable>(loc));
869 }
870 
871 DEFINE_WFL_FUNCTION(move, 2, 2)
872 {
873  const map_location src = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "move:src")).convert_to<location_callable>()->loc();
874  const map_location dst = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "move:dst")).convert_to<location_callable>()->loc();
875  LOG_AI << "move(): " << src << ", " << dst << ")";
876  return variant(std::make_shared<move_callable>(src, dst));
877 }
878 
879 DEFINE_WFL_FUNCTION(move_partial, 2, 2)
880 {
881  const map_location src = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "move_partial:src")).convert_to<location_callable>()->loc();
882  const map_location dst = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "move_partial:dst")).convert_to<location_callable>()->loc();
883  LOG_AI << "move_partial(): " << src << ", " << dst << ")";
884  return variant(std::make_shared<move_partial_callable>(src, dst));
885 }
886 
887 DEFINE_WFL_FUNCTION(set_unit_var, 3, 3)
888 {
889  return variant(std::make_shared<set_unit_var_callable>(args()[0]->evaluate(variables,add_debug_info(fdb,0,"set_unit_var:key")).as_string(), args()[1]->evaluate(variables,add_debug_info(fdb,1,"set_unit_var:value")), args()[2]->evaluate(variables,add_debug_info(fdb,2,"set_unit_var:unit_location")).convert_to<location_callable>()->loc()));
890 }
891 
892 DEFINE_WFL_FUNCTION(fallback, 0, 1)
893 {
894  UNUSED(fdb);
895  // The parameter is not used, but is accepted for legacy compatibility
896  if(args().size() == 1 && args()[0]->evaluate(variables).as_string() != "human")
897  return variant();
898  return variant(std::make_shared<fallback_callable>());
899 }
900 
901 DEFINE_WFL_FUNCTION(attack, 3, 4)
902 {
903  const map_location move_from = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "attack:move_from")).convert_to<location_callable>()->loc();
904  const map_location src = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "attack:src")).convert_to<location_callable>()->loc();
905  const map_location dst = args()[2]->evaluate(variables, add_debug_info(fdb, 2, "attack:dst")).convert_to<location_callable>()->loc();
906  const int weapon = args().size() == 4 ? args()[3]->evaluate(variables,add_debug_info(fdb,3,"attack:weapon")).as_int() : -1;
907  if(resources::gameboard->units().count(move_from) == 0 || resources::gameboard->units().count(dst) == 0) {
908  ERR_AI << "AI ERROR: Formula produced illegal attack: " << move_from << " -> " << src << " -> " << dst;
909  return variant();
910  }
911  return variant(std::make_shared<attack_callable>(move_from, src, dst, weapon));
912 }
913 
914 DEFINE_FAI_FUNCTION(debug_label, 2, 2)
915 {
916  const args_list& arguments = args();
917  const variant var0 = arguments[0]->evaluate(variables,fdb);
918  const variant var1 = arguments[1]->evaluate(variables,fdb);
919 
920  const map_location location = var0.convert_to<location_callable>()->loc();
921  std::string text;
922  if( var1.is_string() )
923  text = var1.as_string();
924  else
925  text = var1.to_debug_string();
926 
928  std::string team_name;
929 
931 
932  const terrain_label *res;
933  res = gui->labels().set_label(location, text, ai_.get_side() - 1, team_name, color);
934  if (res && resources::recorder)
936 
937  std::vector<variant> result;
938  result.push_back(var0);
939  result.push_back(var1);
940  return variant(result);
941 }
942 
943 DEFINE_WFL_FUNCTION(is_village, 2, 3)
944 {
945  const gamemap& m = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "is_village:map")).convert_to<gamemap_callable>()->get_gamemap();
946 
947  map_location loc;
948  if(args().size() == 2) {
949  loc = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "is_village:location")).convert_to<location_callable>()->loc();
950  } else {
951  loc = map_location( args()[1]->evaluate(variables,add_debug_info(fdb,1,"is_village:x")).as_int(),
952  args()[2]->evaluate(variables,add_debug_info(fdb,2,"is_village:y")).as_int(), wml_loc());
953  }
954  return variant(m.is_village(loc));
955 }
956 
957 DEFINE_FAI_FUNCTION(is_unowned_village, 2, 3)
958 {
959 
960  const gamemap& m = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "is_unowned_village:map")).convert_to<gamemap_callable>()->get_gamemap();
961  const std::set<map_location>& my_villages = ai_.current_team().villages();
962 
963  map_location loc;
964  if(args().size() == 2) {
965  loc = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "is_unowned_village:location")).convert_to<location_callable>()->loc();
966  } else {
967  loc = map_location( args()[1]->evaluate(variables,add_debug_info(fdb,1,"is_unowned_village:x")).as_int(),
968  args()[2]->evaluate(variables,add_debug_info(fdb,2,"is_unowned_village:y")).as_int(), wml_loc());
969  }
970 
971  if(m.is_village(loc) && (my_villages.count(loc)==0) ) {
972  return variant(true);
973  } else {
974  return variant(false);
975  }
976 }
977 
979 {
980  variant res = args()[0]->evaluate(variables,add_debug_info(fdb,0,"unit_moves:unit_location"));
981  std::vector<variant> vars;
982  if(res.is_null()) {
983  return variant(vars);
984  }
985 
986  const map_location& loc = res.convert_to<location_callable>()->loc();
987  const ai::move_map& srcdst = ai_.get_srcdst();
988  typedef ai::move_map::const_iterator Itor;
989  std::pair<Itor,Itor> range = srcdst.equal_range(loc);
990 
991  for(Itor i = range.first; i != range.second; ++i) {
992  vars.emplace_back(std::make_shared<location_callable>(i->second));
993  }
994 
995  return variant(vars);
996 }
997 
998 DEFINE_WFL_FUNCTION(units_can_reach, 2, 2)
999 {
1000  std::vector<variant> vars;
1001  variant dstsrc_var = args()[0]->evaluate(variables,add_debug_info(fdb,0,"units_can_reach:possible_move_list"));
1002  const ai::move_map& dstsrc = dstsrc_var.convert_to<move_map_callable>()->dstsrc();
1003  std::pair<ai::move_map::const_iterator,ai::move_map::const_iterator> range =
1004  dstsrc.equal_range(args()[1]->evaluate(variables, add_debug_info(fdb, 1, "units_can_reach:possible_move_list")).convert_to<location_callable>()->loc());
1005  while(range.first != range.second) {
1006  unit_map::const_iterator un = resources::gameboard->units().find(range.first->second);
1007  assert(un != resources::gameboard->units().end());
1008  vars.emplace_back(std::make_shared<unit_callable>(*un));
1009  ++range.first;
1010  }
1011 
1012  return variant(vars);
1013 }
1014 
1015 DEFINE_FAI_FUNCTION(is_avoided_location, 1, 1)
1016 {
1017  variant res = args()[0]->evaluate(variables,add_debug_info(fdb,0,"is_avoided_location:location"));
1018  if(res.is_null()) {
1019  return variant();
1020  }
1021  const map_location& loc = res.convert_to<location_callable>()->loc();
1022  return variant(ai_.get_avoid().match(loc));
1023 }
1024 
1025 DEFINE_WFL_FUNCTION(max_possible_damage, 2, 2)
1026 {
1027  variant u1 = args()[0]->evaluate(variables,add_debug_info(fdb,0,"max_possible_damage:unit1"));
1028  variant u2 = args()[1]->evaluate(variables,add_debug_info(fdb,1,"max_possible_damage:unit2"));
1029  if(u1.is_null() || u2.is_null()) {
1030  return variant();
1031  }
1032 
1033  unit_adapter u_attacker(u1), u_defender(u2);
1034  int best = 0;
1035  for(const attack_type& atk : u_attacker.attacks()) {
1036  const int dmg = round_damage(atk.damage(), u_defender.damage_from(atk), 100) * atk.num_attacks();
1037  if(dmg > best)
1038  best = dmg;
1039  }
1040  return variant(best);
1041 }
1042 
1043 namespace {
1044  std::pair<int, int> best_melee_and_ranged_attacks(unit_adapter attacker, unit_adapter defender) {
1045  int highest_melee_damage = 0;
1046  int highest_ranged_damage = 0;
1047 
1048  for (const attack_type &attack : attacker.attacks()) {
1049  const int dmg = round_damage(attack.damage(), defender.damage_from(attack), 100) * attack.num_attacks();
1050  if (attack.range() == "melee") {
1051  highest_melee_damage = std::max(highest_melee_damage, dmg);
1052  } else {
1053  highest_ranged_damage = std::max(highest_ranged_damage, dmg);
1054  }
1055  }
1056 
1057  return std::pair(highest_melee_damage, highest_ranged_damage);
1058  }
1059 }
1060 
1061 DEFINE_WFL_FUNCTION(max_possible_damage_with_retaliation, 2, 2)
1062 {
1063  variant u1 = args()[0]->evaluate(variables,add_debug_info(fdb,0,"max_possible_damage_with_retaliation:unit1"));
1064  variant u2 = args()[1]->evaluate(variables,add_debug_info(fdb,1,"max_possible_damage_with_retaliation:unit2"));
1065 
1066  if(u1.is_null() || u2.is_null()) {
1067  return variant();
1068  }
1069 
1070  unit_adapter attacker(u1);
1071  unit_adapter defender(u2);
1072 
1073  // find max damage inflicted by attacker and by defender to the attacker
1074  std::pair<int, int> best_attacker_attacks = best_melee_and_ranged_attacks(attacker, defender);
1075  std::pair<int, int> best_defender_attacks = best_melee_and_ranged_attacks(defender, attacker);
1076 
1077  std::vector<variant> vars;
1078  vars.emplace_back(best_attacker_attacks.first);
1079  vars.emplace_back(best_attacker_attacks.second);
1080  vars.emplace_back(best_defender_attacks.first);
1081  vars.emplace_back(best_defender_attacks.second);
1082 
1083  return variant(vars);
1084 }
1085 
1086 template<typename T>
1087 class ai_formula_function : public formula_function {
1088 protected:
1089  formula_ai& ai_;
1090 public:
1091  ai_formula_function(const std::string& name, ai::formula_ai& ai) : formula_function(name), ai_(ai) {}
1092  function_expression_ptr generate_function_expression(const std::vector<expression_ptr>& args) const {
1093  return std::make_shared<T>(args, ai_);
1094  }
1095 };
1096 
1097 }
1098 
1099 // This macro is for functions taking an additional formula_ai argument.
1100 // Functions using the other macro could potentially be made core.
1101 #define DECLARE_FAI_FUNCTION(name) \
1102  add_function(#name, std::make_shared<ai_formula_function<name##_function>>(#name, ai))
1103 
1106 {
1107  function_symbol_table& functions_table = *this;
1108  DECLARE_WFL_FUNCTION(outcomes);
1109  //DECLARE_FAI_FUNCTION(evaluate_for_position);
1110  DECLARE_WFL_FUNCTION(move);
1111  DECLARE_WFL_FUNCTION(move_partial);
1112  DECLARE_WFL_FUNCTION(attack);
1113  DECLARE_FAI_FUNCTION(rate_action);
1114  DECLARE_WFL_FUNCTION(recall);
1115  DECLARE_WFL_FUNCTION(recruit);
1116  DECLARE_FAI_FUNCTION(is_avoided_location);
1117  DECLARE_WFL_FUNCTION(is_village);
1118  DECLARE_FAI_FUNCTION(is_unowned_village);
1120  DECLARE_WFL_FUNCTION(set_unit_var);
1121  DECLARE_WFL_FUNCTION(fallback);
1122  DECLARE_WFL_FUNCTION(units_can_reach);
1123  DECLARE_FAI_FUNCTION(debug_label);
1124  DECLARE_WFL_FUNCTION(max_possible_damage);
1125  DECLARE_WFL_FUNCTION(max_possible_damage_with_retaliation);
1126  DECLARE_FAI_FUNCTION(next_hop);
1127  DECLARE_WFL_FUNCTION(castle_locs);
1128  DECLARE_WFL_FUNCTION(timeofday_modifier);
1129  DECLARE_FAI_FUNCTION(distance_to_nearest_unowned_village);
1130  DECLARE_FAI_FUNCTION(shortest_path);
1131  DECLARE_FAI_FUNCTION(simplest_path);
1132  DECLARE_FAI_FUNCTION(nearest_keep);
1133  DECLARE_FAI_FUNCTION(suitable_keep);
1134  DECLARE_WFL_FUNCTION(nearest_loc);
1135  DECLARE_FAI_FUNCTION(find_shroud);
1136  DECLARE_FAI_FUNCTION(close_enemies);
1137  DECLARE_WFL_FUNCTION(calculate_outcome);
1138  DECLARE_FAI_FUNCTION(run_file);
1139  DECLARE_FAI_FUNCTION(calculate_map_ownership);
1140 }
1141 #undef DECLARE_WFL_FUNCTION
1142 
1143 }
int combat_modifier(const unit_map &units, const gamemap &map, const map_location &loc, unit_alignments::type alignment, bool is_fearless)
Returns the amount that a unit's damage should be multiplied by due to the current time of day.
Definition: attack.cpp:1582
double t
Definition: astarsearch.cpp:63
boost::iterator_range< boost::indirect_iterator< attack_list::const_iterator > > const_attack_itors
const wfl::variant & get_keeps_cache() const
Definition: ai.hpp:114
pathfind::teleport_map get_allowed_teleports(unit_map::iterator &unit_it) const
Definition: ai.cpp:248
wfl::formula_ptr create_optional_formula(const std::string &formula_string) const
Create a new formula from the string, using the symbol table which is stored in the AI.
Definition: ai.cpp:133
wfl::variant get_keeps() const
Definition: ai.cpp:616
pathfind::plain_route shortest_path_calculator(const map_location &src, const map_location &dst, unit_map::iterator &unit_it, pathfind::teleport_map &allowed_teleports) const
Definition: ai.cpp:194
virtual const map_location & suitable_keep(const map_location &leader_location, const pathfind::paths &leader_paths) const override
get most suitable keep for leader - nearest free that can be reached in 1 turn, if none - return near...
Definition: contexts.hpp:856
virtual const team & current_team() const override
Definition: contexts.hpp:450
virtual const terrain_filter & get_avoid() const override
Definition: contexts.hpp:586
virtual const move_map & get_srcdst() const override
Definition: contexts.hpp:716
virtual double get_aggression() const override
Definition: contexts.hpp:546
virtual const moves_map & get_possible_moves() const override
Definition: contexts.hpp:676
virtual side_number get_side() const override
Get the side number.
Definition: contexts.hpp:396
std::pair< std::string, std::string > damage_type() const
return a modified damage type and/or add a secondary_type for hybrid use if special is active.
Definition: abilities.cpp:1242
Computes the statistics of a battle between an attacker and a defender unit.
Definition: attack.hpp:167
Sort-of-Singleton that many classes, both GUI and non-GUI, use to access the game data.
Definition: display.hpp:88
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:102
Game board class.
Definition: game_board.hpp:46
virtual const unit_map & units() const override
Definition: game_board.hpp:106
virtual const gamemap & map() const override
Definition: game_board.hpp:96
int w() const
Effective map width.
Definition: map.hpp:50
int h() const
Effective map height.
Definition: map.hpp:53
Encapsulates the map of the game.
Definition: map.hpp:172
bool is_village(const map_location &loc) const
Definition: map.cpp:65
const std::vector< map_location > & villages() const
Return a list of the locations of villages on the map.
Definition: map.hpp:237
static const int UNREACHABLE
Magic value that signifies a hex is unreachable.
Definition: movetype.hpp:174
int resistance_against(const std::string &damage_type) const
Returns the vulnerability to the indicated damage type (higher means takes more damage).
Definition: movetype.hpp:292
int movement_cost(const t_translation::terrain_code &terrain, bool slowed=false) const
Returns the cost to move through the indicated terrain.
Definition: movetype.hpp:278
void add_label(const terrain_label *)
Definition: replay.cpp:273
bool is_enemy(int n) const
Definition: team.hpp:229
const std::set< map_location > & villages() const
Definition: team.hpp:170
bool shrouded(const map_location &loc) const
Definition: team.cpp:650
static color_t get_side_color(int side)
Definition: team.cpp:959
To store label data Class implements logic for rendering.
Definition: label.hpp:111
Container associating units to locations.
Definition: map.hpp:98
unit_iterator end()
Definition: map.hpp:428
std::size_t count(const map_location &loc) const
Definition: map.hpp:413
unit_iterator find(std::size_t id)
Definition: map.cpp:302
unit_iterator begin()
Definition: map.hpp:418
A single unit type that the player may recruit.
Definition: types.hpp:43
const movetype & movement_type() const
Definition: types.hpp:189
const_attack_itors attacks() const
Definition: types.cpp:543
This class represents a single unit of a specific type.
Definition: unit.hpp:133
ai_function_symbol_table(ai::formula_ai &ai)
std::size_t num_elements() const
Definition: variant.cpp:267
@ DECIMAL_VARIANT
Definition: variant.hpp:31
std::shared_ptr< T > convert_to() const
Definition: variant.hpp:100
Default AI contexts.
Declarations for File-IO.
Defines formula ai.
std::size_t i
Definition: function.cpp:968
#define DEFINE_WFL_FUNCTION(name, min_args, max_args)
Helper macro to declare an associated class for a WFL function.
Definition: function.hpp:27
#define DECLARE_WFL_FUNCTION(name)
Declares a function name in the local function table functions_table.
Definition: function.hpp:47
#define ERR_AI
const unit * unit_
formula_ai & ai_
static lg::log_domain log_formula_ai("ai/engine/fai")
int h
map_location loc_
#define LOG_AI
#define WRN_AI
#define DEFINE_FAI_FUNCTION(name, min_args, max_args)
int movement_cost_
const unit_type * unit_type_
const std::vector< node > & nodes
int w
#define DECLARE_FAI_FUNCTION(name)
unsigned in
If equal to search_counter, the node is off the list.
static bool operator<(const placing_info &a, const placing_info &b)
Definition: game_state.cpp:108
#define UNUSED(x)
Definition: global.hpp:31
unit_alignments::type alignment() const
The alignment of this unit.
Definition: unit.hpp:475
int damage_from(const attack_type &attack, bool attacker, const map_location &loc, const_attack_ptr weapon=nullptr) const
Calculates the damage this unit would take from a certain attack.
Definition: unit.hpp:972
attack_itors attacks()
Gets an iterator over this unit's attacks.
Definition: unit.hpp:927
int movement_cost(const t_translation::terrain_code &terrain) const
Get the unit's movement cost on a particular terrain.
Definition: unit.hpp:1479
bool is_fearless() const
Gets whether this unit is fearless - ie, unaffected by time of day.
Definition: unit.hpp:1286
T end(const std::pair< T, T > &p)
void get_adjacent_tiles(const map_location &a, map_location *res)
Function which, given a location, will place all adjacent locations in res.
Definition: location.cpp:474
std::size_t distance_between(const map_location &a, const map_location &b)
Function which gives the number of hexes between two tiles (i.e.
Definition: location.cpp:545
Standard logging facilities (interface).
constexpr int round_damage(int base_damage, int bonus, int divisor)
round (base_damage * bonus / divisor) to the closest integer, but up or down towards base_damage
Definition: math.hpp:80
A small explanation about what's going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:59
std::multimap< map_location, map_location > move_map
The standard way in which a map of possible moves is recorded.
Definition: game_info.hpp:43
std::map< map_location, pathfind::paths > moves_map
The standard way in which a map of possible movement routes to location is recorded.
Definition: game_info.hpp:46
std::string get_wml_location(const std::string &filename, const std::string &current_dir)
Returns a complete path to the actual WML file or directory or an empty string if the file isn't pres...
std::string read_file(const std::string &fname)
Basic disk I/O - read file.
std::string path
Definition: filesystem.cpp:84
General purpose widgets.
const std::vector< std::string > items
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)
game_board * gameboard
Definition: resources.cpp:20
replay * recorder
Definition: resources.cpp:28
std::size_t index(const std::string &str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:70
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:85
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
Definition: contexts.hpp:43
formula_debugger * add_debug_info(formula_debugger *fdb, int arg_number, const std::string &f_name)
std::shared_ptr< formula > formula_ptr
Definition: formula_fwd.hpp:22
std::shared_ptr< function_expression > function_expression_ptr
Definition: function.hpp:172
This module contains various pathfinding functions and utilities.
Replay control code.
static config unit_moves(const reports::context &rc, const unit *u, bool is_visible_unit)
Definition: reports.cpp:667
The basic class for representing 8-bit RGB or RGBA colour values.
Definition: color.hpp:59
Encapsulates the map of the game.
Definition: location.hpp:38
bool valid() const
Definition: location.hpp:89
int wml_y() const
Definition: location.hpp:154
static const map_location & null_location()
Definition: location.hpp:81
int wml_x() const
Definition: location.hpp:153
Function which only uses terrain, ignoring shroud, enemies, etc.
Definition: pathfind.hpp:242
Object which contains all the possible locations a unit can move to, with associated best routes to t...
Definition: pathfind.hpp:73
Structure which holds a single route between one location and another.
Definition: pathfind.hpp:133
std::vector< map_location > steps
Definition: pathfind.hpp:135
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
Definition: translation.hpp:49
pointer get_shared_ptr() const
This is exactly the same as operator-> but it's slightly more readable, and can replace &*iter syntax...
Definition: map.hpp:217
static map_location::DIRECTION n
static const unit_type & get_unit_type(const std::string &type_id)
Converts a string ID to a unit_type.
Definition: unit.cpp:207
#define a
#define b