move wesnoth lua ai support from scripting/lua.?pp...

...into separate files in src/ai/lua/
This commit is contained in:
Iurii Chernyi 2010-04-30 13:42:49 +00:00
parent f6953c07ac
commit 41291670bb
16 changed files with 576 additions and 363 deletions

View file

@ -98,6 +98,8 @@
<Unit filename="..\..\src\ai\gamestate_observer.hpp" />
<Unit filename="..\..\src\ai\interface.cpp" />
<Unit filename="..\..\src\ai\interface.hpp" />
<Unit filename="..\..\src\ai\lua\core.cpp" />
<Unit filename="..\..\src\ai\lua\core.hpp" />
<Unit filename="..\..\src\ai\manager.cpp" />
<Unit filename="..\..\src\ai\manager.hpp" />
<Unit filename="..\..\src\ai\registry.cpp" />
@ -590,6 +592,7 @@
<Unit filename="..\..\src\scoped_resource.hpp" />
<Unit filename="..\..\src\scripting\lua.cpp" />
<Unit filename="..\..\src\scripting\lua.hpp" />
<Unit filename="..\..\src\scripting\lua_api.hpp" />
<Unit filename="..\..\src\sdl_utils.cpp" />
<Unit filename="..\..\src\sdl_utils.hpp" />
<Unit filename="..\..\src\serialization\binary_or_text.cpp" />

View file

@ -127,6 +127,8 @@
<Unit filename="..\..\src\ai\gamestate_observer.hpp" />
<Unit filename="..\..\src\ai\interface.cpp" />
<Unit filename="..\..\src\ai\interface.hpp" />
<Unit filename="..\..\src\ai\lua\core.cpp" />
<Unit filename="..\..\src\ai\lua\core.hpp" />
<Unit filename="..\..\src\ai\manager.cpp" />
<Unit filename="..\..\src\ai\manager.hpp" />
<Unit filename="..\..\src\ai\registry.cpp" />
@ -619,6 +621,7 @@
<Unit filename="..\..\src\scoped_resource.hpp" />
<Unit filename="..\..\src\scripting\lua.cpp" />
<Unit filename="..\..\src\scripting\lua.hpp" />
<Unit filename="..\..\src\scripting\lua_core.hpp" />
<Unit filename="..\..\src\sdl_utils.cpp" />
<Unit filename="..\..\src\sdl_utils.hpp" />
<Unit filename="..\..\src\serialization\binary_or_text.cpp" />

View file

@ -4811,6 +4811,38 @@
</FileConfiguration>
</File>
</Filter>
<Filter
Name="lua"
>
<File
RelativePath="..\..\src\ai\lua\core.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)\ai\lua\"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)\ai\lua\"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug (fast)|Win32"
>
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)\ai\lua\"
/>
</FileConfiguration>
</File>
</Filter>
<Filter
Name="testing"
>
@ -6010,6 +6042,10 @@
RelativePath="..\..\src\scripting\lua.hpp"
>
</File>
<File
RelativePath="..\..\src\scripting\lua_api.hpp"
>
</File>
</Filter>
<Filter
Name="Storyscreen"
@ -6174,6 +6210,14 @@
>
</File>
</Filter>
<Filter
Name="lua"
>
<File
RelativePath="..\..\src\ai\lua\core.hpp"
>
</File>
</Filter>
<Filter
Name="testing"
>

View file

