Huge refactor of Lua AI engine

This commit potentially breaks any Lua AI customization, except for external Lua candidate actions.
In practice, though, Lua aspects and goals will probably continue to work for the most part.

- The ai table now has a read_only attribute.
  If true, functions that change the game state will be missing from the table.
  The read_only attribute is false in CA execution and in stages.
  It is true everywhere else.
- Every Lua AI component now supports a [args] subtag.
  The contents of this tag are passed as parameters to the component code.
  This data is immutable; components cannot alter its contents.
  (External Lua candidate actions do not receive this data.)
- Accessing the persistent engine data is now supported in all Lua components.

When calling a Lua component, the Lua engine now passes two parameters:
1. The contents of the [args] tag in the specific component.
2. The contents of the [data] tag in the Lua [engine].

The return value of the [engine] code, if any, is stored for later used.
It will be passed as the third parameter to any other Lua component.
This data can be changed, but will not be saved.
The default engine does not return any such data.
This commit is contained in:
Celtic Minstrel 2016-02-29 04:13:05 -05:00 committed by mattsc
parent 56a99175da
commit f8f5557eb0
8 changed files with 233 additions and 127 deletions

View file

@ -2,21 +2,9 @@
-- This is the engine used by the Lua AI when no engine is
-- defined specifically in the [side] tag
return {
get_ai = function(ai)
local my_ai = {}
-- This provides a cache level for the move map functions,
-- making them a bit easier to use
local ai_stdlib = wesnoth.require('ai/lua/stdlib.lua')
ai_stdlib.init(ai)
local ai_stdlib = wesnoth.require('ai/lua/stdlib.lua')
ai_stdlib.init(ai)
-- Make the ai table available to the eval/exec functions
function my_ai:get_ai()
return ai
end
-- Make the persistent data table available to the eval/exec functions
my_ai.data = {}
return my_ai
end
}
-- No special state is returned by the default engine

View file

@ -148,4 +148,15 @@ known_aspect::~known_aspect()
{
}
std::string lua_aspect_visitor::quote_string(const std::string& s)
{
if (s.find_first_of('"') == std::string::npos) {
return '"' + s + '"';
} else if (s.find_first_of("'") == std::string::npos) {
return "'" + s + "'";
} else {
return "[=====[" + s + "]=====]";
}
}
} //end of namespace ai

View file

@ -26,6 +26,7 @@
#include "scripting/game_lua_kernel.hpp"
#include "log.hpp"
#include "util.hpp"
#include <boost/bind.hpp>
#include <boost/pointer_cast.hpp>
@ -394,6 +395,18 @@ protected:
std::string turns_;
};
class lua_aspect_visitor : public boost::static_visitor<std::string> {
static std::string quote_string(const std::string& s);
public:
std::string operator()(bool b) const {return b ? "true" : "false";}
std::string operator()(int i) const {return quote_string(str_cast(i));}
std::string operator()(unsigned long long i) const {return quote_string(str_cast(i));}
std::string operator()(double i) const {return quote_string(str_cast(i));}
std::string operator()(const std::string& s) const {return quote_string(s);}
std::string operator()(const t_string& s) const {return quote_string(s.str());}
std::string operator()(boost::blank) const {return "nil";}
};
template<typename T>
class lua_aspect : public typesafe_aspect<T>
@ -401,50 +414,44 @@ class lua_aspect : public typesafe_aspect<T>
public:
lua_aspect(readonly_context &context, const config &cfg, const std::string &id, boost::shared_ptr<lua_ai_context>& l_ctx)
: typesafe_aspect<T>(context, cfg, id)
, handler_(), code_()
, handler_(), code_(), params_(cfg.child_or_empty("args"))
{
std::string value;
if (cfg.has_attribute("value"))
{
value = cfg["value"].str();
if (value == "yes") /** @todo for Nephro or Crab: get rid of this workaround */
{
value = "true";
}
value = "return " + value;
code_ = "return " + cfg["value"].apply_visitor(lua_aspect_visitor());
}
else if (cfg.has_attribute("code"))
{
value = cfg["code"].str();
code_ = cfg["code"].str();
}
else
{
// error
return;
}
code_ = value;
handler_ = boost::shared_ptr<lua_ai_action_handler>(resources::lua_kernel->create_lua_ai_action_handler(value.c_str(), *l_ctx));
handler_ = boost::shared_ptr<lua_ai_action_handler>(resources::lua_kernel->create_lua_ai_action_handler(code_.c_str(), *l_ctx));
}
void recalculate() const
{
this->valid_lua_ = true;
boost::shared_ptr< lua_object<T> > l_obj = boost::shared_ptr< lua_object<T> >(new lua_object<T>());
config c = config();
handler_->handle(c, true, l_obj);
this->value_lua_ = l_obj;
handler_->handle(params_, true, this->value_lua_);
}
config to_config() const
{
config cfg = aspect::to_config();
cfg["code"] = code_;
if (!params_.empty()) {
cfg.add_child("args", params_);
}
return cfg;
}
private:
boost::shared_ptr<lua_ai_action_handler> handler_;
std::string code_;
const config params_;
};

