Allow the attacks aspect to be implemented with Lua
Semi-related changes also included: - Unit filter now has empty() and to_config() methods - Advancements aspect now unrefs its function on destruction - Const correctness for ai.get_attacks()
This commit is contained in:
parent
678444133e
commit
97b33cfdfc
11 changed files with 256 additions and 36 deletions
|
@ -147,6 +147,10 @@ Version 1.13.4+dev:
|
|||
* New wesnoth.micro_ais table contains the loaders for all Micro AIs.
|
||||
New loaders can easily be installed by add-ons. See any built-in
|
||||
micro AI (in ai/micro_ais/mai-defs/) for an example of how to do this.
|
||||
* The attacks aspect can now be defined as a Lua aspect. The code
|
||||
should return a table with keys "own" and "enemy", each of which may
|
||||
be either a unit filter table or a function which takes a unit as a
|
||||
parameter and returns true or false.
|
||||
* Wesnoth formula engine:
|
||||
* Formulas in unit filters can now access nearly all unit attributes
|
||||
* New syntax features:
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
#include "units/unit.hpp"
|
||||
#include "pathfind/pathfind.hpp"
|
||||
#include "units/filter.hpp"
|
||||
#include "scripting/lua_api.hpp"
|
||||
#include "lauxlib.h"
|
||||
|
||||
namespace ai {
|
||||
|
||||
|
@ -40,30 +42,35 @@ static lg::log_domain log_ai_testing_aspect_attacks("ai/aspect/attacks");
|
|||
#define LOG_AI LOG_STREAM(info, log_ai_testing_aspect_attacks)
|
||||
#define ERR_AI LOG_STREAM(err, log_ai_testing_aspect_attacks)
|
||||
|
||||
aspect_attacks::aspect_attacks(readonly_context &context, const config &cfg, const std::string &id)
|
||||
aspect_attacks_base::aspect_attacks_base(readonly_context &context, const config &cfg, const std::string &id)
|
||||
: typesafe_aspect<attacks_vector>(context,cfg,id)
|
||||
{
|
||||
}
|
||||
|
||||
aspect_attacks::aspect_attacks(readonly_context &context, const config &cfg, const std::string &id)
|
||||
: aspect_attacks_base(context,cfg,id)
|
||||
, filter_own_()
|
||||
, filter_enemy_()
|
||||
{
|
||||
if (const config &filter_own = cfg.child("filter_own")) {
|
||||
filter_own_ = filter_own;
|
||||
vconfig vcfg(filter_own);
|
||||
vcfg.make_safe();
|
||||
filter_own_.reset(new unit_filter(vcfg, resources::filter_con));
|
||||
}
|
||||
if (const config &filter_enemy = cfg.child("filter_enemy")) {
|
||||
filter_enemy_ = filter_enemy;
|
||||
vconfig vcfg(filter_enemy);
|
||||
vcfg.make_safe();
|
||||
filter_enemy_.reset(new unit_filter(vcfg, resources::filter_con));
|
||||
}
|
||||
}
|
||||
|
||||
aspect_attacks::~aspect_attacks()
|
||||
{
|
||||
}
|
||||
|
||||
void aspect_attacks::recalculate() const
|
||||
void aspect_attacks_base::recalculate() const
|
||||
{
|
||||
this->value_ = analyze_targets();
|
||||
this->valid_ = true;
|
||||
}
|
||||
|
||||
boost::shared_ptr<attacks_vector> aspect_attacks::analyze_targets() const
|
||||
boost::shared_ptr<attacks_vector> aspect_attacks_base::analyze_targets() const
|
||||
{
|
||||
const move_map& srcdst = get_srcdst();
|
||||
const move_map& dstsrc = get_dstsrc();
|
||||
|
@ -74,10 +81,9 @@ boost::shared_ptr<attacks_vector> aspect_attacks::analyze_targets() const
|
|||
unit_map& units_ = *resources::units;
|
||||
|
||||
std::vector<map_location> unit_locs;
|
||||
const unit_filter filt_own(vconfig(filter_own_), resources::filter_con);
|
||||
for(unit_map::const_iterator i = units_.begin(); i != units_.end(); ++i) {
|
||||
if (i->side() == get_side() && i->attacks_left() && !(i->can_recruit() && get_passive_leader())) {
|
||||
if (!filt_own(*i)) {
|
||||
if (!is_allowed_attacker(*i)) {
|
||||
continue;
|
||||
}
|
||||
unit_locs.push_back(i->get_location());
|
||||
|
@ -93,7 +99,6 @@ boost::shared_ptr<attacks_vector> aspect_attacks::analyze_targets() const
|
|||
|
||||
unit_stats_cache().clear();
|
||||
|
||||
const unit_filter filt_en(vconfig(filter_enemy_), resources::filter_con);
|
||||
for(unit_map::const_iterator j = units_.begin(); j != units_.end(); ++j) {
|
||||
|
||||
// Attack anyone who is on the enemy side,
|
||||
|
@ -101,7 +106,7 @@ boost::shared_ptr<attacks_vector> aspect_attacks::analyze_targets() const
|
|||
if (current_team().is_enemy(j->side()) && !j->incapacitated() &&
|
||||
!j->invisible(j->get_location()))
|
||||
{
|
||||
if (!filt_en( *j)) {
|
||||
if (!is_allowed_enemy(*j)) {
|
||||
continue;
|
||||
}
|
||||
map_location adjacent[6];
|
||||
|
@ -120,7 +125,7 @@ boost::shared_ptr<attacks_vector> aspect_attacks::analyze_targets() const
|
|||
|
||||
|
||||
|
||||
void aspect_attacks::do_attack_analysis(
|
||||
void aspect_attacks_base::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,
|
||||
|
@ -361,7 +366,7 @@ void aspect_attacks::do_attack_analysis(
|
|||
}
|
||||
}
|
||||
|
||||
int aspect_attacks::rate_terrain(const unit& u, const map_location& loc)
|
||||
int aspect_attacks_base::rate_terrain(const unit& u, const map_location& loc)
|
||||
{
|
||||
const gamemap &map_ = resources::gameboard->map();
|
||||
const t_translation::t_terrain terrain = map_.get_terrain(loc);
|
||||
|
@ -396,15 +401,110 @@ int aspect_attacks::rate_terrain(const unit& u, const map_location& loc)
|
|||
config aspect_attacks::to_config() const
|
||||
{
|
||||
config cfg = typesafe_aspect<attacks_vector>::to_config();
|
||||
if (!filter_own_.empty()) {
|
||||
cfg.add_child("filter_own",filter_own_);
|
||||
if (filter_own_ && !filter_own_->empty()) {
|
||||
cfg.add_child("filter_own", filter_own_->to_config());
|
||||
}
|
||||
if (!filter_enemy_.empty()) {
|
||||
cfg.add_child("filter_enemy",filter_enemy_);
|
||||
if (filter_enemy_ && !filter_enemy_->empty()) {
|
||||
cfg.add_child("filter_enemy", filter_enemy_->to_config());
|
||||
}
|
||||
return cfg;
|
||||
}
|
||||
|
||||
bool aspect_attacks::is_allowed_attacker(const unit& u) const
|
||||
{
|
||||
return (*filter_own_)(u);
|
||||
}
|
||||
|
||||
bool aspect_attacks::is_allowed_enemy(const unit& u) const
|
||||
{
|
||||
return (*filter_enemy_)(u);
|
||||
}
|
||||
|
||||
} // end of namespace testing_ai_default
|
||||
|
||||
aspect_attacks_lua::aspect_attacks_lua(readonly_context &context, const config &cfg, const std::string &id, boost::shared_ptr<lua_ai_context>& l_ctx)
|
||||
: aspect_attacks_base(context, cfg, id)
|
||||
, handler_(), code_(), params_(cfg.child_or_empty("args"))
|
||||
{
|
||||
this->name_ = "lua_aspect";
|
||||
if (cfg.has_attribute("code"))
|
||||
{
|
||||
code_ = cfg["code"].str();
|
||||
}
|
||||
else if (cfg.has_attribute("value"))
|
||||
{
|
||||
code_ = "return " + cfg["value"].apply_visitor(lua_aspect_visitor());
|
||||
}
|
||||
else
|
||||
{
|
||||
// error
|
||||
return;
|
||||
}
|
||||
handler_ = boost::shared_ptr<lua_ai_action_handler>(resources::lua_kernel->create_lua_ai_action_handler(code_.c_str(), *l_ctx));
|
||||
}
|
||||
|
||||
void aspect_attacks_lua::recalculate() const
|
||||
{
|
||||
obj_.reset(new lua_object<aspect_attacks_lua_filter>);
|
||||
handler_->handle(params_, true, obj_);
|
||||
aspect_attacks_lua_filter filt = *obj_->get();
|
||||
aspect_attacks_base::recalculate();
|
||||
if(filt.lua) {
|
||||
if(filt.ref_own_ != -1) {
|
||||
luaL_unref(filt.lua, LUA_REGISTRYINDEX, filt.ref_own_);
|
||||
}
|
||||
if(filt.ref_enemy_ != -1) {
|
||||
luaL_unref(filt.lua, LUA_REGISTRYINDEX, filt.ref_enemy_);
|
||||
}
|
||||
}
|
||||
obj_.reset();
|
||||
}
|
||||
|
||||
config aspect_attacks_lua::to_config() const
|
||||
{
|
||||
config cfg = aspect::to_config();
|
||||
cfg["code"] = code_;
|
||||
if (!params_.empty()) {
|
||||
cfg.add_child("args", params_);
|
||||
}
|
||||
return cfg;
|
||||
}
|
||||
|
||||
static bool call_lua_filter_fcn(lua_State* L, const unit& u, int idx)
|
||||
{
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, idx);
|
||||
new(lua_newuserdata(L, sizeof(lua_unit))) lua_unit(u.underlying_id());
|
||||
lua_pushlightuserdata(L, getunitKey);
|
||||
lua_rawget(L, LUA_REGISTRYINDEX);
|
||||
lua_setmetatable(L, -2);
|
||||
luaW_pcall(L, 1, 1);
|
||||
bool result = luaW_toboolean(L, -1);
|
||||
lua_pop(L, 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool aspect_attacks_lua::is_allowed_attacker(const unit& u) const
|
||||
{
|
||||
const aspect_attacks_lua_filter& filt = *obj_->get();
|
||||
if(filt.lua && filt.ref_own_ != -1) {
|
||||
return call_lua_filter_fcn(filt.lua, u, filt.ref_own_);
|
||||
} else if(filt.filter_own_) {
|
||||
return (*filt.filter_own_)(u);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool aspect_attacks_lua::is_allowed_enemy(const unit& u) const
|
||||
{
|
||||
const aspect_attacks_lua_filter& filt = *obj_->get();
|
||||
if(filt.lua && filt.ref_enemy_ != -1) {
|
||||
return call_lua_filter_fcn(filt.lua, u, filt.ref_enemy_);
|
||||
} else if(filt.filter_enemy_) {
|
||||
return (*filt.filter_enemy_)(u);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
} // end of namespace ai
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#define AI_TESTING_ASPECT_ATTACKS_HPP_INCLUDED
|
||||
|
||||
#include "ai/composite/aspect.hpp"
|
||||
#include "units/filter.hpp"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
|
@ -33,18 +34,19 @@ namespace ai {
|
|||
|
||||
namespace ai_default_rca {
|
||||
|
||||
class aspect_attacks: public typesafe_aspect<attacks_vector> {
|
||||
class aspect_attacks_base : public typesafe_aspect<attacks_vector> {
|
||||
public:
|
||||
aspect_attacks(readonly_context &context, const config &cfg, const std::string &id);
|
||||
aspect_attacks_base(readonly_context &context, const config &cfg, const std::string &id);
|
||||
|
||||
|
||||
virtual ~aspect_attacks();
|
||||
virtual ~aspect_attacks_base() {}
|
||||
|
||||
|
||||
virtual void recalculate() const;
|
||||
|
||||
|
||||
virtual config to_config() const;
|
||||
virtual bool is_allowed_attacker(const unit& u) const = 0;
|
||||
virtual bool is_allowed_enemy(const unit& u) const = 0;
|
||||
|
||||
|
||||
protected:
|
||||
|
@ -60,14 +62,44 @@ protected:
|
|||
attack_analysis& cur_analysis,
|
||||
const team ¤t_team) const;
|
||||
static int rate_terrain(const unit& u, const map_location& loc);
|
||||
config filter_own_;
|
||||
config filter_enemy_;
|
||||
};
|
||||
|
||||
class aspect_attacks : public aspect_attacks_base {
|
||||
public:
|
||||
aspect_attacks(readonly_context &context, const config &cfg, const std::string &id);
|
||||
virtual ~aspect_attacks() {}
|
||||
|
||||
virtual bool is_allowed_attacker(const unit& u) const;
|
||||
virtual bool is_allowed_enemy(const unit& u) const;
|
||||
virtual config to_config() const;
|
||||
private:
|
||||
boost::shared_ptr<unit_filter> filter_own_, filter_enemy_;
|
||||
};
|
||||
|
||||
} // end of namespace testing_ai_default
|
||||
|
||||
struct aspect_attacks_lua_filter {
|
||||
lua_State* lua;
|
||||
boost::shared_ptr<unit_filter> filter_own_, filter_enemy_;
|
||||
int ref_own_, ref_enemy_;
|
||||
};
|
||||
|
||||
class aspect_attacks_lua : public ai_default_rca::aspect_attacks_base {
|
||||
public:
|
||||
aspect_attacks_lua(readonly_context &context, const config &cfg, const std::string &id, boost::shared_ptr<lua_ai_context>& l_ctx);
|
||||
virtual ~aspect_attacks_lua() {}
|
||||
|
||||
virtual bool is_allowed_attacker(const unit& u) const;
|
||||
virtual bool is_allowed_enemy(const unit& u) const;
|
||||
virtual config to_config() const;
|
||||
virtual void recalculate() const;
|
||||
private:
|
||||
boost::shared_ptr<lua_ai_action_handler> handler_;
|
||||
mutable boost::shared_ptr<lua_object<aspect_attacks_lua_filter> > obj_;
|
||||
std::string code_;
|
||||
const config params_;
|
||||
};
|
||||
|
||||
} // end of namespace ai
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
|
|
@ -59,6 +59,14 @@ unit_advancements_aspect::unit_advancements_aspect(const std::string& val): val
|
|||
{
|
||||
}
|
||||
|
||||
unit_advancements_aspect::~unit_advancements_aspect()
|
||||
{
|
||||
if(L_) {
|
||||
// Remove the function from the registry
|
||||
luaL_unref(L_, LUA_REGISTRYINDEX, ref_);
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<std::string> unit_advancements_aspect::get_advancements(const unit_map::const_iterator& unit) const
|
||||
{
|
||||
|
||||
|
|
|
@ -31,9 +31,7 @@ public:
|
|||
unit_advancements_aspect(lua_State* L, int n);
|
||||
unit_advancements_aspect(const std::string& val);
|
||||
const std::vector<std::string> get_advancements(const unit_map::const_iterator& unit) const;
|
||||
virtual ~unit_advancements_aspect()
|
||||
{
|
||||
}
|
||||
virtual ~unit_advancements_aspect();
|
||||
const std::string get_value() const;
|
||||
|
||||
private:
|
||||
|
|
|
@ -54,7 +54,7 @@ static char const aisKey = 0;
|
|||
|
||||
namespace ai {
|
||||
|
||||
static void push_attack_analysis(lua_State *L, attack_analysis&);
|
||||
static void push_attack_analysis(lua_State *L, const attack_analysis&);
|
||||
|
||||
void lua_ai_context::init(lua_State *L)
|
||||
{
|
||||
|
@ -410,11 +410,11 @@ static int cfun_ai_get_attack_depth(lua_State *L)
|
|||
|
||||
static int cfun_ai_get_attacks(lua_State *L)
|
||||
{
|
||||
ai::attacks_vector attacks = get_readonly_context(L).get_attacks();
|
||||
const ai::attacks_vector& attacks = get_readonly_context(L).get_attacks();
|
||||
lua_createtable(L, attacks.size(), 0);
|
||||
int table_index = lua_gettop(L);
|
||||
|
||||
ai::attacks_vector::iterator it = attacks.begin();
|
||||
ai::attacks_vector::const_iterator it = attacks.begin();
|
||||
for (int i = 1; it != attacks.end(); ++it, ++i)
|
||||
{
|
||||
push_attack_analysis(L, *it);
|
||||
|
@ -546,7 +546,7 @@ static int cfun_attack_rating(lua_State *L)
|
|||
// the attack_analysis table should be on top of the stack
|
||||
lua_getfield(L, -1, "att_ptr"); // [-2: attack_analysis; -1: pointer to attack_analysis object in c++]
|
||||
// now the pointer to our attack_analysis C++ object is on top
|
||||
attack_analysis* aa_ptr = static_cast< attack_analysis * >(lua_touserdata(L, -1));
|
||||
const attack_analysis* aa_ptr = static_cast< attack_analysis * >(lua_touserdata(L, -1));
|
||||
|
||||
//[-2: attack_analysis; -1: pointer to attack_analysis object in c++]
|
||||
|
||||
|
@ -586,13 +586,13 @@ static void push_movements(lua_State *L, const std::vector< std::pair < map_loca
|
|||
|
||||
}
|
||||
|
||||
static void push_attack_analysis(lua_State *L, attack_analysis& aa)
|
||||
static void push_attack_analysis(lua_State *L, const attack_analysis& aa)
|
||||
{
|
||||
lua_newtable(L);
|
||||
|
||||
// Pushing a pointer to the current object
|
||||
lua_pushstring(L, "att_ptr");
|
||||
lua_pushlightuserdata(L, &aa);
|
||||
lua_pushlightuserdata(L, const_cast<attack_analysis*>(&aa));
|
||||
lua_rawset(L, -3);
|
||||
|
||||
// Registering callback function for the rating method
|
||||
|
|
|
@ -18,7 +18,15 @@
|
|||
*/
|
||||
|
||||
|
||||
#include "lua_object.hpp"
|
||||
#include "ai/lua/lua_object.hpp"
|
||||
#include "ai/lua/engine_lua.hpp"
|
||||
#include "ai/default/aspect_attacks.hpp"
|
||||
#include "scripting/lua_types.hpp"
|
||||
#include "scripting/lua_common.hpp"
|
||||
#include "resources.hpp"
|
||||
|
||||
#include <boost/smart_ptr/make_shared_object.hpp>
|
||||
#include "lauxlib.h"
|
||||
|
||||
namespace ai {
|
||||
|
||||
|
@ -26,5 +34,42 @@ namespace ai {
|
|||
{
|
||||
// empty
|
||||
}
|
||||
|
||||
template <>
|
||||
boost::shared_ptr<aspect_attacks_lua_filter> lua_object<aspect_attacks_lua_filter>::to_type(lua_State *L, int n)
|
||||
{
|
||||
boost::shared_ptr<aspect_attacks_lua_filter> att(new aspect_attacks_lua_filter);
|
||||
att->lua = NULL;
|
||||
att->ref_own_ = att->ref_enemy_ = -1;
|
||||
if(!lua_istable(L, n)) {
|
||||
return att;
|
||||
}
|
||||
lua_getfield(L, n, "own");
|
||||
if(lua_istable(L, -1)) {
|
||||
config cfg;
|
||||
vconfig vcfg(cfg, true);
|
||||
if(luaW_tovconfig(L, -1, vcfg)) {
|
||||
att->filter_own_.reset(new unit_filter(vcfg, resources::filter_con));
|
||||
}
|
||||
} else if(lua_isfunction(L, -1)) {
|
||||
att->lua = L;
|
||||
att->ref_own_ = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
assert(att->ref_own_ != -1);
|
||||
}
|
||||
lua_getfield(L, n, "enemy");
|
||||
if(lua_istable(L, -1)) {
|
||||
config cfg;
|
||||
vconfig vcfg(cfg, true);
|
||||
if(luaW_tovconfig(L, -1, vcfg)) {
|
||||
att->filter_enemy_.reset(new unit_filter(vcfg, resources::filter_con));
|
||||
}
|
||||
} else if(lua_isfunction(L, -1)) {
|
||||
att->lua = L;
|
||||
att->ref_enemy_ = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
assert(att->ref_enemy_ != -1);
|
||||
}
|
||||
lua_pop(L, 2);
|
||||
return att;
|
||||
}
|
||||
|
||||
} //end of namespace ai
|
||||
|
|
|
@ -69,7 +69,7 @@ public:
|
|||
|
||||
void store(lua_State* L, int n)
|
||||
{
|
||||
this->value_ = boost::shared_ptr<T>(to_type(L, n));
|
||||
this->value_ = boost::shared_ptr<T>(to_type(L, lua_absindex(L, n)));
|
||||
}
|
||||
|
||||
protected:
|
||||
|
@ -200,6 +200,11 @@ inline boost::shared_ptr<unit_advancements_aspect> lua_object<unit_advancements_
|
|||
boost::shared_ptr<unit_advancements_aspect> uaa = boost::shared_ptr<unit_advancements_aspect>(new unit_advancements_aspect(L, n));
|
||||
return uaa;
|
||||
}
|
||||
|
||||
// This one is too complex to define in the header.
|
||||
struct aspect_attacks_lua_filter;
|
||||
template <>
|
||||
boost::shared_ptr<aspect_attacks_lua_filter> lua_object<aspect_attacks_lua_filter>::to_type(lua_State *L, int n);
|
||||
} // end of namespace ai
|
||||
|
||||
|
||||
|
|
|
@ -419,6 +419,9 @@ static register_lua_aspect_factory< lua_aspect<double> >
|
|||
static register_lua_aspect_factory< lua_aspect<int> >
|
||||
attack_depth__lua_aspect_factory("attack_depth*lua_aspect");
|
||||
|
||||
static register_lua_aspect_factory< aspect_attacks_lua >
|
||||
attacks__lua_aspect_factory("attacks*lua_aspect");
|
||||
|
||||
static register_lua_aspect_factory< lua_aspect<terrain_filter> >
|
||||
avoid__lua_aspect_factory("avoid*lua_aspect");
|
||||
|
||||
|
|
|
@ -48,6 +48,11 @@ static lg::log_domain log_config("config");
|
|||
#define ERR_CF LOG_STREAM(err, log_config)
|
||||
#define DBG_CF LOG_STREAM(debug, log_config)
|
||||
|
||||
// Defined out of line to avoid including config in unit_filter.hpp
|
||||
config unit_filter::to_config() const {
|
||||
return impl_->to_config();
|
||||
}
|
||||
|
||||
///Defined out of line to prevent including unit at unit_filter.hpp
|
||||
bool unit_filter::matches(const unit & u) const {
|
||||
return matches (u, u.get_location());
|
||||
|
@ -95,6 +100,14 @@ public:
|
|||
|
||||
|
||||
virtual ~null_unit_filter_impl() {}
|
||||
|
||||
config to_config() const {
|
||||
return config();
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
const filter_context & fc_;
|
||||
|
@ -159,6 +172,9 @@ public:
|
|||
virtual bool matches(const unit & u, const map_location & loc, const unit * u2) const;
|
||||
virtual std::vector<const unit *> all_matches_on_map(unsigned max_matches) const;
|
||||
virtual unit_const_ptr first_match_on_map() const;
|
||||
config to_config() const {
|
||||
return vcfg.get_config();
|
||||
}
|
||||
|
||||
virtual ~basic_unit_filter_impl() {}
|
||||
private:
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
|
||||
class filter_context;
|
||||
class unit;
|
||||
class config;
|
||||
class vconfig;
|
||||
struct map_location;
|
||||
|
||||
|
@ -41,6 +42,8 @@ public:
|
|||
virtual bool matches(const unit & u, const map_location & loc, const unit * u2 = NULL) const = 0;
|
||||
virtual std::vector<const unit*> all_matches_on_map(unsigned max_matches) const = 0;
|
||||
virtual unit_const_ptr first_match_on_map() const = 0;
|
||||
virtual config to_config() const = 0;
|
||||
virtual bool empty() const {return false;}
|
||||
virtual ~unit_filter_abstract_impl() {}
|
||||
};
|
||||
|
||||
|
@ -95,6 +98,12 @@ public:
|
|||
unit_const_ptr first_match_on_map() const {
|
||||
return impl_->first_match_on_map();
|
||||
}
|
||||
|
||||
config to_config() const;
|
||||
|
||||
bool empty() const {
|
||||
return impl_->empty();
|
||||
}
|
||||
private:
|
||||
boost::shared_ptr<unit_filter_abstract_impl> impl_;
|
||||
unsigned max_matches_;
|
||||
|
|
Loading…
Add table
Reference in a new issue