@ -241,6 +241,7 @@ set(wesnoth-main_SRC
ai/game_info.cpp
ai/gamestate_observer.cpp
ai/interface.cpp
ai/lua/core.cpp
ai/manager.cpp
ai/registry.cpp
ai/testing/aspect_attacks.cpp

View file

@ -68,6 +68,7 @@ wesnoth_source = \
ai/game_info.cpp \
ai/gamestate_observer.cpp \
ai/interface.cpp \
ai/lua/core.cpp \
ai/manager.cpp \
ai/registry.cpp \
ai/testing/aspect_attacks.cpp \

View file

@ -173,6 +173,7 @@ wesnoth_sources = Split("""
ai/game_info.cpp
ai/gamestate_observer.cpp
ai/interface.cpp
ai/lua/core.cpp
ai/manager.cpp
ai/registry.cpp
ai/testing/aspect_attacks.cpp

View file

@ -26,6 +26,7 @@
#include "../../log.hpp"
#include "../../resources.hpp"
#include "../lua/core.hpp"
#include "../../scripting/lua.hpp"
#include "../../util.hpp"
@ -49,8 +50,8 @@ public:
lua_candidate_action_wrapper( rca_context &context, const config &cfg, lua_ai_context &lua_ai_ctx)
: candidate_action(context,cfg),evaluation_(cfg["evaluation"]),evaluation_action_handler_(),execution_(cfg["execution"]),execution_action_handler_(),serialized_evaluation_state_()
{
evaluation_action_handler_ = boost::shared_ptr<lua_ai_action_handler>(resources::lua_kernel->create_ai_action_handler(evaluation_.c_str(),lua_ai_ctx));
execution_action_handler_ = boost::shared_ptr<lua_ai_action_handler>(resources::lua_kernel->create_ai_action_handler(execution_.c_str(),lua_ai_ctx));
evaluation_action_handler_ = boost::shared_ptr<lua_ai_action_handler>(resources::lua_kernel->create_lua_ai_action_handler(evaluation_.c_str(),lua_ai_ctx));
execution_action_handler_ = boost::shared_ptr<lua_ai_action_handler>(resources::lua_kernel->create_lua_ai_action_handler(execution_.c_str(),lua_ai_ctx));
}
virtual ~lua_candidate_action_wrapper() {}
@ -99,7 +100,7 @@ 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"))
{
action_handler_ = boost::shared_ptr<lua_ai_action_handler>(resources::lua_kernel->create_ai_action_handler(code_.c_str(),lua_ai_ctx));
action_handler_ = boost::shared_ptr<lua_ai_action_handler>(resources::lua_kernel->create_lua_ai_action_handler(code_.c_str(),lua_ai_ctx));
}
virtual ~lua_stage_wrapper()
@ -136,9 +137,9 @@ private:
*/
engine_lua::engine_lua( readonly_context &context, const config &cfg )
: engine(context,cfg)
, lua_ai_context_(resources::lua_kernel->create_ai_context(
, lua_ai_context_(resources::lua_kernel->create_lua_ai_context(
cfg["code"].c_str()
, this)) //will be moved to set_ai_context
, this))
{
name_ = "lua";
}

View file

@ -25,11 +25,11 @@
#include "engine.hpp"
#include "../contexts.hpp"
class lua_ai_context;
//============================================================================
namespace ai {
class lua_ai_context;
class engine_lua : public engine {
public:
engine_lua( readonly_context &context, const config &cfg );

354
src/ai/lua/core.cpp Normal file
View file

@ -0,0 +1,354 @@
/* $Id$ */
/*
Copyright (C) 2010 by Yurii Chernyi <terraninfo@terraninfo.net>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2
or at your option any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/**
* @file ai/lua/core.cpp
* Provides core classes for the Lua AI.
*
*/
extern "C" {
#include <lualib.h>
#include <lauxlib.h>
}
#include <cassert>
#include <cstring>
#include "core.hpp"
#include "../../scripting/lua.hpp"
#include "../../scripting/lua_api.hpp"
#include "../../actions.hpp"
#include "../../attack_prediction.hpp"
#include "../../filesystem.hpp"
#include "../../foreach.hpp"
#include "../../game_display.hpp"
#include "../../gamestatus.hpp"
#include "../../log.hpp"
#include "../../map.hpp"
#include "../../pathfind/pathfind.hpp"
#include "../../play_controller.hpp"
#include "../../resources.hpp"
#include "../../terrain_translation.hpp"
#include "../../unit.hpp"
#include "../actions.hpp"
#include "../composite/engine_lua.hpp"
static lg::log_domain log_ai_engine_lua("ai/engine/lua");
#define LOG_LUA LOG_STREAM(info, log_ai_engine_lua)
#define ERR_LUA LOG_STREAM(err, log_ai_engine_lua)
static char const aisKey = 0;
namespace ai {
void lua_ai_context::init(lua_State *L)
{
// Create the ai elements table.
lua_pushlightuserdata(L, (void *)&aisKey);
lua_newtable(L);
lua_rawset(L, LUA_REGISTRYINDEX);
}
static int transform_ai_action(lua_State *L, ai::action_result_ptr action_result)
{
lua_newtable(L);
lua_pushboolean(L,action_result->is_ok());
lua_setfield(L, -2, "ok");
lua_pushboolean(L,action_result->is_gamestate_changed());
lua_setfield(L, -2, "gamestate_changed");
lua_pushinteger(L,action_result->get_status());
lua_setfield(L, -2, "status");
return 1;
}
static bool to_map_location(lua_State *L, int &index, map_location &res)
{
if (lua_isuserdata(L, index))
{
unit const *u = lua::luaW_tounit(L, index);
if (!u) return false;
res = u->get_location();
++index;
}
else
{
if (!lua_isnumber(L, index)) return false;
res.x = lua_tointeger(L, index) - 1;
++index;
if (!lua_isnumber(L, index)) return false;
res.y = lua_tointeger(L, index) - 1;
++index;
}
return true;
}
static int ai_execute_move(lua_State *L, bool remove_movement)
{
int index = 1;
if (false) {
error_call_destructors:
return luaL_typerror(L, index, "location (unit/integers)");
}
int side = ((ai::engine_lua*)lua_touserdata(L,lua_upvalueindex(1)))->get_readonly_context().get_side();
map_location from, to;
if (!to_map_location(L, index, from)) goto error_call_destructors;
if (!to_map_location(L, index, to)) goto error_call_destructors;
ai::move_result_ptr move_result = ai::actions::execute_move_action(side,true,from,to,remove_movement);
return transform_ai_action(L,move_result);
}
static int cfun_ai_execute_move_full(lua_State *L)
{
return ai_execute_move(L, true);
}
static int cfun_ai_execute_move_partial(lua_State *L)
{
return ai_execute_move(L, false);
}
static int cfun_ai_execute_attack(lua_State *L)
{
int index = 1;
if (false) {
error_call_destructors:
return luaL_typerror(L, index, "location (unit/integers)");
}
ai::readonly_context &context = ((ai::engine_lua*)lua_touserdata(L,lua_upvalueindex(1)))->get_readonly_context();
int side = context.get_side();
map_location attacker, defender;
if (!to_map_location(L, index, attacker)) goto error_call_destructors;
if (!to_map_location(L, index, defender)) goto error_call_destructors;
int attacker_weapon = -1;//-1 means 'select what is best'
double aggression = context.get_aggression();//use the aggression from the context
if (!lua_isnoneornil(L, index+1) && lua_isnumber(L,index+1)) {
aggression = lua_tonumber(L, index+1);
}
if (!lua_isnoneornil(L, index)) {
attacker_weapon = lua_tointeger(L, index);
}
ai::attack_result_ptr attack_result = ai::actions::execute_attack_action(side,true,attacker,defender,attacker_weapon,aggression);
return transform_ai_action(L,attack_result);
}
static int ai_execute_stopunit_select(lua_State *L, bool remove_movement, bool remove_attacks)
{
int index = 1;
if (false) {
error_call_destructors:
return luaL_typerror(L, index, "location (unit/integers)");
}
int side = ((ai::engine_lua*)lua_touserdata(L,lua_upvalueindex(1)))->get_readonly_context().get_side();
map_location loc;
if (!to_map_location(L, index, loc)) goto error_call_destructors;
ai::stopunit_result_ptr stopunit_result = ai::actions::execute_stopunit_action(side,true,loc,remove_movement,remove_attacks);
return transform_ai_action(L,stopunit_result);
}
static int cfun_ai_execute_stopunit_moves(lua_State *L)
{
return ai_execute_stopunit_select(L, true, false);
}
static int cfun_ai_execute_stopunit_attacks(lua_State *L)
{
return ai_execute_stopunit_select(L, false, true);
}
static int cfun_ai_execute_stopunit_all(lua_State *L)
{
return ai_execute_stopunit_select(L, true, true);
}
static int cfun_ai_execute_recruit(lua_State *L)
{
const char *unit_name = luaL_checkstring(L, 1);
int side = ((ai::engine_lua*)lua_touserdata(L,lua_upvalueindex(1)))->get_readonly_context().get_side();
map_location where;
if (!lua_isnoneornil(L, 2)) {
where.x = lua_tonumber(L, 2) - 1;
where.y = lua_tonumber(L, 3) - 1;
}
ai::recruit_result_ptr recruit_result = ai::actions::execute_recruit_action(side,true,std::string(unit_name),where);
return transform_ai_action(L,recruit_result);
}
static int cfun_ai_execute_recall(lua_State *L)
{
const char *unit_id = luaL_checkstring(L, 1);
int side = ((ai::engine_lua*)lua_touserdata(L,lua_upvalueindex(1)))->get_readonly_context().get_side();
map_location where;
if (!lua_isnoneornil(L, 2)) {
where.x = lua_tonumber(L, 2) - 1;
where.y = lua_tonumber(L, 3) - 1;
}
ai::recall_result_ptr recall_result = ai::actions::execute_recall_action(side,true,std::string(unit_id),where);
return transform_ai_action(L,recall_result);
}
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)
{
char const *m = lua_tostring(L, -1);
ERR_LUA << "error while initializing ai: " <<m << '\n';
lua_pop(L, 2);//return with stack size 0 []
return NULL;
}
//push data table here
lua_newtable(L);// stack size is 2 [ -1: new table, -2: ai as string ]
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 luaL_reg const 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 },
{ 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);
}
//compile the ai as a closure
if (!lua::luaW_pcall(L, 1, 1, true)) {
return NULL;//return with stack size 0 []
}
// Retrieve the ai elements table from the registry.
lua_pushlightuserdata(L, (void *)&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_objlen(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]
lua_pop(L, 2);
return new lua_ai_context(L, length_ai + 1, engine->get_readonly_context().get_side());
}
lua_ai_action_handler* lua_ai_action_handler::create(lua_State *L, char const *code, lua_ai_context &context)
{
int res = luaL_loadstring(L, code);//stack size is now 1 [ -1: f]
if (res)
{
char const *m = lua_tostring(L, -1);
ERR_LUA << "error while creating ai function: " <<m << '\n';
lua_pop(L, 2);//return with stack size 0 []
return NULL;
}
// Retrieve the ai elements table from the registry.
lua_pushlightuserdata(L, (void *)&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 = lua_objlen(L, -1);//length of ais_table
lua_pushvalue(L, -2); //stack size is now 3: [-1: f -2: ais_table -3: f]
lua_rawseti(L, -2, length + 1);// ais_table[length+1]=f. stack size is now 2 [-1: ais_table -2: f]
lua_remove(L, -1);//stack size is now 1 [-1: f]
lua_remove(L, -1);//stack size is now 0 []
// Create the proxy C++ action handler.
return new lua_ai_action_handler(L, context, length + 1);
}
void lua_ai_context::load()
{
lua_pushlightuserdata(L, (void *)&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_ai_context::~lua_ai_context()
{
// Remove the ai context from the registry, so that it can be collected.
lua_pushlightuserdata(L, (void *)&aisKey);
lua_rawget(L, LUA_REGISTRYINDEX);
lua_pushnil(L);
lua_rawseti(L, -2, num_);
lua_pop(L, 1);
}
void lua_ai_action_handler::handle(config &cfg, bool configOut)
{
int initial_top = lua_gettop(L);//get the old stack size
// Load the user function from the registry.
lua_pushlightuserdata(L, (void *)&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)
{
lua_newtable(L);//stack size is 3 [-1: table -2: ai_context -3: ai_action]
lua::table_of_wml_config(L, cfg);//the new table now contains the config
lua::luaW_pcall(L, 2, LUA_MULTRET, true);
}
else if (lua_gettop(L) > initial_top)
{
if (lua::luaW_pcall(L, 1, LUA_MULTRET, true)) {
int score = lua_tonumber(L, initial_top + 1);//get score
if (lua_gettop(L) >= initial_top + 2) {//check if we also have config
lua::luaW_toconfig(L, initial_top + 2, cfg);//get config
}
cfg["score"] = score; // write score to the config
}
}
lua_settop(L, initial_top);//empty stack
}
lua_ai_action_handler::~lua_ai_action_handler()
{
// Remove the function from the registry, so that it can be collected.
lua_pushlightuserdata(L, (void *)&aisKey);
lua_rawget(L, LUA_REGISTRYINDEX);
lua_pushnil(L);
lua_rawseti(L, -2, num_);
lua_pop(L, 1);
}
} // of namespace ai

72
src/ai/lua/core.hpp Normal file
View file

@ -0,0 +1,72 @@
/* $Id$ */
/*
Copyright (C) 2010 by Yurii Chernyi <terraninfo@terraninfo.net>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2
or at your option any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#ifndef AI_LUA_CORE_HPP
#define AI_LUA_CORE_HPP
struct lua_State;
namespace lua {
class LuaKernel;
} // of namespace lua
class config;
namespace ai {
class engine_lua;
/**
* Proxy table for the AI context
*/
class lua_ai_context
{
private:
lua_State *L;
int num_;
int side_;
lua_ai_context(lua_State *l, int num, int side) : L(l), num_(num), side_(side)
{
}
static lua_ai_context* create(lua_State *L, char const *code, engine_lua *engine);
public:
~lua_ai_context();
void load();
static void init(lua_State *L);
friend class lua::LuaKernel;
};
/**
* Proxy class for calling AI action handlers defined in Lua.
*/
class lua_ai_action_handler
{
private:
lua_State *L;
lua_ai_context &context_;
int num_;
lua_ai_action_handler(lua_State *l, lua_ai_context &context, int num) : L(l), context_(context),num_(num)
{
}
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 = false);
friend class lua::LuaKernel;
};
}//of namespace ai
#endif

View file

@ -3369,7 +3369,7 @@ namespace game_events {
unit_wml_ids.insert(id);
}
resources::lua_kernel = new LuaKernel;
resources::lua_kernel = new lua::LuaKernel;
manager_running = true;
foreach (static_wml_action_map::value_type &action, static_wml_actions) {

View file

@ -22,7 +22,7 @@ namespace resources
unit_map *units;
std::vector<team> *teams;
game_state *state_of_game;
LuaKernel *lua_kernel;
lua::LuaKernel *lua_kernel;
play_controller *controller;
::tod_manager *tod_manager;
}

View file

@ -20,7 +20,10 @@
class game_display;
class gamemap;
class game_state;
class LuaKernel;
namespace lua {
class LuaKernel;
} //of namespace lua
class play_controller;
class team;
class tod_manager;
@ -36,7 +39,7 @@ namespace resources
extern unit_map *units;
extern std::vector<team> *teams;
extern game_state *state_of_game;
extern LuaKernel *lua_kernel;
extern lua::LuaKernel *lua_kernel;
extern play_controller *controller;
extern tod_manager *tod_manager;
}

View file

@ -39,6 +39,7 @@ extern "C" {
#include <cstring>
#include "scripting/lua.hpp"
#include "scripting/lua_api.hpp"
#include "actions.hpp"
#include "attack_prediction.hpp"
@ -53,13 +54,13 @@ extern "C" {
#include "resources.hpp"
#include "terrain_translation.hpp"
#include "unit.hpp"
#include "ai/actions.hpp"
#include "ai/composite/engine_lua.hpp"
#include "ai/lua/core.hpp"
static lg::log_domain log_scripting_lua("scripting/lua");
#define LOG_LUA LOG_STREAM(info, log_scripting_lua)
#define ERR_LUA LOG_STREAM(err, log_scripting_lua)
namespace lua {
/**
* Stack storing the queued_event objects needed for calling WML actions.
@ -97,7 +98,6 @@ static char const tstringKey = 0;
static char const uactionKey = 0;
static char const vconfigKey = 0;
static char const wactionKey = 0;
static char const aisKey = 0;
/* Global definition so that it does not leak on longjmp. */
static std::string error_buffer;
@ -162,7 +162,7 @@ static void luaW_pushscalar(lua_State *L, t_string const &v)
/**
* Returns true if the metatable of the object is the one found in the registry.
*/
static bool luaW_hasmetatable(lua_State *L, int index, char const &key)
bool luaW_hasmetatable(lua_State *L, int index, char const &key)
{
if (!lua_getmetatable(L, index))
return false;
@ -203,7 +203,7 @@ static bool luaW_totstring(lua_State *L, int index, t_string &str)
* The destination table should be at the top of the stack on entry. It is
* still at the top on exit.
*/
static void table_of_wml_config(lua_State *L, config const &cfg)
void table_of_wml_config(lua_State *L, config const &cfg)
{
if (!lua_checkstack(L, LUA_MINSTACK))
return;
@ -236,7 +236,7 @@ static void table_of_wml_config(lua_State *L, config const &cfg)
* @note If the table has holes in the integer keys or floating-point keys,
* some keys will be ignored and the error will go undetected.
*/
static bool luaW_toconfig(lua_State *L, int index, config &cfg, int tstring_meta = 0)
bool luaW_toconfig(lua_State *L, int index, config &cfg, int tstring_meta)
{
if (!lua_checkstack(L, LUA_MINSTACK))
return false;
@ -357,8 +357,8 @@ static bool luaW_tovconfig(lua_State *L, int index, vconfig &vcfg, bool def = tr
* Calls a Lua function stored below its @a nArgs arguments at the top of the stack.
* @return true if the call was successful and @a nRets return values are available.
*/
static bool luaW_pcall(lua_State *L
, int nArgs, int nRets, bool allow_wml_error = false)
bool luaW_pcall(lua_State *L
, int nArgs, int nRets, bool allow_wml_error)
{
// Load the error handler before the function and its arguments.
lua_pushlightuserdata(L, (void *)&executeKey);
@ -395,23 +395,12 @@ static bool luaW_pcall(lua_State *L
return true;
}
/**
* Storage for a unit, either one on the map, or one owned by the Lua code.
*/
class lua_unit
{
size_t uid;
unit *ptr;
lua_unit(lua_unit const &);
public:
lua_unit(size_t u): uid(u), ptr(NULL) {}
lua_unit(unit *u): uid(0), ptr(u) {}
~lua_unit() { delete ptr; }
bool on_map() const { return !ptr; }
void reload();
unit *get();
};
lua_unit::~lua_unit()
{
delete ptr;
}
unit *lua_unit::get()
{
@ -432,7 +421,7 @@ void lua_unit::reload()
/**
* Converts a Lua value to a unit pointer.
*/
static unit *luaW_tounit(lua_State *L, int index, bool only_on_map = false)
unit *luaW_tounit(lua_State *L, int index, bool only_on_map)
{
if (!luaW_hasmetatable(L, index, getunitKey)) return NULL;
lua_unit *lu = static_cast<lua_unit *>(lua_touserdata(L, index));
@ -2379,10 +2368,7 @@ LuaKernel::LuaKernel()
// Create the ai elements table.
lua_pushlightuserdata(L, (void *)&aisKey);
lua_newtable(L);
lua_rawset(L, LUA_REGISTRYINDEX);
ai::lua_ai_context::init(L);
// Delete dofile and loadfile.
lua_pushnil(L);
@ -2535,294 +2521,15 @@ bool LuaKernel::execute(char const *prog, int nArgs, int nRets)
return luaW_pcall(L, nArgs, nRets);
}
static int transform_ai_action(lua_State *L, ai::action_result_ptr action_result)
ai::lua_ai_context* LuaKernel::create_lua_ai_context(char const *code, ai::engine_lua *engine)
{
lua_newtable(L);
lua_pushboolean(L,action_result->is_ok());
lua_setfield(L, -2, "ok");
lua_pushboolean(L,action_result->is_gamestate_changed());
lua_setfield(L, -2, "gamestate_changed");
lua_pushinteger(L,action_result->get_status());
lua_setfield(L, -2, "status");
return 1;
ai::lua_ai_context::create(mState,code,engine);
}
static bool to_map_location(lua_State *L, int &index, map_location &res)
ai::lua_ai_action_handler* LuaKernel::create_lua_ai_action_handler(char const *code, ai::lua_ai_context &context)
{
if (lua_isuserdata(L, index))
{
if (!luaW_hasmetatable(L, index, getunitKey)) return false;
unit const *u = static_cast<lua_unit *>(lua_touserdata(L, index))->get();
if (!u) return false;
res = u->get_location();
++index;
}
else
{
if (!lua_isnumber(L, index)) return false;
res.x = lua_tointeger(L, index) - 1;
++index;
if (!lua_isnumber(L, index)) return false;
res.y = lua_tointeger(L, index) - 1;
++index;
}
return true;
ai::lua_ai_action_handler::create(mState,code,context);
}
static int ai_execute_move(lua_State *L, bool remove_movement)
{
int index = 1;
if (false) {
error_call_destructors:
return luaL_typerror(L, index, "location (unit/integers)");
}
} // of namespace lua
int side = ((ai::engine_lua*)lua_touserdata(L,lua_upvalueindex(1)))->get_readonly_context().get_side();
map_location from, to;
if (!to_map_location(L, index, from)) goto error_call_destructors;
if (!to_map_location(L, index, to)) goto error_call_destructors;
ai::move_result_ptr move_result = ai::actions::execute_move_action(side,true,from,to,remove_movement);
return transform_ai_action(L,move_result);
}
static int cfun_ai_execute_move_full(lua_State *L)
{
return ai_execute_move(L, true);
}
static int cfun_ai_execute_move_partial(lua_State *L)
{
return ai_execute_move(L, false);
}
static int cfun_ai_execute_attack(lua_State *L)
{
int index = 1;
if (false) {
error_call_destructors:
return luaL_typerror(L, index, "location (unit/integers)");
}
ai::readonly_context &context = ((ai::engine_lua*)lua_touserdata(L,lua_upvalueindex(1)))->get_readonly_context();
int side = context.get_side();
map_location attacker, defender;
if (!to_map_location(L, index, attacker)) goto error_call_destructors;
if (!to_map_location(L, index, defender)) goto error_call_destructors;
int attacker_weapon = -1;//-1 means 'select what is best'
double aggression = context.get_aggression();//use the aggression from the context
if (!lua_isnoneornil(L, index+1) && lua_isnumber(L,index+1)) {
aggression = lua_tonumber(L, index+1);
}
if (!lua_isnoneornil(L, index)) {
attacker_weapon = lua_tointeger(L, index);
}
ai::attack_result_ptr attack_result = ai::actions::execute_attack_action(side,true,attacker,defender,attacker_weapon,aggression);
return transform_ai_action(L,attack_result);
}
static int ai_execute_stopunit_select(lua_State *L, bool remove_movement, bool remove_attacks)
{
int index = 1;
if (false) {
error_call_destructors:
return luaL_typerror(L, index, "location (unit/integers)");
}
int side = ((ai::engine_lua*)lua_touserdata(L,lua_upvalueindex(1)))->get_readonly_context().get_side();
map_location loc;
if (!to_map_location(L, index, loc)) goto error_call_destructors;
ai::stopunit_result_ptr stopunit_result = ai::actions::execute_stopunit_action(side,true,loc,remove_movement,remove_attacks);
return transform_ai_action(L,stopunit_result);
}
static int cfun_ai_execute_stopunit_moves(lua_State *L)
{
return ai_execute_stopunit_select(L, true, false);
}
static int cfun_ai_execute_stopunit_attacks(lua_State *L)
{
return ai_execute_stopunit_select(L, false, true);
}
static int cfun_ai_execute_stopunit_all(lua_State *L)
{
return ai_execute_stopunit_select(L, true, true);
}
static int cfun_ai_execute_recruit(lua_State *L)
{
const char *unit_name = luaL_checkstring(L, 1);
int side = ((ai::engine_lua*)lua_touserdata(L,lua_upvalueindex(1)))->get_readonly_context().get_side();
map_location where;
if (!lua_isnoneornil(L, 2)) {
where.x = lua_tonumber(L, 2) - 1;
where.y = lua_tonumber(L, 3) - 1;
}
ai::recruit_result_ptr recruit_result = ai::actions::execute_recruit_action(side,true,std::string(unit_name),where);
return transform_ai_action(L,recruit_result);
}
static int cfun_ai_execute_recall(lua_State *L)
{
const char *unit_id = luaL_checkstring(L, 1);
int side = ((ai::engine_lua*)lua_touserdata(L,lua_upvalueindex(1)))->get_readonly_context().get_side();
map_location where;
if (!lua_isnoneornil(L, 2)) {
where.x = lua_tonumber(L, 2) - 1;
where.y = lua_tonumber(L, 3) - 1;
}
ai::recall_result_ptr recall_result = ai::actions::execute_recall_action(side,true,std::string(unit_id),where);
return transform_ai_action(L,recall_result);
}
lua_ai_context* LuaKernel::create_ai_context(char const *code, ai::engine_lua *engine)
{
lua_State *L = mState;
int res_ai = luaL_loadstring(L, code);//stack size is now 1 [ -1: ai_context]
if (res_ai)
{
char const *m = lua_tostring(L, -1);
ERR_LUA << "error while initializing ai: " <<m << '\n';
lua_pop(L, 2);//return with stack size 0 []
return NULL;
}
//push data table here
lua_newtable(L);// stack size is 2 [ -1: new table, -2: ai as string ]
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 luaL_reg const 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 },
{ 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);
}
//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, (void *)&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_objlen(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]
lua_pop(L, 2);
return new lua_ai_context(L, length_ai + 1, engine->get_readonly_context().get_side());
}
lua_ai_action_handler* LuaKernel::create_ai_action_handler(char const *code, lua_ai_context &context)
{
lua_State *L = mState;
int res = luaL_loadstring(L, code);//stack size is now 1 [ -1: f]
if (res)
{
char const *m = lua_tostring(L, -1);
ERR_LUA << "error while creating ai function: " <<m << '\n';
lua_pop(L, 2);//return with stack size 0 []
return NULL;
}
// Retrieve the ai elements table from the registry.
lua_pushlightuserdata(L, (void *)&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 = lua_objlen(L, -1);//length of ais_table
lua_pushvalue(L, -2); //stack size is now 3: [-1: f -2: ais_table -3: f]
lua_rawseti(L, -2, length + 1);// ais_table[length+1]=f. stack size is now 2 [-1: ais_table -2: f]
lua_remove(L, -1);//stack size is now 1 [-1: f]
lua_remove(L, -1);//stack size is now 0 []
// Create the proxy C++ action handler.
return new lua_ai_action_handler(L, context, length + 1);
}
void lua_ai_context::load()
{
lua_pushlightuserdata(L, (void *)&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_ai_context::~lua_ai_context()
{
// Remove the ai context from the registry, so that it can be collected.
lua_pushlightuserdata(L, (void *)&aisKey);
lua_rawget(L, LUA_REGISTRYINDEX);
lua_pushnil(L);
lua_rawseti(L, -2, num_);
lua_pop(L, 1);
}
void lua_ai_action_handler::handle(config &cfg, bool configOut)
{
int initial_top = lua_gettop(L);//get the old stack size
// Load the user function from the registry.
lua_pushlightuserdata(L, (void *)&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)
{
lua_newtable(L);//stack size is 3 [-1: table -2: ai_context -3: ai_action]
table_of_wml_config(L, cfg);//the new table now contains the config
luaW_pcall(L, 2, LUA_MULTRET, true);
}
else if (lua_gettop(L) > initial_top)
{
if (luaW_pcall(L, 1, LUA_MULTRET, true)) {
int score = lua_tonumber(L, initial_top + 1);//get score
if (lua_gettop(L) >= initial_top + 2) {//check if we also have config
luaW_toconfig(L, initial_top + 2, cfg);//get config
}
cfg["score"] = score; // write score to the config
}
}
lua_settop(L, initial_top);//empty stack
}
lua_ai_action_handler::~lua_ai_action_handler()
{
// Remove the function from the registry, so that it can be collected.
lua_pushlightuserdata(L, (void *)&aisKey);
lua_rawget(L, LUA_REGISTRYINDEX);
lua_pushnil(L);
lua_rawseti(L, -2, num_);
lua_pop(L, 1);
}

View file

@ -20,44 +20,12 @@
struct lua_State;
namespace ai {
struct engine_lua;
}
/**
* Proxy table for the AI context
*/
class lua_ai_context
{
private:
lua_State *L;
int num_;
int side_;
public:
lua_ai_context(lua_State *l, int num, int side) : L(l), num_(num), side_(side)
{
}
~lua_ai_context();
void load();
};
/**
* Proxy class for calling AI action handlers defined in Lua.
*/
class lua_ai_action_handler
{
private:
lua_State *L;
lua_ai_context &context_;
int num_;
public:
lua_ai_action_handler(lua_State *l, lua_ai_context &context, int num) : L(l), context_(context),num_(num)
{
}
~lua_ai_action_handler();
void handle(config &, bool configOut = false);
};
class lua_ai_action_handler;
class lua_ai_context;
class engine_lua;
} // of namespace ai
namespace lua {
class LuaKernel
{
@ -70,8 +38,10 @@ public:
bool run_filter(char const *name, unit const &u);
/** Runs a plain script. */
void run(char const *prog) { execute(prog, 0, 0); }
lua_ai_context* create_ai_context(char const *code, ai::engine_lua *engine);
lua_ai_action_handler* create_ai_action_handler(char const *code, lua_ai_context &context);
ai::lua_ai_context* create_lua_ai_context(char const *code, ai::engine_lua *engine);
ai::lua_ai_action_handler* create_lua_ai_action_handler(char const *code, ai::lua_ai_context &context);
};
} //of namespace lua
#endif

53
src/scripting/lua_api.hpp Normal file
View file

@ -0,0 +1,53 @@
/* $Id$ */
/*
Copyright (C) 2009 - 2010 by Guillaume Melquiond <guillaume.melquiond@gmail.com>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2
or at your option any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#ifndef SCRIPTING_LUA_API_HPP
#define SCRIPTING_LUA_API_HPP
#include "game_events.hpp"
struct lua_State;
namespace lua {
bool luaW_pcall(lua_State *L , int nArgs, int nRets, bool allow_wml_error = false);
unit *luaW_tounit(lua_State *L, int index, bool only_on_map = false);
void table_of_wml_config(lua_State *L, config const &cfg);
bool luaW_toconfig(lua_State *L, int index, config &cfg, int tstring_meta = 0);
/**
* Storage for a unit, either one on the map, or one owned by the Lua code.
*/
class lua_unit
{
size_t uid;
unit *ptr;
lua_unit(lua_unit const &);
public:
lua_unit(size_t u): uid(u), ptr(NULL) {}
lua_unit(unit *u): uid(0), ptr(u) {}
~lua_unit();
bool on_map() const { return !ptr; }
void reload();
unit *get();
};
} //of namespace lua
#endif