View file

@ -56,7 +56,7 @@ class lua_candidate_action_wrapper_base : public candidate_action {
public:
lua_candidate_action_wrapper_base( rca_context &context, const config &cfg)
: candidate_action(context, cfg),evaluation_action_handler_(),execution_action_handler_(),serialized_evaluation_state_()
: candidate_action(context, cfg),evaluation_action_handler_(),execution_action_handler_(),serialized_evaluation_state_(cfg.child_or_empty("args"))
{
// do nothing
}
@ -65,8 +65,6 @@ public:
virtual double evaluate()
{
serialized_evaluation_state_ = config();
lua_int_obj l_obj = lua_int_obj(new lua_object<int>());
if (evaluation_action_handler_) {
@ -82,15 +80,15 @@ public:
virtual void execute() {
lua_int_obj l_obj = lua_int_obj(new lua_object<int>());
if (execution_action_handler_) {
execution_action_handler_->handle(serialized_evaluation_state_, false, l_obj);
lua_object_ptr nil;
execution_action_handler_->handle(serialized_evaluation_state_, false, nil);
}
}
virtual config to_config() const {
config cfg = candidate_action::to_config();
cfg.add_child("state",serialized_evaluation_state_);
cfg.add_child("args",serialized_evaluation_state_);
return cfg;
}
@ -155,9 +153,10 @@ private:
std::string exec_parms_;
void generate_code(std::string& eval, std::string& exec) {
std::string code = "wesnoth.require(\"" + location_ + "\")";
eval = "return " + code + ":evaluation((...):get_ai(), {" + eval_parms_ + "}, (...))";
exec = code + ":execution((...):get_ai(), {" + exec_parms_ + "}, (...))";
std::string preamble = "local params, data, state = ...\n";
std::string load = "wesnoth.require(\"" + location_ + "\")";
eval = preamble + "return " + load + ":evaluation(ai, {" + eval_parms_ + "}, {data = data})";
exec = preamble + load + ":execution(ai, {" + exec_parms_ + "}, {data = data})";
}
};
@ -197,7 +196,7 @@ private:
class lua_stage_wrapper : public stage {
public:
lua_stage_wrapper( ai_context &context, const config &cfg, lua_ai_context &lua_ai_ctx )
: stage(context,cfg),action_handler_(),code_(cfg["code"]),serialized_evaluation_state_(cfg.child_or_empty("state"))
: stage(context,cfg),action_handler_(),code_(cfg["code"]),serialized_evaluation_state_(cfg.child_or_empty("args"))
{
action_handler_ = boost::shared_ptr<lua_ai_action_handler>(resources::lua_kernel->create_lua_ai_action_handler(code_.c_str(),lua_ai_ctx));
}
@ -209,10 +208,10 @@ public:
virtual bool do_play_stage()
{
gamestate_observer gs_o;
lua_int_obj l_obj = lua_int_obj(new lua_object<int>());
if (action_handler_) {
action_handler_->handle(serialized_evaluation_state_, false, l_obj);
lua_object_ptr nil;
action_handler_->handle(serialized_evaluation_state_, false, nil);
}
return gs_o.is_gamestate_changed();
@ -222,7 +221,7 @@ public:
{
config cfg = stage::to_config();
cfg["code"] = code_;
cfg.add_child("state",serialized_evaluation_state_);
cfg.add_child("args",serialized_evaluation_state_);
return cfg;
}
private:
@ -244,9 +243,12 @@ engine_lua::engine_lua( readonly_context &context, const config &cfg )
{
name_ = "lua";
config data(cfg.child_or_empty("data"));
config args(cfg.child_or_empty("args"));
if (lua_ai_context_) { // The context might be NULL if the config contains errors
lua_ai_context_->set_persistent_data(data);
lua_ai_context_->set_arguments(args);
lua_ai_context_->update_state();
}
}
@ -256,7 +258,7 @@ std::string engine_lua::get_engine_code(const config &cfg) const
return cfg["code"].str();
}
// If there is no engine defined we create a dummy engine
std::string code = "local ai = ... return wesnoth.require(\"ai/lua/dummy_engine_lua.lua\").get_ai(ai)";
std::string code = "wesnoth.require(\"ai/lua/dummy_engine_lua.lua\")";
return code;
}
@ -273,7 +275,7 @@ void engine_lua::push_ai_table()
{
if (game_config::debug)
{
lua_ai_context_->load_and_inject_ai_table(this);
// TODO: Reimplement this somehow
}
}

View file

@ -350,7 +350,7 @@ void lua_goal::add_targets(std::back_insert_iterator< std::vector< target > > ta
{
boost::shared_ptr< lua_object< std::vector < target > > > l_obj
= boost::shared_ptr< lua_object< std::vector < target > > >(new lua_object< std::vector < target > >());
config c = config();
config c(cfg_.child_or_empty("args"));
handler_->handle(c, true, l_obj);
try {
std::vector < target > targets = *(l_obj->get());

View file

@ -63,6 +63,34 @@ void lua_ai_context::init(lua_State *L)
lua_rawset(L, LUA_REGISTRYINDEX);
}
void lua_ai_context::get_arguments(config &cfg) const
{
int top = lua_gettop(L);
lua_pushlightuserdata(L, static_cast<void *>(const_cast<char *>(&aisKey)));
lua_rawget(L, LUA_REGISTRYINDEX);
lua_rawgeti(L, -1, num_);
lua_getfield(L, -1, "args");
luaW_toconfig(L, -1, cfg);
lua_settop(L, top);
}
void lua_ai_context::set_arguments(const config &cfg)
{
int top = lua_gettop(L);
lua_pushlightuserdata(L, static_cast<void *>(const_cast<char *>(&aisKey)));
lua_rawget(L, LUA_REGISTRYINDEX);
lua_rawgeti(L, -1, num_);
luaW_pushconfig(L, cfg);
lua_setfield(L, -2, "args");
lua_settop(L, top);
}
void lua_ai_context::get_persistent_data(config &cfg) const
{
int top = lua_gettop(L);
@ -84,20 +112,13 @@ void lua_ai_context::set_persistent_data(const config &cfg)
lua_pushlightuserdata(L, static_cast<void *>(const_cast<char *>(&aisKey)));
lua_rawget(L, LUA_REGISTRYINDEX);
lua_rawgeti(L, -1, num_);
if(lua_isnoneornil(L, -1)) {
// Just in case the self table wasn't initialized.
lua_pop(L, 1);
lua_newtable(L);
lua_rawseti(L, -2, num_);
lua_rawgeti(L, -1, num_);
}
luaW_pushconfig(L, cfg);
lua_setfield(L, -2, "data");
lua_settop(L, top);
}
static ai::engine_lua &get_engine(lua_State *L)
{
return *(static_cast<ai::engine_lua*>(
@ -812,13 +833,18 @@ static int cfun_ai_recalculate_move_maps_enemy(lua_State *L)
return 1;
}
static void generate_and_push_ai_table(lua_State* L, ai::engine_lua* engine) {
//push data table here
lua_newtable(L);
lua_pushinteger(L, engine->get_readonly_context().get_side());
lua_setfield(L, -2, "side"); //stack size is 2 [- 1: new table; -2 ai as string]
static int impl_ai_get(lua_State* L)
{
if(!lua_isstring(L,2)) {
return 0;
}
ai::engine_lua& engine = get_engine(L);
std::string m = lua_tostring(L,2);
if(m == "side") {
lua_pushinteger(L, engine.get_readonly_context().get_side());
return 1;
}
static luaL_Reg const callbacks[] = {
{ "attack", &cfun_ai_execute_attack },
// Move maps
{ "get_new_dst_src", &cfun_ai_get_dstsrc },
{ "get_new_src_dst", &cfun_ai_get_srcdst },
@ -859,14 +885,6 @@ static void generate_and_push_ai_table(lua_State* L, ai::engine_lua* engine) {
{ "is_src_dst_valid", &cfun_ai_is_src_dst_valid },
{ "is_enemy_src_dst_valid", &cfun_ai_is_src_dst_enemy_valid },
// End of validation functions
{ "move", &cfun_ai_execute_move_partial },
{ "move_full", &cfun_ai_execute_move_full },
{ "recall", &cfun_ai_execute_recall },
{ "recruit", &cfun_ai_execute_recruit },
{ "stopunit_all", &cfun_ai_execute_stopunit_all },
{ "stopunit_attacks", &cfun_ai_execute_stopunit_attacks },
{ "stopunit_moves", &cfun_ai_execute_stopunit_moves },
{ "synced_command", &cfun_ai_execute_synced_command },
{ "suitable_keep", &cfun_ai_get_suitable_keep },
{ "check_recall", &cfun_ai_check_recall },
{ "check_move", &cfun_ai_check_move },
@ -878,16 +896,73 @@ static void generate_and_push_ai_table(lua_State* L, ai::engine_lua* engine) {
//{ "",},
{ NULL, NULL } };
for (const luaL_Reg* p = callbacks; p->name; ++p) {
lua_pushlightuserdata(L, engine);
lua_pushcclosure(L, p->func, 1);
lua_setfield(L, -2, p->name);
if(m == p->name) {
lua_pushlightuserdata(L, &engine); // [-1: engine ...]
lua_pushcclosure(L, p->func, 1); // [-1: function ...]
// Store the function so that __index doesn't need to be called next time
lua_pushstring(L, p->name); // [-1: name -2: function ...]
lua_pushvalue(L, -2); // [-1: function -2: name -3: function ...]
lua_rawset(L, 1); // [-1: function ...]
return 1;
}
}
lua_pushstring(L, "read_only");
lua_rawget(L, 1);
bool read_only = lua_toboolean(L, -1);
lua_pop(L, 1);
if(read_only) {
return 0;
}
static luaL_Reg const mutating_callbacks[] = {
{ "attack", &cfun_ai_execute_attack },
{ "move", &cfun_ai_execute_move_partial },
{ "move_full", &cfun_ai_execute_move_full },
{ "recall", &cfun_ai_execute_recall },
{ "recruit", &cfun_ai_execute_recruit },
{ "stopunit_all", &cfun_ai_execute_stopunit_all },
{ "stopunit_attacks", &cfun_ai_execute_stopunit_attacks },
{ "stopunit_moves", &cfun_ai_execute_stopunit_moves },
{ "synced_command", &cfun_ai_execute_synced_command },
{ NULL, NULL } };
for (const luaL_Reg* p = mutating_callbacks; p->name; ++p) {
if(m == p->name) {
lua_pushlightuserdata(L, &engine);
lua_pushcclosure(L, p->func, 1);
return 1;
}
}
return 0;
}
static void generate_and_push_ai_table(lua_State* L, ai::engine_lua* engine) {
//push data table here
lua_newtable(L); // [-1: ai table]
lua_newtable(L); // [-1: metatable -2: ai table]
lua_pushlightuserdata(L, engine); // [-1: engine -2: metatable -3: ai table]
lua_pushcclosure(L, &impl_ai_get, 1); // [-1: metafunc -2: metatable -3: ai table]
lua_setfield(L, -2, "__index"); // [-1: metatable -2: ai table]
lua_setmetatable(L, -2); // [-1: ai table]
}
static size_t generate_and_push_ai_state(lua_State* L, ai::engine_lua* engine)
{
// Retrieve the ai elements table from the registry.
lua_pushlightuserdata(L, static_cast<void *>(const_cast<char *>(&aisKey)));
lua_rawget(L, LUA_REGISTRYINDEX); // [-1: AIs registry table]
size_t length_ai = lua_rawlen(L, -1); // length of table
lua_newtable(L); // [-1: AI state table -2: AIs registry table]
generate_and_push_ai_table(L, engine); // [-1: AI routines -2: AI state -3: AIs registry]
lua_setfield(L, -2, "ai"); // [-1: AI state -2: AIs registry]
lua_pushvalue(L, -1); // [-1: AI state -2: AI state -3: AIs registry]
lua_rawseti(L, -3, length_ai + 1); // [-1: AI state -2: AIs registry]
lua_remove(L, -2); // [-1: AI state table]
return length_ai + 1;
}
lua_ai_context* lua_ai_context::create(lua_State *L, char const *code, ai::engine_lua *engine)
{
int res_ai = luaL_loadstring(L, code);//stack size is now 1 [ -1: ai_context]
if (res_ai)
int res_ai = luaL_loadstring(L, code); // [-1: AI code]
if (res_ai != 0)
{
char const *m = lua_tostring(L, -1);
@ -896,22 +971,35 @@ lua_ai_context* lua_ai_context::create(lua_State *L, char const *code, ai::engin
return NULL;
}
//push data table here
generate_and_push_ai_table(L, engine);
//compile the ai as a closure
if (!luaW_pcall(L, 1, 1, true)) {
return NULL;//return with stack size 0 []
}
// Retrieve the ai elements table from the registry.
lua_pushlightuserdata(L, static_cast<void *>(const_cast<char *>(&aisKey)));
lua_rawget(L, LUA_REGISTRYINDEX); //stack size is now 2 [-1: ais_table -2: f]
// Push the function in the table so that it is not collected.
size_t length_ai = lua_rawlen(L, -1);//length of ais_table
lua_pushvalue(L, -2); //stack size is now 3: [-1: ai_context -2: ais_table -3: ai_context]
lua_rawseti(L, -2, length_ai + 1);// ais_table[length+1]=ai_context. stack size is now 2 [-1: ais_table -2: ai_context]
size_t idx = generate_and_push_ai_state(L, engine); // [-1: AI state -2: AI code]
lua_pushvalue(L, -2); // [-1: AI code -2: AI state -3: AI code]
lua_setfield(L, -2, "update_state"); // [-1: AI state -2: AI code]
lua_pushlightuserdata(L, engine);
lua_setfield(L, -2, "engine"); // [-1: AI state -2: AI code]
lua_pop(L, 2);
return new lua_ai_context(L, length_ai + 1, engine->get_readonly_context().get_side());
return new lua_ai_context(L, idx, engine->get_readonly_context().get_side());
}
void lua_ai_context::update_state()
{
lua_ai_load ctx(*this, true); // [-1: AI state table]
// Load the AI code and arguments
lua_getfield(L, -1, "update_state"); // [-1: AI code -2: AI state]
lua_getfield(L, -2, "args"); // [-1: Arguments -2: AI code -3: AI state]
lua_getfield(L, -3, "data"); // [-1: Persistent data -2: Arguments -3: AI code -4: AI state]
// Call the function
if (!luaW_pcall(L, 2, 1, true)) { // [-1: Result -2: AI state]
lua_pop(L, 2); // (The result in this case is an error message.)
return; // return with stack size 0 []
}
// Store the state for use by components
lua_setfield(L, -2, "state"); // [-1: AI state]
// And return with empty stack.
lua_pop(L, 1);
}
lua_ai_action_handler* lua_ai_action_handler::create(lua_State *L, char const *code, lua_ai_context &context)
@ -925,7 +1013,6 @@ lua_ai_action_handler* lua_ai_action_handler::create(lua_State *L, char const *c
return NULL;
}
// Retrieve the ai elements table from the registry.
lua_pushlightuserdata(L, static_cast<void *>(const_cast<char *>(&aisKey)));
lua_rawget(L, LUA_REGISTRYINDEX); //stack size is now 2 [-1: ais_table -2: f]
@ -940,19 +1027,26 @@ lua_ai_action_handler* lua_ai_action_handler::create(lua_State *L, char const *c
}
void lua_ai_context::load()
lua_ai_load::lua_ai_load(lua_ai_context& ctx, bool read_only) : L(ctx.L)
{
lua_pushlightuserdata(L, static_cast<void *>(const_cast<char *>(&aisKey)));//stack size is now 1 [-1: ais_table key]
lua_rawget(L, LUA_REGISTRYINDEX);//stack size is still 1 [-1: ais_table]
lua_rawgeti(L, -1, num_);//stack size is 2 [-1: ai_context -2: ais_table]
lua_remove(L,-2);
lua_pushlightuserdata(L, static_cast<void *>(const_cast<char *>(&aisKey))); // [-1: key]
lua_rawget(L, LUA_REGISTRYINDEX); // [-1: AI registry]
lua_rawgeti(L, -1, ctx.num_); // [-1: AI state -2: AI registry]
lua_remove(L,-2); // [-1: AI state]
// Load the AI functions table into global scope
lua_getfield(L, -1, "ai"); // [-1: AI functions -2: AI state]
lua_pushstring(L, "read_only"); // [-1: key -2: AI functions -3: AI state]
lua_pushboolean(L, read_only); // [-1: value -2: key -3: AI functions -4: AI state]
lua_rawset(L, -3); // [-1: AI functions -2: AI state]
lua_setglobal(L, "ai"); // [-1: AI state]
}
void lua_ai_context::load_and_inject_ai_table(ai::engine_lua* engine)
lua_ai_load::~lua_ai_load()
{
load(); //stack size is 1 [-1: ai_context]
generate_and_push_ai_table(L, engine); //stack size is 2 [-1: ai_table -2: ai_context]
lua_setfield(L, -2, "ai"); //stack size is 1 [-1: ai_context]
// Remove the AI functions from the global scope
lua_pushnil(L);
lua_setglobal(L, "ai");
}
lua_ai_context::~lua_ai_context()
@ -965,26 +1059,28 @@ lua_ai_context::~lua_ai_context()
lua_pop(L, 1);
}
void lua_ai_action_handler::handle(config &cfg, bool configOut, lua_object_ptr l_obj)
void lua_ai_action_handler::handle(const config &cfg, bool read_only, lua_object_ptr l_obj)
{
int initial_top = lua_gettop(L);//get the old stack size
// Load the context
lua_ai_load ctx(context_, read_only); // [-1: AI state table]
// Load the user function from the registry.
lua_pushlightuserdata(L, static_cast<void *>(const_cast<char *>(&aisKey)));//stack size is now 1 [-1: ais_table key]
lua_rawget(L, LUA_REGISTRYINDEX);//stack size is still 1 [-1: ais_table]
lua_rawgeti(L, -1, num_);//stack size is 2 [-1: ai_action -2: ais_table]
lua_remove(L, -2);//stack size is 1 [-1: ai_action]
//load the lua ai context as a parameter
context_.load();//stack size is 2 [-1: ai_context -2: ai_action]
if (!configOut)
{
luaW_pushconfig(L, cfg);
luaW_pcall(L, 2, 0, true);
}
else if (luaW_pcall(L, 1, 5, true)) // @note for Crab: how much nrets should we actually have here
{ // there were 2 initially, but aspects like recruitment pattern
l_obj->store(L, initial_top + 1); // return a lot of results
lua_pushlightuserdata(L, static_cast<void *>(const_cast<char *>(&aisKey))); // [-1: key -2: AI state]
lua_rawget(L, LUA_REGISTRYINDEX); // [-1: AI registry -2: AI state]
lua_rawgeti(L, -1, num_); // [-1: AI action -2: AI registry -3: AI state]
lua_remove(L, -2); // [-1: AI action -2: AI state]
// Load the arguments
luaW_pushconfig(L, cfg); // [-1: parameters -2: AI action -3: AI state]
lua_getfield(L, -3, "data"); // [-1: data -2: parameters -3: action -4: state]
lua_getfield(L, -4, "state");
// Call the function
luaW_pcall(L, 3, l_obj ? 1 : 0, true);
if (l_obj) {
l_obj->store(L, initial_top + 1);
}
lua_settop(L, initial_top);//empty stack

View file

@ -44,20 +44,23 @@ private:
static lua_ai_context* create(lua_State *L, char const *code, engine_lua *engine);
public:
~lua_ai_context();
lua_ai_context()
: L(NULL)
, num_(0)
, side_(0)
{
}
void load();
void load_and_inject_ai_table(engine_lua* engine);
void update_state();
void get_persistent_data(config &) const;
void set_persistent_data(const config &);
void get_arguments(config &) const;
void set_arguments(const config &);
static void init(lua_State *L);
friend class ::game_lua_kernel;
friend class lua_ai_load;
};
class lua_ai_load
{
lua_State* L;
public:
lua_ai_load(lua_ai_context& ctx, bool read_only);
~lua_ai_load();
};
/**
* Proxy class for calling AI action handlers defined in Lua.
@ -74,7 +77,7 @@ private:
static lua_ai_action_handler* create(lua_State *L, char const *code, lua_ai_context &context);
public:
~lua_ai_action_handler();
void handle(config &, bool configOut, lua_object_ptr);
void handle(const config &cfg, bool read_only, lua_object_ptr l_obj);
friend class ::game_lua_kernel;
};

View file

@ -135,11 +135,10 @@ inline boost::shared_ptr<config> lua_object<config>::to_type(lua_State *L, int n
template <>
inline boost::shared_ptr<terrain_filter> lua_object<terrain_filter>::to_type(lua_State *L, int n)
{
// To Crab_: Is this part ok? I tested it, works fine
boost::shared_ptr<config> cfg = boost::shared_ptr<config>(new config());
boost::shared_ptr<vconfig> vcfg = boost::shared_ptr<vconfig>(new vconfig(*cfg));
luaW_tovconfig(L, n, *vcfg);
boost::shared_ptr<terrain_filter> tf = boost::shared_ptr<terrain_filter>(new terrain_filter(*vcfg, resources::filter_con));
boost::shared_ptr<terrain_filter> tf(new terrain_filter(*vcfg, resources::filter_con));
return tf;
}