The Battle for Wesnoth  1.13.10+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
aspect_attacks.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2017 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 "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 
46  : typesafe_aspect<attacks_vector>(context,cfg,id)
47 {
48 }
49 
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();
59  }
60  if (const config &filter_enemy = cfg.child("filter_enemy")) {
61  vconfig vcfg(filter_enemy);
62  vcfg.make_safe();
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  std::shared_ptr<attacks_vector> res(new 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(), *resources::gameboard))
108  {
109  if (!is_allowed_enemy(*j)) {
110  continue;
111  }
112  map_location adjacent[6];
113  get_adjacent_tiles(j->get_location(), adjacent);
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 map_location* 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 default_attack_depth = 5;
144  if(cur_analysis.movements.size() >= size_t(default_attack_depth)) {
145  //std::cerr << "ANALYSIS " << cur_analysis.movements.size() << " >= " << get_attack_depth() << "\n";
146  return;
147  }
148  const gamemap &map_ = resources::gameboard->map();
149  unit_map &units_ = resources::gameboard->units();
150  std::vector<team> &teams_ = resources::gameboard->teams();
151 
152 
153  const size_t max_positions = 1000;
154  if(result.size() > max_positions && !cur_analysis.movements.empty()) {
155  LOG_AI << "cut analysis short with number of positions\n";
156  return;
157  }
158 
159  for(size_t i = 0; i != units.size(); ++i) {
160  const map_location current_unit = units[i];
161 
162  unit_map::iterator unit_itor = units_.find(current_unit);
163  assert(unit_itor != units_.end());
164 
165  // See if the unit has the backstab ability.
166  // Units with backstab will want to try to have a
167  // friendly unit opposite the position they move to.
168  //
169  // See if the unit has the slow ability -- units with slow only attack first.
170  bool backstab = false, slow = false;
171  for(const attack_type& a : unit_itor->attacks()) {
172  // For speed, just assume these specials will be active if
173  // they are present.
174  if ( a.get_special_bool("backstab", true) ) {
175  backstab = true;
176  }
177 
178  if ( a.get_special_bool("slow", true) ) {
179  slow = true;
180  }
181  }
182 
183  if(slow && cur_analysis.movements.empty() == false) {
184  continue;
185  }
186 
187  // Check if the friendly unit is surrounded,
188  // A unit is surrounded if it is flanked by enemy units
189  // and at least one other enemy unit is nearby
190  // or if the unit is totaly surrounded by enemies
191  // with max. one tile to escape.
192  bool is_surrounded = false;
193  bool is_flanked = false;
194  int enemy_units_around = 0;
195  int accessible_tiles = 0;
196  map_location adj[6];
197  get_adjacent_tiles(current_unit, adj);
198 
199  size_t tile;
200  for(tile = 0; tile != 3; ++tile) {
201 
202  const unit_map::const_iterator tmp_unit = units_.find(adj[tile]);
203  bool possible_flanked = false;
204 
205  if(map_.on_board(adj[tile]))
206  {
207  accessible_tiles++;
208  if (tmp_unit != units_.end() && current_team.is_enemy(tmp_unit->side()))
209  {
210  enemy_units_around++;
211  possible_flanked = true;
212  }
213  }
214 
215  const unit_map::const_iterator tmp_opposite_unit = units_.find(adj[tile + 3]);
216  if(map_.on_board(adj[tile + 3]))
217  {
218  accessible_tiles++;
219  if (tmp_opposite_unit != units_.end() && current_team.is_enemy(tmp_opposite_unit->side()))
220  {
221  enemy_units_around++;
222  if(possible_flanked)
223  {
224  is_flanked = true;
225  }
226  }
227  }
228  }
229 
230  if((is_flanked && enemy_units_around > 2) || enemy_units_around >= accessible_tiles - 1)
231  is_surrounded = true;
232 
233 
234 
235  double best_vulnerability = 0.0, best_support = 0.0;
236  int best_rating = 0;
237  int cur_position = -1;
238 
239  // Iterate over positions adjacent to the unit, finding the best rated one.
240  for(int j = 0; j != 6; ++j) {
241 
242  // If in this planned attack, a unit is already in this location.
243  if(used_locations[j]) {
244  continue;
245  }
246 
247  // See if the current unit can reach that position.
248  if (tiles[j] != current_unit) {
249  typedef std::multimap<map_location,map_location>::const_iterator Itor;
250  std::pair<Itor,Itor> its = dstsrc.equal_range(tiles[j]);
251  while(its.first != its.second) {
252  if(its.first->second == current_unit)
253  break;
254  ++its.first;
255  }
256 
257  // If the unit can't move to this location.
258  if(its.first == its.second || units_.find(tiles[j]) != units_.end()) {
259  continue;
260  }
261  }
262 
263  unit_ability_list abil = unit_itor->get_abilities("leadership",tiles[j]);
264  int best_leadership_bonus = abil.highest("value").first;
265  double leadership_bonus = static_cast<double>(best_leadership_bonus+100)/100.0;
266  if (leadership_bonus > 1.1) {
267  LOG_AI << unit_itor->name() << " is getting leadership " << leadership_bonus << "\n";
268  }
269 
270  // Check to see whether this move would be a backstab.
271  int backstab_bonus = 1;
272  double surround_bonus = 1.0;
273 
274  if(tiles[(j+3)%6] != current_unit) {
275  const unit_map::const_iterator itor = units_.find(tiles[(j+3)%6]);
276 
277  // Note that we *could* also check if a unit plans to move there
278  // before we're at this stage, but we don't because, since the
279  // attack calculations don't actually take backstab into account (too complicated),
280  // this could actually make our analysis look *worse* instead of better.
281  // So we only check for 'concrete' backstab opportunities.
282  // That would also break backstab_check, since it assumes
283  // the defender is in place.
284  if(itor != units_.end() &&
285  backstab_check(tiles[j], loc, units_, teams_)) {
286  if(backstab) {
287  backstab_bonus = 2;
288  }
289 
290  // No surround bonus if target is skirmisher
291  if (!itor->get_ability_bool("skirmisher", *resources::gameboard))
292  surround_bonus = 1.2;
293  }
294 
295 
296  }
297 
298  // See if this position is the best rated we've seen so far.
299  int rating = static_cast<int>(rate_terrain(*unit_itor, tiles[j]) * backstab_bonus * leadership_bonus);
300  if(cur_position >= 0 && rating < best_rating) {
301  continue;
302  }
303 
304  // Find out how vulnerable we are to attack from enemy units in this hex.
305  //FIXME: suokko's r29531 multiplied this by a constant 1.5. ?
306  const double vulnerability = power_projection(tiles[j],enemy_dstsrc);//?
307 
308  // Calculate how much support we have on this hex from allies.
309  const double support = power_projection(tiles[j], fullmove_dstsrc);//?
310 
311  // If this is a position with equal defense to another position,
312  // but more vulnerability then we don't want to use it.
313  if(cur_position >= 0 && rating == best_rating && vulnerability/surround_bonus - support*surround_bonus >= best_vulnerability - best_support) {
314  continue;
315  }
316  cur_position = j;
317  best_rating = rating;
318  best_vulnerability = vulnerability/surround_bonus;
319  best_support = support*surround_bonus;
320  }
321 
322  if(cur_position != -1) {
323  units.erase(units.begin() + i);
324 
325  cur_analysis.movements.emplace_back(current_unit,tiles[cur_position]);
326 
327  cur_analysis.vulnerability += best_vulnerability;
328 
329  cur_analysis.support += best_support;
330 
331  cur_analysis.is_surrounded = is_surrounded;
332  cur_analysis.analyze(map_, units_, *this, dstsrc, srcdst, enemy_dstsrc, get_aggression());
333  result.push_back(cur_analysis);
334  used_locations[cur_position] = true;
335  do_attack_analysis(loc,srcdst,dstsrc,fullmove_srcdst,fullmove_dstsrc,enemy_srcdst,enemy_dstsrc,
336  tiles,used_locations,
337  units,result,cur_analysis, current_team);
338  used_locations[cur_position] = false;
339 
340 
341  cur_analysis.vulnerability -= best_vulnerability;
342  cur_analysis.support -= best_support;
343 
344  cur_analysis.movements.pop_back();
345 
346  units.insert(units.begin() + i, current_unit);
347  }
348  }
349 }
350 
352 {
353  const gamemap &map_ = resources::gameboard->map();
355  const int defense = u.defense_modifier(terrain);
356  int rating = 100 - defense;
357 
358  const int healing_value = 10;
359  const int friendly_village_value = 5;
360  const int neutral_village_value = 10;
361  const int enemy_village_value = 15;
362 
363  if(map_.gives_healing(terrain) && u.get_ability_bool("regenerate", loc, *resources::gameboard) == false) {
364  rating += healing_value;
365  }
366 
367  if(map_.is_village(terrain)) {
368  int owner = resources::gameboard->village_owner(loc) + 1;
369 
370  if(owner == u.side()) {
371  rating += friendly_village_value;
372  } else if(owner == 0) {
373  rating += neutral_village_value;
374  } else {
375  rating += enemy_village_value;
376  }
377  }
378 
379  return rating;
380 }
381 
382 
384 {
386  if (filter_own_ && !filter_own_->empty()) {
387  cfg.add_child("filter_own", filter_own_->to_config());
388  }
389  if (filter_enemy_ && !filter_enemy_->empty()) {
390  cfg.add_child("filter_enemy", filter_enemy_->to_config());
391  }
392  return cfg;
393 }
394 
396 {
397  if(u.side() != get_side()) {
398  return false;
399  }
400  if (filter_own_) {
401  return (*filter_own_)(u);
402  }
403  return true;
404 }
405 
407 {
409  if(!my_team.is_enemy(u.side())) {
410  return false;
411  }
412  if (filter_enemy_) {
413  return (*filter_enemy_)(u);
414  }
415  return true;
416 }
417 
418 } // end of namespace testing_ai_default
419 
420 aspect_attacks_lua::aspect_attacks_lua(readonly_context &context, const config &cfg, const std::string &id, std::shared_ptr<lua_ai_context>& l_ctx)
421  : aspect_attacks_base(context, cfg, id)
422  , handler_(), code_(), params_(cfg.child_or_empty("args"))
423 {
424  this->name_ = "lua_aspect";
425  if (cfg.has_attribute("code"))
426  {
427  code_ = cfg["code"].str();
428  }
429  else if (cfg.has_attribute("value"))
430  {
431  code_ = "return " + cfg["value"].apply_visitor(lua_aspect_visitor());
432  }
433  else
434  {
435  // error
436  return;
437  }
438  handler_.reset(resources::lua_kernel->create_lua_ai_action_handler(code_.c_str(), *l_ctx));
439 }
440 
442 {
444  handler_->handle(params_, true, obj_);
445  aspect_attacks_lua_filter filt = *obj_->get();
446  aspect_attacks_base::recalculate();
447  if(filt.lua) {
448  if(filt.ref_own_ != -1) {
450  }
451  if(filt.ref_enemy_ != -1) {
453  }
454  }
455  obj_.reset();
456 }
457 
459 {
460  config cfg = aspect::to_config();
461  cfg["code"] = code_;
462  if (!params_.empty()) {
463  cfg.add_child("args", params_);
464  }
465  return cfg;
466 }
467 
468 static bool call_lua_filter_fcn(lua_State* L, const unit& u, int idx)
469 {
472  luaW_pcall(L, 1, 1);
473  bool result = luaW_toboolean(L, -1);
474  lua_pop(L, 1);
475  return result;
476 }
477 
479 {
480  const aspect_attacks_lua_filter& filt = *obj_->get();
481  if(filt.lua && filt.ref_own_ != -1) {
482  return call_lua_filter_fcn(filt.lua, u, filt.ref_own_);
483  } else if(filt.filter_own_) {
484  return (*filt.filter_own_)(u);
485  } else {
486  return true;
487  }
488 }
489 
491 {
492  const aspect_attacks_lua_filter& filt = *obj_->get();
493  if(filt.lua && filt.ref_enemy_ != -1) {
494  return call_lua_filter_fcn(filt.lua, u, filt.ref_enemy_);
495  } else if(filt.filter_enemy_) {
496  return (*filt.filter_enemy_)(u);
497  } else {
498  return true;
499  }
500 }
501 
502 } // end of namespace ai
static std::unique_ptr< class sdl_event_handler > handler_
Definition: handler.cpp:62
virtual config to_config() const
Definition: aspect.cpp:117
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:400
virtual const move_map & get_enemy_srcdst() const override
Definition: contexts.hpp:718
virtual const unit_map & units() const
Definition: game_board.hpp:97
unit_iterator end()
Definition: map.hpp:415
std::vector< char_t > string
aspect_attacks_lua(readonly_context &context, const config &cfg, const std::string &id, std::shared_ptr< lua_ai_context > &l_ctx)
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:960
This class represents a single unit of a specific type.
Definition: unit.hpp:100
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.
std::shared_ptr< attacks_vector > analyze_targets() const
#define a
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.hpp:299
bool is_village(const map_location &loc) const
Definition: map.cpp:66
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:42
lua_unit * luaW_pushunit(lua_State *L, Args...args)
Definition: lua_unit.hpp:114
bool is_enemy(int n) const
Definition: team.hpp:241
virtual const move_map & get_srcdst() const override
Definition: contexts.hpp:851
map_location target
Definition: contexts.hpp:92
double vulnerability
The vulnerability is the power projection of enemy units onto the hex we're standing on...
Definition: contexts.hpp:129
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:55
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...
virtual const std::vector< team > & teams() const
Definition: game_board.hpp:92
aspect_attacks_base(readonly_context &context, const config &cfg, const std::string &id)
virtual config to_config() const
std::shared_ptr< attacks_vector > value_
Definition: aspect.hpp:188
-file sdl_utils.hpp
bool empty() const
Definition: config.cpp:811
#define LOG_AI
#define lua_pop(L, n)
Definition: lua.h:344
virtual config to_config() const
virtual unit_stats_cache_t & unit_stats_cache() const override
Definition: contexts.hpp:992
t_translation::terrain_code get_terrain(const map_location &loc) const
Looks up terrain at a particular location.
Definition: map.cpp:301
std::vector< attack_analysis > attacks_vector
Definition: game_info.hpp:48
std::shared_ptr< unit_filter > filter_enemy_
std::multimap< map_location, map_location > move_map
The standard way in which a map of possible moves is recorded.
Definition: game_info.hpp:39
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:44
A small explanation about what's going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:58
std::string name_
Definition: aspect.hpp:109
team & get_team(int i)
Definition: game_board.hpp:94
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:848
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 map_location *tiles, bool *used_locations, std::vector< map_location > &units, std::vector< attack_analysis > &result, attack_analysis &cur_analysis, const team &current_team) const
filter_context * filter_con
Definition: resources.cpp:23
virtual bool is_allowed_enemy(const unit &u) const
game_board * gameboard
Definition: resources.cpp:20
virtual bool get_passive_leader() const override
Definition: contexts.hpp:785
size_t underlying_id() const
This unit's unique internal ID.
Definition: unit.hpp:296
Encapsulates the map of the game.
Definition: map.hpp:34
aspect_attacks(readonly_context &context, const config &cfg, const std::string &id)
int defense_modifier(const t_translation::terrain_code &terrain) const
The unit's defense on a given terrain.
Definition: unit.cpp:1538
virtual const move_map & get_enemy_dstsrc() const override
Definition: contexts.hpp:706
Managing the AIs lifecycle - headers.
static const ::config * terrain
The terrain used to create the cache.
Definition: minimap.cpp:133
Encapsulates the map of the game.
Definition: location.hpp:40
bool has_attribute(config_key_type key) const
Definition: config.cpp:196
static void raise_user_interact()
Notifies all observers of 'ai_user_interact' event.
Definition: manager.cpp:410
std::pair< int, map_location > highest(const std::string &key, int def=0) const
Definition: unit.hpp:59
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:1617
virtual bool is_allowed_attacker(const unit &u) const =0
virtual const team & current_team() const override
Definition: contexts.hpp:536
virtual bool is_allowed_attacker(const unit &u) const
std::shared_ptr< lua_ai_action_handler > handler_
std::vector< std::pair< map_location, map_location > > movements
Definition: contexts.hpp:93
virtual double power_projection(const map_location &loc, const move_map &dstsrc) const override
Function which finds how much 'power' a side can attack a certain location with.
Definition: contexts.hpp:803
size_t i
Definition: function.cpp:933
virtual side_number get_side() const override
Get the side number.
Definition: contexts.hpp:477
Aspect: attacks.
std::shared_ptr< unit_filter > filter_own_
config & add_child(config_key_type key)
Definition: config.cpp:456
bool valid_
Definition: aspect.hpp:99
virtual const gamemap & map() const
Definition: game_board.hpp:96
bool on_board(const map_location &loc) const
Tell if a location is on the map.
Definition: map.cpp:369
virtual const move_map & get_dstsrc() const override
Definition: contexts.hpp:700
#define LUA_REGISTRYINDEX
Definition: lua.h:42
virtual double get_aggression() const override
Definition: contexts.hpp:638
virtual bool is_allowed_enemy(const unit &u) const =0
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:585
const vconfig & make_safe() const
instruct the vconfig to make a private copy of its underlying data.
Definition: variable.cpp:127
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:138
virtual bool is_allowed_attacker(const unit &u) const
virtual void recalculate() const
unit_iterator find(size_t id)
Definition: map.cpp:311
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:93
virtual bool is_allowed_enemy(const unit &u) const
std::shared_ptr< lua_object< aspect_attacks_lua_filter > > obj_
static int rate_terrain(const unit &u, const map_location &loc)
This module contains various pathfinding functions and utilities.
bool get_ability_bool(const std::string &tag_name, const map_location &loc, const display_context &dc) const
Checks whether this unit currently possesses or is affected by a given ability.
Definition: abilities.cpp:138
unit_map * units
Definition: resources.cpp:34
int side() const
The side this unit belongs to.
Definition: unit.hpp:244
int gives_healing(const map_location &loc) const
Definition: map.cpp:68