The Battle for Wesnoth  1.15.0-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 http://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 /**
16  * 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 "resources.hpp"
29 #include "units/unit.hpp"
30 #include "pathfind/pathfind.hpp"
31 #include "units/filter.hpp"
32 #include "scripting/lua_unit.hpp"
33 #include "lua/lauxlib.h"
34 
35 namespace ai {
36 
37 namespace ai_default_rca {
38 
39 static lg::log_domain log_ai_testing_aspect_attacks("ai/aspect/attacks");
40 #define DBG_AI LOG_STREAM(debug, log_ai_testing_aspect_attacks)
41 #define LOG_AI LOG_STREAM(info, log_ai_testing_aspect_attacks)
42 #define ERR_AI LOG_STREAM(err, log_ai_testing_aspect_attacks)
43 
44 aspect_attacks_base::aspect_attacks_base(readonly_context &context, const config &cfg, const std::string &id)
45  : typesafe_aspect<attacks_vector>(context,cfg,id)
46 {
47 }
48 
49 aspect_attacks::aspect_attacks(readonly_context &context, const config &cfg, const std::string &id)
50  : aspect_attacks_base(context,cfg,id)
51  , filter_own_()
52  , filter_enemy_()
53 {
54  if (const config &filter_own = cfg.child("filter_own")) {
55  vconfig vcfg(filter_own);
56  vcfg.make_safe();
57  filter_own_.reset(new unit_filter(vcfg));
58  }
59  if (const config &filter_enemy = cfg.child("filter_enemy")) {
60  vconfig vcfg(filter_enemy);
61  vcfg.make_safe();
62  filter_enemy_.reset(new unit_filter(vcfg));
63  }
64 }
65 
67 {
68  this->value_ = analyze_targets();
69  this->valid_ = true;
70 }
71 
72 std::shared_ptr<attacks_vector> aspect_attacks_base::analyze_targets() const
73 {
74  const move_map& srcdst = get_srcdst();
75  const move_map& dstsrc = get_dstsrc();
76  const move_map& enemy_srcdst = get_enemy_srcdst();
77  const move_map& enemy_dstsrc = get_enemy_dstsrc();
78 
79  std::shared_ptr<attacks_vector> res(new attacks_vector());
80  unit_map& units_ = resources::gameboard->units();
81 
82  std::vector<map_location> unit_locs;
83  for(unit_map::const_iterator i = units_.begin(); i != units_.end(); ++i) {
84  if (i->side() == get_side() && i->attacks_left() && !(i->can_recruit() && get_passive_leader())) {
85  if (!is_allowed_attacker(*i)) {
86  continue;
87  }
88  unit_locs.push_back(i->get_location());
89  }
90  }
91 
92  bool used_locations[6];
93  std::fill(used_locations,used_locations+6,false);
94 
95  moves_map dummy_moves;
96  move_map fullmove_srcdst, fullmove_dstsrc;
97  calculate_possible_moves(dummy_moves,fullmove_srcdst,fullmove_dstsrc,false,true);
98 
99  unit_stats_cache().clear();
100 
101  for(unit_map::const_iterator j = units_.begin(); j != units_.end(); ++j) {
102 
103  // Attack anyone who is on the enemy side,
104  // and who is not invisible or petrified.
105  if (current_team().is_enemy(j->side()) && !j->incapacitated() &&
106  !j->invisible(j->get_location()))
107  {
108  if (!is_allowed_enemy(*j)) {
109  continue;
110  }
111  adjacent_loc_array_t adjacent;
112  get_adjacent_tiles(j->get_location(), adjacent.data());
113  attack_analysis analysis;
114  analysis.target = j->get_location();
115  analysis.vulnerability = 0.0;
116  analysis.support = 0.0;
117  do_attack_analysis(j->get_location(), srcdst, dstsrc,
118  fullmove_srcdst, fullmove_dstsrc, enemy_srcdst, enemy_dstsrc,
119  adjacent,used_locations,unit_locs,*res,analysis, current_team());
120  }
121  }
122  return res;
123 }
124 
125 
126 
128  const map_location& loc,
129  const move_map& srcdst, const move_map& dstsrc,
130  const move_map& fullmove_srcdst, const move_map& fullmove_dstsrc,
131  const move_map& enemy_srcdst, const move_map& enemy_dstsrc,
132  const adjacent_loc_array_t& tiles, bool* used_locations,
133  std::vector<map_location>& units,
134  std::vector<attack_analysis>& result,
135  attack_analysis& cur_analysis,
136  const team &current_team
137  ) const
138 {
139  // This function is called fairly frequently, so interact with the user here.
140 
142  const int default_attack_depth = 5;
143  if(cur_analysis.movements.size() >= std::size_t(default_attack_depth)) {
144  //std::cerr << "ANALYSIS " << cur_analysis.movements.size() << " >= " << get_attack_depth() << "\n";
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  unit_ability_list abil = unit_itor->get_abilities("leadership",tiles[j]);
263  int best_leadership_bonus = abil.highest("value").first;
264  double leadership_bonus = static_cast<double>(best_leadership_bonus+100)/100.0;
265  if (leadership_bonus > 1.1) {
266  LOG_AI << unit_itor->name() << " is getting leadership " << leadership_bonus << "\n";
267  }
268 
269  // Check to see whether this move would be a backstab.
270  int backstab_bonus = 1;
271  double surround_bonus = 1.0;
272 
273  if(tiles[(j+3)%6] != current_unit) {
274  const unit_map::const_iterator itor = units_.find(tiles[(j+3)%6]);
275 
276  // Note that we *could* also check if a unit plans to move there
277  // before we're at this stage, but we don't because, since the
278  // attack calculations don't actually take backstab into account (too complicated),
279  // this could actually make our analysis look *worse* instead of better.
280  // So we only check for 'concrete' backstab opportunities.
281  // That would also break backstab_check, since it assumes
282  // the defender is in place.
283  if(itor != units_.end() &&
284  backstab_check(tiles[j], loc, units_, teams_)) {
285  if(backstab) {
286  backstab_bonus = 2;
287  }
288 
289  // No surround bonus if target is skirmisher
290  if (!itor->get_ability_bool("skirmisher"))
291  surround_bonus = 1.2;
292  }
293 
294 
295  }
296 
297  // See if this position is the best rated we've seen so far.
298  int rating = static_cast<int>(rate_terrain(*unit_itor, tiles[j]) * backstab_bonus * leadership_bonus);
299  if(cur_position >= 0 && rating < best_rating) {
300  continue;
301  }
302 
303  // Find out how vulnerable we are to attack from enemy units in this hex.
304  //FIXME: suokko's r29531 multiplied this by a constant 1.5. ?
305  const double vulnerability = power_projection(tiles[j],enemy_dstsrc);//?
306 
307  // Calculate how much support we have on this hex from allies.
308  const double support = power_projection(tiles[j], fullmove_dstsrc);//?
309 
310  // If this is a position with equal defense to another position,
311  // but more vulnerability then we don't want to use it.
312  if(cur_position >= 0 && rating == best_rating && vulnerability/surround_bonus - support*surround_bonus >= best_vulnerability - best_support) {
313  continue;
314  }
315  cur_position = j;
316  best_rating = rating;
317  best_vulnerability = vulnerability/surround_bonus;
318  best_support = support*surround_bonus;
319  }
320 
321  if(cur_position != -1) {
322  units.erase(units.begin() + i);
323 
324  cur_analysis.movements.emplace_back(current_unit,tiles[cur_position]);
325 
326  cur_analysis.vulnerability += best_vulnerability;
327 
328  cur_analysis.support += best_support;
329 
330  cur_analysis.is_surrounded = is_surrounded;
331  cur_analysis.analyze(map_, units_, *this, dstsrc, srcdst, enemy_dstsrc, get_aggression());
332  result.push_back(cur_analysis);
333  used_locations[cur_position] = true;
334  do_attack_analysis(loc,srcdst,dstsrc,fullmove_srcdst,fullmove_dstsrc,enemy_srcdst,enemy_dstsrc,
335  tiles,used_locations,
336  units,result,cur_analysis, current_team);
337  used_locations[cur_position] = false;
338 
339 
340  cur_analysis.vulnerability -= best_vulnerability;
341  cur_analysis.support -= best_support;
342 
343  cur_analysis.movements.pop_back();
344 
345  units.insert(units.begin() + i, current_unit);
346  }
347  }
348 }
349 
351 {
352  const gamemap &map_ = resources::gameboard->map();
353  const t_translation::terrain_code terrain = map_.get_terrain(loc);
354  const int defense = u.defense_modifier(terrain);
355  int rating = 100 - defense;
356 
357  const int healing_value = 10;
358  const int friendly_village_value = 5;
359  const int neutral_village_value = 10;
360  const int enemy_village_value = 15;
361 
362  if(map_.gives_healing(terrain) && u.get_ability_bool("regenerate", loc) == false) {
363  rating += healing_value;
364  }
365 
366  if(map_.is_village(terrain)) {
367  int owner = resources::gameboard->village_owner(loc) + 1;
368 
369  if(owner == u.side()) {
370  rating += friendly_village_value;
371  } else if(owner == 0) {
372  rating += neutral_village_value;
373  } else {
374  rating += enemy_village_value;
375  }
376  }
377 
378  return rating;
379 }
380 
381 
383 {
385  if (filter_own_ && !filter_own_->empty()) {
386  cfg.add_child("filter_own", filter_own_->to_config());
387  }
388  if (filter_enemy_ && !filter_enemy_->empty()) {
389  cfg.add_child("filter_enemy", filter_enemy_->to_config());
390  }
391  return cfg;
392 }
393 
395 {
396  if(u.side() != get_side()) {
397  return false;
398  }
399  if (filter_own_) {
400  return (*filter_own_)(u);
401  }
402  return true;
403 }
404 
406 {
408  if(!my_team.is_enemy(u.side())) {
409  return false;
410  }
411  if (filter_enemy_) {
412  return (*filter_enemy_)(u);
413  }
414  return true;
415 }
416 
417 } // end of namespace testing_ai_default
418 
419 aspect_attacks_lua::aspect_attacks_lua(readonly_context &context, const config &cfg, const std::string &id, std::shared_ptr<lua_ai_context>& l_ctx)
420  : aspect_attacks_base(context, cfg, id)
421  , handler_(), code_(), params_(cfg.child_or_empty("args"))
422 {
423  this->name_ = "lua_aspect";
424  if (cfg.has_attribute("code"))
425  {
426  code_ = cfg["code"].str();
427  }
428  else if (cfg.has_attribute("value"))
429  {
430  code_ = "return " + cfg["value"].apply_visitor(lua_aspect_visitor());
431  }
432  else
433  {
434  // error
435  return;
436  }
437  handler_.reset(resources::lua_kernel->create_lua_ai_action_handler(code_.c_str(), *l_ctx));
438 }
439 
441 {
443  handler_->handle(params_, true, obj_);
444  aspect_attacks_lua_filter filt = *obj_->get();
445  aspect_attacks_base::recalculate();
446  if(filt.lua) {
447  if(filt.ref_own_ != -1) {
449  }
450  if(filt.ref_enemy_ != -1) {
452  }
453  }
454  obj_.reset();
455 }
456 
458 {
459  config cfg = aspect::to_config();
460  cfg["code"] = code_;
461  if (!params_.empty()) {
462  cfg.add_child("args", params_);
463  }
464  return cfg;
465 }
466 
467 static bool call_lua_filter_fcn(lua_State* L, const unit& u, int idx)
468 {
471  luaW_pcall(L, 1, 1);
472  bool result = luaW_toboolean(L, -1);
473  lua_pop(L, 1);
474  return result;
475 }
476 
478 {
479  const aspect_attacks_lua_filter& filt = *obj_->get();
480  if(filt.lua && filt.ref_own_ != -1) {
481  return call_lua_filter_fcn(filt.lua, u, filt.ref_own_);
482  } else if(filt.filter_own_) {
483  return (*filt.filter_own_)(u);
484  } else {
485  return true;
486  }
487 }
488 
490 {
491  const aspect_attacks_lua_filter& filt = *obj_->get();
492  if(filt.lua && filt.ref_enemy_ != -1) {
493  return call_lua_filter_fcn(filt.lua, u, filt.ref_enemy_);
494  } else if(filt.filter_enemy_) {
495  return (*filt.filter_enemy_)(u);
496  } else {
497  return true;
498  }
499 }
500 
501 } // 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:54
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:423
virtual const move_map & get_enemy_srcdst() const override
Definition: contexts.hpp:681
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.
Definition: lua_common.cpp:982
This class represents a single unit of a specific type.
Definition: unit.hpp:99
static manager & get_singleton()
Definition: manager.hpp:152
LUA_API int lua_rawgeti(lua_State *L, int idx, lua_Integer n)
Definition: lapi.cpp:657
Various functions that implement attacks and attack calculations.
bool has_attribute(config_key_type key) const
Definition: config.cpp:217
#define a
virtual bool is_allowed_attacker(const unit &u) const
virtual const move_map & get_srcdst() const override
Definition: contexts.hpp:791
map_location target
Definition: contexts.hpp:86
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:123
unit_iterator begin()
Definition: map.hpp:405
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
bool on_board(const map_location &loc) const
Tell if a location is on the map.
Definition: map.cpp:367
aspect_attacks_base(readonly_context &context, const config &cfg, const std::string &id)
std::shared_ptr< attacks_vector > value_
Definition: aspect.hpp:182
-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:299
virtual unit_stats_cache_t & unit_stats_cache() const override
Definition: contexts.hpp:917
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:1561
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:103
int gives_healing(const map_location &loc) const
Definition: map.cpp:66
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:736
Encapsulates the map of the game.
Definition: map.hpp:34
aspect_attacks(readonly_context &context, const config &cfg, const std::string &id)
bool is_enemy(int n) const
Definition: team.hpp:241
virtual const move_map & get_enemy_dstsrc() const override
Definition: contexts.hpp:671
Managing the AIs lifecycle - headers.
virtual config to_config() const
Definition: aspect.cpp:120
std::array< map_location, 6 > adjacent_loc_array_t
Definition: location.hpp:170
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:140
void raise_user_interact()
Notifies all observers of &#39;ai_user_interact&#39; event.
Definition: manager.cpp:408
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:1680
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:521
std::shared_ptr< lua_ai_action_handler > handler_
std::vector< std::pair< map_location, map_location > > movements
Definition: contexts.hpp:87
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:751
virtual bool is_allowed_enemy(const unit &u) const
virtual side_number get_side() const override
Get the side number.
Definition: contexts.hpp:464
Aspect: attacks.
std::shared_ptr< unit_filter > filter_own_
config & add_child(config_key_type key)
Definition: config.cpp:479
virtual void recalculate() const
bool valid_
Definition: aspect.hpp:93
bool is_village(const map_location &loc) const
Definition: map.cpp:64
virtual const move_map & get_dstsrc() const override
Definition: contexts.hpp:666
#define LUA_REGISTRYINDEX
Definition: lua.h:42
virtual double get_aggression() const override
Definition: contexts.hpp:616
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:558
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:132
int side() const
The side this unit belongs to.
Definition: unit.hpp:265
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:68
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:317
bool empty() const
Definition: config.cpp:837
std::shared_ptr< attacks_vector > analyze_targets() const
std::pair< int, map_location > highest(const std::string &key, int def=0) const
Definition: unit.hpp:56