The Battle for Wesnoth  1.15.1+dev
aspect_attacks.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2018 by Yurii Chernyi <terraninfo@terraninfo.net>
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 /**
16  * Stage: fallback to other AI
17  * @file
18  */
19 
21 
22 #include "ai/manager.hpp"
23 #include "actions/attack.hpp"
24 #include "game_board.hpp"
25 #include "log.hpp"
26 #include "map/map.hpp"
27 #include "team.hpp"
28 #include "tod_manager.hpp"
29 #include "resources.hpp"
30 #include "units/unit.hpp"
31 #include "pathfind/pathfind.hpp"
32 #include "units/filter.hpp"
33 #include "scripting/lua_unit.hpp"
34 #include "lua/lauxlib.h"
35 
36 namespace ai {
37 
38 namespace ai_default_rca {
39 
40 static lg::log_domain log_ai_testing_aspect_attacks("ai/aspect/attacks");
41 #define DBG_AI LOG_STREAM(debug, log_ai_testing_aspect_attacks)
42 #define LOG_AI LOG_STREAM(info, log_ai_testing_aspect_attacks)
43 #define ERR_AI LOG_STREAM(err, log_ai_testing_aspect_attacks)
44 
45 aspect_attacks_base::aspect_attacks_base(readonly_context &context, const config &cfg, const std::string &id)
46  : typesafe_aspect<attacks_vector>(context,cfg,id)
47 {
48 }
49 
50 aspect_attacks::aspect_attacks(readonly_context &context, const config &cfg, const std::string &id)
51  : aspect_attacks_base(context,cfg,id)
52  , filter_own_()
53  , filter_enemy_()
54 {
55  if (const config &filter_own = cfg.child("filter_own")) {
56  vconfig vcfg(filter_own);
57  vcfg.make_safe();
58  filter_own_.reset(new unit_filter(vcfg));
59  }
60  if (const config &filter_enemy = cfg.child("filter_enemy")) {
61  vconfig vcfg(filter_enemy);
62  vcfg.make_safe();
63  filter_enemy_.reset(new unit_filter(vcfg));
64  }
65 }
66 
68 {
69  this->value_ = analyze_targets();
70  this->valid_ = true;
71 }
72 
73 std::shared_ptr<attacks_vector> aspect_attacks_base::analyze_targets() const
74 {
75  const move_map& srcdst = get_srcdst();
76  const move_map& dstsrc = get_dstsrc();
77  const move_map& enemy_srcdst = get_enemy_srcdst();
78  const move_map& enemy_dstsrc = get_enemy_dstsrc();
79 
80  auto res = std::make_shared<attacks_vector>();
81  unit_map& units_ = resources::gameboard->units();
82 
83  std::vector<map_location> unit_locs;
84  for(unit_map::const_iterator i = units_.begin(); i != units_.end(); ++i) {
85  if (i->side() == get_side() && i->attacks_left() && !(i->can_recruit() && get_passive_leader())) {
86  if (!is_allowed_attacker(*i)) {
87  continue;
88  }
89  unit_locs.push_back(i->get_location());
90  }
91  }
92 
93  bool used_locations[6];
94  std::fill(used_locations,used_locations+6,false);
95 
96  moves_map dummy_moves;
97  move_map fullmove_srcdst, fullmove_dstsrc;
98  calculate_possible_moves(dummy_moves,fullmove_srcdst,fullmove_dstsrc,false,true);
99 
100  unit_stats_cache().clear();
101 
102  for(unit_map::const_iterator j = units_.begin(); j != units_.end(); ++j) {
103 
104  // Attack anyone who is on the enemy side,
105  // and who is not invisible or petrified.
106  if (current_team().is_enemy(j->side()) && !j->incapacitated() &&
107  !j->invisible(j->get_location()))
108  {
109  if (!is_allowed_enemy(*j)) {
110  continue;
111  }
112  adjacent_loc_array_t adjacent;
113  get_adjacent_tiles(j->get_location(), adjacent.data());
114  attack_analysis analysis;
115  analysis.target = j->get_location();
116  analysis.vulnerability = 0.0;
117  analysis.support = 0.0;
118  do_attack_analysis(j->get_location(), srcdst, dstsrc,
119  fullmove_srcdst, fullmove_dstsrc, enemy_srcdst, enemy_dstsrc,
120  adjacent,used_locations,unit_locs,*res,analysis, current_team());
121  }
122  }
123  return res;
124 }
125 
126 
127 
129  const map_location& loc,
130  const move_map& srcdst, const move_map& dstsrc,
131  const move_map& fullmove_srcdst, const move_map& fullmove_dstsrc,
132  const move_map& enemy_srcdst, const move_map& enemy_dstsrc,
133  const adjacent_loc_array_t& tiles, bool* used_locations,
134  std::vector<map_location>& units,
135  std::vector<attack_analysis>& result,
136  attack_analysis& cur_analysis,
137  const team &current_team
138  ) const
139 {
140  // This function is called fairly frequently, so interact with the user here.
141 
143  const int max_attack_depth = 5;
144  if(cur_analysis.movements.size() >= std::size_t(max_attack_depth)) {
145  return;
146  }
147  const gamemap &map_ = resources::gameboard->map();
148  unit_map &units_ = resources::gameboard->units();
149  std::vector<team> &teams_ = resources::gameboard->teams();
150 
151 
152  const std::size_t max_positions = 1000;
153  if(result.size() > max_positions && !cur_analysis.movements.empty()) {
154  LOG_AI << "cut analysis short with number of positions\n";
155  return;
156  }
157 
158  for(std::size_t i = 0; i != units.size(); ++i) {
159  const map_location current_unit = units[i];
160 
161  unit_map::iterator unit_itor = units_.find(current_unit);
162  assert(unit_itor != units_.end());
163 
164  // See if the unit has the backstab ability.
165  // Units with backstab will want to try to have a
166  // friendly unit opposite the position they move to.
167  //
168  // See if the unit has the slow ability -- units with slow only attack first.
169  bool backstab = false, slow = false;
170  for(const attack_type& a : unit_itor->attacks()) {
171  // For speed, just assume these specials will be active if
172  // they are present.
173  if ( a.get_special_bool("backstab", true) ) {
174  backstab = true;
175  }
176 
177  if ( a.get_special_bool("slow", true) ) {
178  slow = true;
179  }
180  }
181 
182  if(slow && cur_analysis.movements.empty() == false) {
183  continue;
184  }
185 
186  // Check if the friendly unit is surrounded,
187  // A unit is surrounded if it is flanked by enemy units
188  // and at least one other enemy unit is nearby
189  // or if the unit is totaly surrounded by enemies
190  // with max. one tile to escape.
191  bool is_surrounded = false;
192  bool is_flanked = false;
193  int enemy_units_around = 0;
194  int accessible_tiles = 0;
196  get_adjacent_tiles(current_unit, adj.data());
197 
198  std::size_t tile;
199  for(tile = 0; tile != 3; ++tile) {
200 
201  const unit_map::const_iterator tmp_unit = units_.find(adj[tile]);
202  bool possible_flanked = false;
203 
204  if(map_.on_board(adj[tile]))
205  {
206  accessible_tiles++;
207  if (tmp_unit != units_.end() && current_team.is_enemy(tmp_unit->side()))
208  {
209  enemy_units_around++;
210  possible_flanked = true;
211  }
212  }
213 
214  const unit_map::const_iterator tmp_opposite_unit = units_.find(adj[tile + 3]);
215  if(map_.on_board(adj[tile + 3]))
216  {
217  accessible_tiles++;
218  if (tmp_opposite_unit != units_.end() && current_team.is_enemy(tmp_opposite_unit->side()))
219  {
220  enemy_units_around++;
221  if(possible_flanked)
222  {
223  is_flanked = true;
224  }
225  }
226  }
227  }
228 
229  if((is_flanked && enemy_units_around > 2) || enemy_units_around >= accessible_tiles - 1)
230  is_surrounded = true;
231 
232 
233 
234  double best_vulnerability = 0.0, best_support = 0.0;
235  int best_rating = 0;
236  int cur_position = -1;
237 
238  // Iterate over positions adjacent to the unit, finding the best rated one.
239  for(unsigned j = 0; j < tiles.size(); ++j) {
240 
241  // If in this planned attack, a unit is already in this location.
242  if(used_locations[j]) {
243  continue;
244  }
245 
246  // See if the current unit can reach that position.
247  if (tiles[j] != current_unit) {
248  typedef std::multimap<map_location,map_location>::const_iterator Itor;
249  std::pair<Itor,Itor> its = dstsrc.equal_range(tiles[j]);
250  while(its.first != its.second) {
251  if(its.first->second == current_unit)
252  break;
253  ++its.first;
254  }
255 
256  // If the unit can't move to this location.
257  if(its.first == its.second || units_.find(tiles[j]) != units_.end()) {
258  continue;
259  }
260  }
261 
262  int best_leadership_bonus = under_leadership(*unit_itor, tiles[j]);
263  double leadership_bonus = static_cast<double>(best_leadership_bonus+100)/100.0;
264  if (leadership_bonus > 1.1) {
265  LOG_AI << unit_itor->name() << " is getting leadership " << leadership_bonus << "\n";
266  }
267 
268  // Check to see whether this move would be a backstab.
269  int backstab_bonus = 1;
270  double surround_bonus = 1.0;
271 
272  if(tiles[(j+3)%6] != current_unit) {
273  const unit_map::const_iterator itor = units_.find(tiles[(j+3)%6]);
274 
275  // Note that we *could* also check if a unit plans to move there
276  // before we're at this stage, but we don't because, since the
277  // attack calculations don't actually take backstab into account (too complicated),
278  // this could actually make our analysis look *worse* instead of better.
279  // So we only check for 'concrete' backstab opportunities.
280  // That would also break backstab_check, since it assumes
281  // the defender is in place.
282  if(itor != units_.end() &&
283  backstab_check(tiles[j], loc, units_, teams_)) {
284  if(backstab) {
285  backstab_bonus = 2;
286  }
287 
288  // No surround bonus if target is skirmisher
289  if (!itor->get_ability_bool("skirmisher"))
290  surround_bonus = 1.2;
291  }
292 
293 
294  }
295 
296  // See if this position is the best rated we've seen so far.
297  int rating = static_cast<int>(rate_terrain(*unit_itor, tiles[j]) * backstab_bonus * leadership_bonus);
298  if(cur_position >= 0 && rating < best_rating) {
299  continue;
300  }
301 
302  // Find out how vulnerable we are to attack from enemy units in this hex.
303  //FIXME: suokko's r29531 multiplied this by a constant 1.5. ?
304  const double vulnerability = power_projection(tiles[j],enemy_dstsrc);//?
305 
306  // Calculate how much support we have on this hex from allies.
307  const double support = power_projection(tiles[j], fullmove_dstsrc);//?
308 
309  // If this is a position with equal defense to another position,
310  // but more vulnerability then we don't want to use it.
311  if(cur_position >= 0 && rating == best_rating && vulnerability/surround_bonus - support*surround_bonus >= best_vulnerability - best_support) {
312  continue;
313  }
314  cur_position = j;
315  best_rating = rating;
316  best_vulnerability = vulnerability/surround_bonus;
317  best_support = support*surround_bonus;
318  }
319 
320  if(cur_position != -1) {
321  units.erase(units.begin() + i);
322 
323  cur_analysis.movements.emplace_back(current_unit,tiles[cur_position]);
324 
325  cur_analysis.vulnerability += best_vulnerability;
326 
327  cur_analysis.support += best_support;
328 
329  cur_analysis.is_surrounded = is_surrounded;
330  cur_analysis.analyze(map_, units_, *this, dstsrc, srcdst, enemy_dstsrc, get_aggression());
331  result.push_back(cur_analysis);
332  used_locations[cur_position] = true;
333  do_attack_analysis(loc,srcdst,dstsrc,fullmove_srcdst,fullmove_dstsrc,enemy_srcdst,enemy_dstsrc,
334  tiles,used_locations,
335  units,result,cur_analysis, current_team);
336  used_locations[cur_position] = false;
337 
338 
339  cur_analysis.vulnerability -= best_vulnerability;
340  cur_analysis.support -= best_support;
341 
342  cur_analysis.movements.pop_back();
343 
344  units.insert(units.begin() + i, current_unit);
345  }
346  }
347 }
348 
350 {
351  const gamemap &map_ = resources::gameboard->map();
353  const int defense = u.defense_modifier(terrain);
354  int rating = 100 - defense;
355 
356  const int healing_value = 10;
357  const int friendly_village_value = 5;
358  const int neutral_village_value = 10;
359  const int enemy_village_value = 15;
360 
361  if(map_.gives_healing(terrain) && u.get_ability_bool("regenerate", loc) == false) {
362  rating += healing_value;
363  }
364 
365  if(map_.is_village(terrain)) {
366  int owner = resources::gameboard->village_owner(loc) + 1;
367 
368  if(owner == u.side()) {
369  rating += friendly_village_value;
370  } else if(owner == 0) {
371  rating += neutral_village_value;
372  } else {
373  rating += enemy_village_value;
374  }
375  }
376 
377  return rating;
378 }
379 
380 
382 {
384  if (filter_own_ && !filter_own_->empty()) {
385  cfg.add_child("filter_own", filter_own_->to_config());
386  }
387  if (filter_enemy_ && !filter_enemy_->empty()) {
388  cfg.add_child("filter_enemy", filter_enemy_->to_config());
389  }
390  return cfg;
391 }
392 
394 {
395  if(u.side() != get_side()) {
396  return false;
397  }
398  if (filter_own_) {
399  return (*filter_own_)(u);
400  }
401  return true;
402 }
403 
405 {
407  if(!my_team.is_enemy(u.side())) {
408  return false;
409  }
410  if (filter_enemy_) {
411  return (*filter_enemy_)(u);
412  }
413  return true;
414 }
415 
416 } // end of namespace testing_ai_default
417 
418 aspect_attacks_lua::aspect_attacks_lua(readonly_context &context, const config &cfg, const std::string &id, std::shared_ptr<lua_ai_context>& l_ctx)
419  : aspect_attacks_base(context, cfg, id)
420  , handler_(), code_(), params_(cfg.child_or_empty("args"))
421 {
422  this->name_ = "lua_aspect";
423  if (cfg.has_attribute("code"))
424  {
425  code_ = cfg["code"].str();
426  }
427  else if (cfg.has_attribute("value"))
428  {
429  code_ = "return " + cfg["value"].apply_visitor(lua_aspect_visitor());
430  }
431  else
432  {
433  // error
434  return;
435  }
436  handler_.reset(resources::lua_kernel->create_lua_ai_action_handler(code_.c_str(), *l_ctx));
437 }
438 
440 {
442  handler_->handle(params_, true, obj_);
443  aspect_attacks_lua_filter filt = *obj_->get();
444  aspect_attacks_base::recalculate();
445  if(filt.lua) {
446  if(filt.ref_own_ != -1) {
448  }
449  if(filt.ref_enemy_ != -1) {
451  }
452  }
453  obj_.reset();
454 }
455 
457 {
458  config cfg = aspect::to_config();
459  cfg["code"] = code_;
460  if (!params_.empty()) {
461  cfg.add_child("args", params_);
462  }
463  return cfg;
464 }
465 
466 static bool call_lua_filter_fcn(lua_State* L, const unit& u, int idx)
467 {
470  luaW_pcall(L, 1, 1);
471  bool result = luaW_toboolean(L, -1);
472  lua_pop(L, 1);
473  return result;
474 }
475 
477 {
478  const aspect_attacks_lua_filter& filt = *obj_->get();
479  if(filt.lua && filt.ref_own_ != -1) {
480  return call_lua_filter_fcn(filt.lua, u, filt.ref_own_);
481  } else if(filt.filter_own_) {
482  return (*filt.filter_own_)(u);
483  } else {
484  return true;
485  }
486 }
487 
489 {
490  const aspect_attacks_lua_filter& filt = *obj_->get();
491  if(filt.lua && filt.ref_enemy_ != -1) {
492  return call_lua_filter_fcn(filt.lua, u, filt.ref_enemy_);
493  } else if(filt.filter_enemy_) {
494  return (*filt.filter_enemy_)(u);
495  } else {
496  return true;
497  }
498 }
499 
500 } // end of namespace ai
int village_owner(const map_location &loc) const
Given the location of a village, will return the 0-based index of the team that currently owns it...
static std::unique_ptr< class sdl_event_handler > handler_
Definition: handler.cpp:62
config & child(config_key_type key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
Definition: config.cpp:420
virtual const move_map & get_enemy_srcdst() const override
Definition: contexts.hpp:700
lua_unit * luaW_pushunit(lua_State *L, Args... args)
Definition: lua_unit.hpp:114
unit_iterator end()
Definition: map.hpp:415
aspect_attacks_lua(readonly_context &context, const config &cfg, const std::string &id, std::shared_ptr< lua_ai_context > &l_ctx)
virtual const std::vector< team > & teams() const override
Definition: game_board.hpp:92
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:517
virtual const unit_map & units() const override
Definition: game_board.hpp:114
static bool call_lua_filter_fcn(lua_State *L, const unit &u, int idx)
bool luaW_pcall(lua_State *L, int nArgs, int nRets, bool allow_wml_error)
Calls a Lua function stored below its nArgs arguments at the top of the stack.
This class represents a single unit of a specific type.
Definition: unit.hpp:99
static manager & get_singleton()
Definition: manager.hpp:150
LUA_API int lua_rawgeti(lua_State *L, int idx, lua_Integer n)
Definition: lapi.cpp:658
Various functions that implement attacks and attack calculations.
bool has_attribute(config_key_type key) const
Definition: config.cpp:213
#define a
virtual bool is_allowed_attacker(const unit &u) const
int under_leadership(const unit &u, const map_location &loc, const_attack_ptr weapon, const_attack_ptr opp_weapon)
Tests if the unit at loc is currently affected by leadership.
Definition: attack.cpp:1571
virtual const move_map & get_srcdst() const override
Definition: contexts.hpp:827
map_location target
Definition: contexts.hpp:85
virtual const gamemap & map() const override
Definition: game_board.hpp:109
double vulnerability
The vulnerability is the power projection of enemy units onto the hex we&#39;re standing on...
Definition: contexts.hpp:122
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
Definition: translation.hpp:50
bool on_board(const map_location &loc) const
Tell if a location is on the map.
Definition: map.cpp:377
aspect_attacks_base(readonly_context &context, const config &cfg, const std::string &id)
std::shared_ptr< attacks_vector > value_
Definition: aspect.hpp:181
-file sdl_utils.hpp
#define LOG_AI
#define lua_pop(L, n)
Definition: lua.h:344
t_translation::terrain_code get_terrain(const map_location &loc) const
Looks up terrain at a particular location.
Definition: map.cpp:309
virtual unit_stats_cache_t & unit_stats_cache() const override
Definition: contexts.hpp:968
std::multimap< map_location, map_location > move_map
The standard way in which a map of possible moves is recorded.
Definition: game_info.hpp:42
std::vector< attack_analysis > attacks_vector
Definition: game_info.hpp:50
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:45
std::shared_ptr< unit_filter > filter_enemy_
int defense_modifier(const t_translation::terrain_code &terrain) const
The unit&#39;s defense on a given terrain.
Definition: unit.cpp:1616
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:44
A small explanation about what&#39;s going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:58
const vconfig & make_safe() const
instruct the vconfig to make a private copy of its underlying data.
Definition: variable.cpp:127
std::string name_
Definition: aspect.hpp:102
int gives_healing(const map_location &loc) const
Definition: map.cpp:67
team & get_team(int i)
Definition: game_board.hpp:104
static lg::log_domain log_ai_testing_aspect_attacks("ai/aspect/attacks")
void analyze(const gamemap &map, unit_map &units, const readonly_context &ai_obj, const move_map &dstsrc, const move_map &srcdst, const move_map &enemy_dstsrc, double aggression)
Definition: attack.cpp:45
std::shared_ptr< unit_filter > filter_own_
std::shared_ptr< unit_filter > filter_enemy_
bool luaW_toboolean(lua_State *L, int n)
Definition: lua_common.cpp:870
game_board * gameboard
Definition: resources.cpp:20
virtual bool get_passive_leader() const override
Definition: contexts.hpp:767
Encapsulates the map of the game.
Definition: map.hpp:36
aspect_attacks(readonly_context &context, const config &cfg, const std::string &id)
bool is_enemy(int n) const
Definition: team.hpp:243
virtual const move_map & get_enemy_dstsrc() const override
Definition: contexts.hpp:688
Managing the AIs lifecycle - headers TODO: Refactor history handling and internal commands...
virtual config to_config() const
Definition: aspect.cpp:106
std::array< map_location, 6 > adjacent_loc_array_t
Definition: location.hpp:170
static const ::config * terrain
The terrain used to create the cache.
Definition: minimap.cpp:130
Encapsulates the map of the game.
Definition: location.hpp:42
unit_iterator find(std::size_t id)
Definition: map.cpp:311
bool get_ability_bool(const std::string &tag_name, const map_location &loc) const
Checks whether this unit currently possesses or is affected by a given ability.
Definition: abilities.cpp:143
void raise_user_interact()
Notifies all observers of &#39;ai_user_interact&#39; event.
Definition: manager.cpp:402
bool backstab_check(const map_location &attacker_loc, const map_location &defender_loc, const unit_map &units, const std::vector< team > &teams)
Function to check if an attack will satisfy the requirements for backstab.
Definition: attack.cpp:1811
virtual bool is_allowed_attacker(const unit &u) const
std::size_t i
Definition: function.cpp:933
virtual const team & current_team() const override
Definition: contexts.hpp:524
std::shared_ptr< lua_ai_action_handler > handler_
std::vector< std::pair< map_location, map_location > > movements
Definition: contexts.hpp:86
virtual double power_projection(const map_location &loc, const move_map &dstsrc) const override
Function which finds how much &#39;power&#39; a side can attack a certain location with.
Definition: contexts.hpp:785
virtual bool is_allowed_enemy(const unit &u) const
virtual side_number get_side() const override
Get the side number.
Definition: contexts.hpp:465
Aspect: attacks.
std::shared_ptr< unit_filter > filter_own_
config & add_child(config_key_type key)
Definition: config.cpp:476
virtual void recalculate() const
bool valid_
Definition: aspect.hpp:93
bool is_village(const map_location &loc) const
Definition: map.cpp:65
virtual const move_map & get_dstsrc() const override
Definition: contexts.hpp:682
#define LUA_REGISTRYINDEX
Definition: lua.h:42
virtual double get_aggression() const override
Definition: contexts.hpp:626
virtual void calculate_possible_moves(std::map< map_location, pathfind::paths > &possible_moves, move_map &srcdst, move_map &dstsrc, bool enemy, bool assume_full_movement=false, const terrain_filter *remove_destinations=nullptr) const override
Definition: contexts.hpp:573
void do_attack_analysis(const map_location &loc, const move_map &srcdst, const move_map &dstsrc, const move_map &fullmove_srcdst, const move_map &fullmove_dstsrc, const move_map &enemy_srcdst, const move_map &enemy_dstsrc, const adjacent_loc_array_t &tiles, bool *used_locations, std::vector< map_location > &units, std::vector< attack_analysis > &result, attack_analysis &cur_analysis, const team &current_team) const
A variable-expanding proxy for the config class.
Definition: variable.hpp:42
Standard logging facilities (interface).
game_lua_kernel * lua_kernel
Definition: resources.cpp:25
Container associating units to locations.
Definition: map.hpp:99
LUALIB_API void luaL_unref(lua_State *L, int t, int ref)
Definition: lauxlib.cpp:616
bool is_surrounded
Is true if the units involved in this attack sequence are surrounded.
Definition: contexts.hpp:131
int side() const
The side this unit belongs to.
Definition: unit.hpp:303
virtual bool is_allowed_enemy(const unit &u) const
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:92
std::shared_ptr< lua_object< aspect_attacks_lua_filter > > obj_
static int rate_terrain(const unit &u, const map_location &loc)
virtual config to_config() const
This module contains various pathfinding functions and utilities.
std::size_t underlying_id() const
This unit&#39;s unique internal ID.
Definition: unit.hpp:355
bool empty() const
Definition: config.cpp:884
std::shared_ptr< attacks_vector > analyze_targets() const