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