scripting/lua.cpp

Go to the documentation of this file.
00001 /* $Id: lua.cpp 54087 2012-05-04 00:58:54Z ejls $ */
00002 /*
00003    Copyright (C) 2009 - 2012 by Guillaume Melquiond <guillaume.melquiond@gmail.com>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2 of the License, or
00009    (at your option) any later version.
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY.
00012 
00013    See the COPYING file for more details.
00014 */
00015 
00016 /**
00017  * @file
00018  * Provides a Lua interpreter.
00019  *
00020  * @note Naming conventions:
00021  *   - intf_ functions are exported in the wesnoth domain,
00022  *   - impl_ functions are hidden inside metatables,
00023  *   - cfun_ functions are closures,
00024  *   - luaW_ functions are helpers in Lua style.
00025  */
00026 
00027 #include "lua/lualib.h"
00028 #include "lua/lauxlib.h"
00029 
00030 #include <cassert>
00031 #include <cstring>
00032 #include <boost/bind.hpp>
00033 #include <boost/variant.hpp>
00034 
00035 #include "scripting/lua.hpp"
00036 #include "scripting/lua_api.hpp"
00037 
00038 #include "actions.hpp"
00039 #include "ai/manager.hpp"
00040 #include "attack_prediction.hpp"
00041 #include "filesystem.hpp"
00042 #include "foreach.hpp"
00043 #include "game_display.hpp"
00044 #include "game_preferences.hpp"
00045 #include "gamestatus.hpp"
00046 #include "log.hpp"
00047 #include "lua_jailbreak_exception.hpp"
00048 #include "map.hpp"
00049 #include "pathfind/pathfind.hpp"
00050 #include "pathfind/teleport.hpp"
00051 #include "play_controller.hpp"
00052 #include "replay.hpp"
00053 #include "reports.hpp"
00054 #include "resources.hpp"
00055 #include "terrain_filter.hpp"
00056 #include "terrain_translation.hpp"
00057 #include "side_filter.hpp"
00058 #include "sound.hpp"
00059 #include "unit.hpp"
00060 #include "ai/lua/core.hpp"
00061 #include "version.hpp"
00062 #include "gui/widgets/clickable.hpp"
00063 #ifdef GUI2_EXPERIMENTAL_LISTBOX
00064 #include "gui/widgets/list.hpp"
00065 #else
00066 #include "gui/widgets/listbox.hpp"
00067 #endif
00068 #include "gui/widgets/multi_page.hpp"
00069 #include "gui/widgets/selectable.hpp"
00070 #include "gui/widgets/settings.hpp"
00071 #include "gui/widgets/text_box.hpp"
00072 #include "gui/widgets/slider.hpp"
00073 #include "gui/widgets/progress_bar.hpp"
00074 #include "gui/widgets/window.hpp"
00075 
00076 #ifdef DEBUG_LUA
00077 #include "scripting/debug_lua.hpp"
00078 #endif
00079 
00080 static lg::log_domain log_scripting_lua("scripting/lua");
00081 #define LOG_LUA LOG_STREAM(info, log_scripting_lua)
00082 #define ERR_LUA LOG_STREAM(err, log_scripting_lua)
00083 
00084 static std::vector<config> preload_scripts;
00085 static config preload_config;
00086 
00087 void extract_preload_scripts(config const &game_config)
00088 {
00089     preload_scripts.clear();
00090     foreach (config const &cfg, game_config.child_range("lua")) {
00091         preload_scripts.push_back(cfg);
00092     }
00093     preload_config = game_config.child("game_config");
00094 }
00095 
00096 /**
00097  * Stack storing the queued_event objects needed for calling WML actions.
00098  */
00099 struct queued_event_context
00100 {
00101     typedef game_events::queued_event qe;
00102     static qe default_qe;
00103     static qe const *current_qe;
00104     static qe const &get()
00105     { return *(current_qe ? current_qe : &default_qe); }
00106     qe const *previous_qe;
00107 
00108     queued_event_context(qe const *new_qe)
00109         : previous_qe(current_qe)
00110     {
00111         current_qe = new_qe;
00112     }
00113 
00114     ~queued_event_context()
00115     { current_qe = previous_qe; }
00116 };
00117 
00118 game_events::queued_event const *queued_event_context::current_qe = 0;
00119 game_events::queued_event queued_event_context::default_qe
00120     ("_from_lua", map_location(), map_location(), config());
00121 
00122 /* Dummy pointer for getting unique keys for Lua's registry. */
00123 static char const dlgclbkKey = 0;
00124 static char const executeKey = 0;
00125 static char const getsideKey = 0;
00126 static char const gettextKey = 0;
00127 static char const gettypeKey = 0;
00128 static char const getraceKey = 0;
00129 static char const getunitKey = 0;
00130 static char const tstringKey = 0;
00131 static char const unitvarKey = 0;
00132 static char const ustatusKey = 0;
00133 static char const vconfigKey = 0;
00134 
00135 /**
00136  * Displays a message in the chat window.
00137  */
00138 static void chat_message(std::string const &caption, std::string const &msg)
00139 {
00140     resources::screen->add_chat_message(time(NULL), caption, 0, msg,
00141         events::chat_handler::MESSAGE_PUBLIC, false);
00142 }
00143 
00144 /**
00145  * Pushes a vconfig on the top of the stack.
00146  */
00147 static void luaW_pushvconfig(lua_State *L, vconfig const &cfg)
00148 {
00149     new(lua_newuserdata(L, sizeof(vconfig))) vconfig(cfg);
00150     lua_pushlightuserdata(L
00151             , static_cast<void *>(const_cast<char *>(&vconfigKey)));
00152 
00153     lua_rawget(L, LUA_REGISTRYINDEX);
00154     lua_setmetatable(L, -2);
00155 }
00156 
00157 /**
00158  * Pushes a t_string on the top of the stack.
00159  */
00160 static void luaW_pushtstring(lua_State *L, t_string const &v)
00161 {
00162     new(lua_newuserdata(L, sizeof(t_string))) t_string(v);
00163     lua_pushlightuserdata(L
00164             , static_cast<void *>(const_cast<char *>(&tstringKey)));
00165 
00166     lua_rawget(L, LUA_REGISTRYINDEX);
00167     lua_setmetatable(L, -2);
00168 }
00169 
00170 struct luaW_pushscalar_visitor : boost::static_visitor<>
00171 {
00172     lua_State *L;
00173     luaW_pushscalar_visitor(lua_State *l): L(l) {}
00174     void operator()(boost::blank const &) const
00175     { lua_pushnil(L); }
00176     void operator()(bool b) const
00177     { lua_pushboolean(L, b); }
00178     void operator()(double d) const
00179     { lua_pushnumber(L, d); }
00180     void operator()(size_t s) const
00181     { lua_pushnumber(L,s); }
00182     void operator()(long t) const
00183     { lua_pushnumber(L,t); }
00184     void operator()(int i) const
00185     { lua_pushnumber(L,i); }
00186     void operator()(std::string const &s) const
00187     { lua_pushstring(L, s.c_str()); }
00188     void operator()(t_string const &s) const
00189     { luaW_pushtstring(L, s); }
00190 };
00191 
00192 /**
00193  * Converts a string into a Lua object pushed at the top of the stack.
00194  */
00195 void luaW_pushscalar(lua_State *L, config::attribute_value const &v)
00196 {
00197     boost::apply_visitor(luaW_pushscalar_visitor(L), v.value);
00198 }
00199 
00200 /**
00201  * Returns true if the metatable of the object is the one found in the registry.
00202  */
00203 static bool luaW_hasmetatable(lua_State *L
00204         , int index
00205         , char const &key)
00206 {
00207     if (!lua_getmetatable(L, index))
00208         return false;
00209     lua_pushlightuserdata(L, static_cast<void *>(const_cast<char *>(&key)));
00210     lua_rawget(L, LUA_REGISTRYINDEX);
00211     bool ok = lua_rawequal(L, -1, -2);
00212     lua_pop(L, 2);
00213     return ok;
00214 }
00215 
00216 /**
00217  * Converts a scalar to a translatable string.
00218  */
00219 static bool luaW_totstring(lua_State *L, int index, t_string &str)
00220 {
00221     switch (lua_type(L, index)) {
00222         case LUA_TBOOLEAN:
00223             str = lua_toboolean(L, index) ? "yes" : "no";
00224             break;
00225         case LUA_TNUMBER:
00226         case LUA_TSTRING:
00227             str = lua_tostring(L, index);
00228             break;
00229         case LUA_TUSERDATA:
00230         {
00231             if (!luaW_hasmetatable(L, index, tstringKey)) return false;
00232             str = *static_cast<t_string *>(lua_touserdata(L, index));
00233             break;
00234         }
00235         default:
00236             return false;
00237     }
00238     return true;
00239 }
00240 
00241 /**
00242  * Converts a scalar to a translatable string.
00243  */
00244 static t_string luaW_checktstring(lua_State *L, int index)
00245 {
00246     t_string result;
00247     if (!luaW_totstring(L, index, result))
00248         luaL_typerror(L, index, "translatable string");
00249     return result;
00250 }
00251 
00252 /**
00253  * Converts a config object to a Lua table.
00254  * The destination table should be at the top of the stack on entry. It is
00255  * still at the top on exit.
00256  */
00257 static void luaW_filltable(lua_State *L, config const &cfg)
00258 {
00259     if (!lua_checkstack(L, LUA_MINSTACK))
00260         return;
00261 
00262     int k = 1;
00263     foreach (const config::any_child &ch, cfg.all_children_range())
00264     {
00265         lua_createtable(L, 2, 0);
00266         lua_pushstring(L, ch.key.c_str());
00267         lua_rawseti(L, -2, 1);
00268         lua_newtable(L);
00269         luaW_filltable(L, ch.cfg);
00270         lua_rawseti(L, -2, 2);
00271         lua_rawseti(L, -2, k++);
00272     }
00273     foreach (const config::attribute &attr, cfg.attribute_range())
00274     {
00275         luaW_pushscalar(L, attr.second);
00276         lua_setfield(L, -2, attr.first.c_str());
00277     }
00278 }
00279 
00280 /**
00281  * Converts a config object to a Lua table pushed at the top of the stack.
00282  */
00283 void luaW_pushconfig(lua_State *L, config const &cfg)
00284 {
00285     lua_newtable(L);
00286     luaW_filltable(L, cfg);
00287 }
00288 
00289 #define return_misformed() \
00290   do { lua_settop(L, initial_top); return false; } while (0)
00291 
00292 /**
00293  * Converts an optional table or vconfig to a config object.
00294  * @param tstring_meta absolute stack position of t_string's metatable, or 0 if none.
00295  * @return false if some attributes had not the proper type.
00296  * @note If the table has holes in the integer keys or floating-point keys,
00297  *       some keys will be ignored and the error will go undetected.
00298  */
00299 bool luaW_toconfig(lua_State *L, int index, config &cfg, int tstring_meta)
00300 {
00301     if (!lua_checkstack(L, LUA_MINSTACK))
00302         return false;
00303 
00304     // Get the absolute index of the table.
00305     int initial_top = lua_gettop(L);
00306     if (-initial_top <= index && index <= -1)
00307         index = initial_top + index + 1;
00308 
00309     switch (lua_type(L, index))
00310     {
00311         case LUA_TTABLE:
00312             break;
00313         case LUA_TUSERDATA:
00314         {
00315             if (!luaW_hasmetatable(L, index, vconfigKey))
00316                 return false;
00317             cfg = static_cast<vconfig *>(lua_touserdata(L, index))->get_parsed_config();
00318             return true;
00319         }
00320         case LUA_TNONE:
00321         case LUA_TNIL:
00322             return true;
00323         default:
00324             return false;
00325     }
00326 
00327     // Get t_string's metatable, so that it can be used later to detect t_string object.
00328     if (!tstring_meta) {
00329         lua_pushlightuserdata(L
00330                 , static_cast<void *>(const_cast<char *>(&tstringKey)));
00331 
00332         lua_rawget(L, LUA_REGISTRYINDEX);
00333         tstring_meta = initial_top + 1;
00334     }
00335 
00336     // First convert the children (integer indices).
00337     for (int i = 1, i_end = lua_rawlen(L, index); i <= i_end; ++i)
00338     {
00339         lua_rawgeti(L, index, i);
00340         if (!lua_istable(L, -1)) return_misformed();
00341         lua_rawgeti(L, -1, 1);
00342         char const *m = lua_tostring(L, -1);
00343         if (!m) return_misformed();
00344         lua_rawgeti(L, -2, 2);
00345         if (!luaW_toconfig(L, -1, cfg.add_child(m), tstring_meta))
00346             return_misformed();
00347         lua_pop(L, 3);
00348     }
00349 
00350     // Then convert the attributes (string indices).
00351     for (lua_pushnil(L); lua_next(L, index); lua_pop(L, 1))
00352     {
00353         if (lua_isnumber(L, -2)) continue;
00354         if (!lua_isstring(L, -2)) return_misformed();
00355         config::attribute_value &v = cfg[lua_tostring(L, -2)];
00356         switch (lua_type(L, -1)) {
00357             case LUA_TBOOLEAN:
00358                 v = bool(lua_toboolean(L, -1));
00359                 break;
00360             case LUA_TNUMBER:
00361                 v = lua_tonumber(L, -1);
00362                 break;
00363             case LUA_TSTRING:
00364                 v = lua_tostring(L, -1);
00365                 break;
00366             case LUA_TUSERDATA:
00367             {
00368                 if (!lua_getmetatable(L, -1)) return_misformed();
00369                 bool tstr = lua_rawequal(L, -1, tstring_meta) != 0;
00370                 lua_pop(L, 1);
00371                 if (!tstr) return_misformed();
00372                 v = *static_cast<t_string *>(lua_touserdata(L, -1));
00373                 break;
00374             }
00375             default:
00376                 return_misformed();
00377         }
00378     }
00379 
00380     lua_settop(L, initial_top);
00381     return true;
00382 }
00383 
00384 #undef return_misformed
00385 
00386 /**
00387  * Converts an optional table or vconfig to a config object.
00388  */
00389 static config luaW_checkconfig(lua_State *L, int index)
00390 {
00391     config result;
00392     if (!luaW_toconfig(L, index, result))
00393         luaL_typerror(L, index, "WML table");
00394     return result;
00395 }
00396 
00397 /**
00398  * Gets an optional vconfig from either a table or a userdata.
00399  * @return false in case of failure.
00400  */
00401 bool luaW_tovconfig(lua_State *L, int index, vconfig &vcfg)
00402 {
00403     switch (lua_type(L, index))
00404     {
00405         case LUA_TTABLE:
00406         {
00407             config cfg;
00408             bool ok = luaW_toconfig(L, index, cfg);
00409             if (!ok) return false;
00410             vcfg = vconfig(cfg, true);
00411             break;
00412         }
00413         case LUA_TUSERDATA:
00414             if (!luaW_hasmetatable(L, index, vconfigKey))
00415                 return false;
00416             vcfg = *static_cast<vconfig *>(lua_touserdata(L, index));
00417             break;
00418         case LUA_TNONE:
00419         case LUA_TNIL:
00420             break;
00421         default:
00422             return false;
00423     }
00424     return true;
00425 }
00426 
00427 /**
00428  * Gets an optional vconfig from either a table or a userdata.
00429  * @param allow_missing true if missing values are allowed; the function
00430  *        then returns an unconstructed vconfig.
00431  */
00432 static vconfig luaW_checkvconfig(lua_State *L, int index, bool allow_missing = false)
00433 {
00434     vconfig result = vconfig::unconstructed_vconfig();
00435     if (!luaW_tovconfig(L, index, result) || (!allow_missing && result.null()))
00436         luaL_typerror(L, index, "WML table");
00437     return result;
00438 }
00439 
00440 #ifdef _MSC_VER
00441 #pragma warning (push)
00442 #pragma warning (disable: 4706)
00443 #endif
00444 /**
00445  * Calls a Lua function stored below its @a nArgs arguments at the top of the stack.
00446  * @param nRets LUA_MULTRET for unbounded return values.
00447  * @return true if the call was successful and @a nRets return values are available.
00448  */
00449 bool luaW_pcall(lua_State *L
00450         , int nArgs, int nRets, bool allow_wml_error)
00451 {
00452     // Load the error handler before the function and its arguments.
00453     lua_pushlightuserdata(L
00454             , static_cast<void *>(const_cast<char *>(&executeKey)));
00455 
00456     lua_rawget(L, LUA_REGISTRYINDEX);
00457     lua_insert(L, -2 - nArgs);
00458 
00459     int error_handler_index = lua_gettop(L) - nArgs - 1;
00460 
00461     // Call the function.
00462     int res = lua_pcall(L, nArgs, nRets, -2 - nArgs);
00463     tlua_jailbreak_exception::rethrow();
00464 
00465     if (res)
00466     {
00467         /*
00468          * When an exception is thrown which doesn't derive from
00469          * std::exception m will be NULL pointer.
00470          */
00471         char const *m = lua_tostring(L, -1);
00472         if(m) {
00473             if (allow_wml_error && strncmp(m, "~wml:", 5) == 0) {
00474                 m += 5;
00475                 char const *e = strstr(m, "stack traceback");
00476                 lg::wml_error << std::string(m, e ? e - m : strlen(m));
00477             } else if (allow_wml_error && strncmp(m, "~lua:", 5) == 0) {
00478                 m += 5;
00479                 char const *e = NULL, *em = m;
00480                 while (em[0] && ((em = strstr(em + 1, "stack traceback"))))
00481 #ifdef _MSC_VER
00482 #pragma warning (pop)
00483 #endif
00484                     e = em;
00485                 chat_message("Lua error", std::string(m, e ? e - m : strlen(m)));
00486             } else {
00487                 ERR_LUA << m << '\n';
00488                 chat_message("Lua error", m);
00489             }
00490         } else {
00491             chat_message("Lua caught unknown exception", "");
00492         }
00493         lua_pop(L, 2);
00494         return false;
00495     }
00496 
00497     // Remove the error handler.
00498     lua_remove(L, error_handler_index);
00499 
00500     return true;
00501 }
00502 
00503 /**
00504  * Pushes the value found by following the variadic names (char *), if the
00505  * value is not nil.
00506  * @return true if an element was pushed.
00507  */
00508 #ifdef __GNUC__
00509 __attribute__((sentinel))
00510 #endif
00511 static bool luaW_getglobal(lua_State *L, ...)
00512 {
00513     lua_pushglobaltable(L);
00514     va_list ap;
00515     va_start(ap, L);
00516     while (const char *s = va_arg(ap, const char *))
00517     {
00518         if (!lua_istable(L, -1)) goto discard;
00519         lua_pushstring(L, s);
00520         lua_rawget(L, -2);
00521         lua_remove(L, -2);
00522     }
00523     va_end(ap);
00524 
00525     if (lua_isnil(L, -1)) {
00526         discard:
00527         lua_pop(L, 1);
00528         return false;
00529     }
00530     return true;
00531 }
00532 
00533 lua_unit::~lua_unit()
00534 {
00535     delete ptr;
00536 }
00537 
00538 unit *lua_unit::get()
00539 {
00540     if (ptr) return ptr;
00541     if (side) {
00542         foreach (unit &u, (*resources::teams)[side - 1].recall_list()) {
00543             if (u.underlying_id() == uid) return &u;
00544         }
00545         return NULL;
00546     }
00547     unit_map::unit_iterator ui = resources::units->find(uid);
00548     if (!ui.valid()) return NULL;
00549     return &*ui;
00550 }
00551 
00552 /**
00553  * Converts a Lua value to a unit pointer.
00554  */
00555 unit *luaW_tounit(lua_State *L, int index, bool only_on_map)
00556 {
00557     if (!luaW_hasmetatable(L, index, getunitKey)) return NULL;
00558     lua_unit *lu = static_cast<lua_unit *>(lua_touserdata(L, index));
00559     if (only_on_map && !lu->on_map()) return NULL;
00560     return lu->get();
00561 }
00562 
00563 /**
00564  * Converts a Lua value to a unit pointer.
00565  */
00566 static unit *luaW_checkunit(lua_State *L, int index, bool only_on_map = false)
00567 {
00568     unit *u = luaW_tounit(L, index, only_on_map);
00569     if (!u) luaL_typerror(L, index, "unit");
00570     return u;
00571 }
00572 
00573 /**
00574  * Creates a t_string object (__call metamethod).
00575  * - Arg 1: userdata containing the domain.
00576  * - Arg 2: string to translate.
00577  * - Ret 1: string containing the translatable string.
00578  */
00579 static int impl_gettext(lua_State *L)
00580 {
00581     char const *m = luaL_checkstring(L, 2);
00582     char const *d = static_cast<char *>(lua_touserdata(L, 1));
00583     // Hidden metamethod, so d has to be a string. Use it to create a t_string.
00584     luaW_pushtstring(L, t_string(m, d));
00585     return 1;
00586 }
00587 
00588 /**
00589  * Creates an interface for gettext
00590  * - Arg 1: string containing the domain.
00591  * - Ret 1: a full userdata with __call pointing to lua_gettext.
00592  */
00593 static int intf_textdomain(lua_State *L)
00594 {
00595     size_t l;
00596     char const *m = luaL_checklstring(L, 1, &l);
00597     void *p = lua_newuserdata(L, l + 1);
00598     memcpy(p, m, l + 1);
00599     lua_pushlightuserdata(L
00600             , static_cast<void *>(const_cast<char *>(&gettextKey)));
00601 
00602     lua_rawget(L, LUA_REGISTRYINDEX);
00603     lua_setmetatable(L, -2);
00604     return 1;
00605 }
00606 
00607 /**
00608  * Converts a Lua value at position @a src and appends it to @a dst.
00609  * @note This function is private to lua_tstring_concat. It expects two things.
00610  *       First, the t_string metatable is at the top of the stack on entry. (It
00611  *       is still there on exit.) Second, the caller hasn't any valuable object
00612  *       with dynamic lifetime, since they would be leaked on error.
00613  */
00614 static void tstring_concat_aux(lua_State *L, t_string &dst, int src)
00615 {
00616     switch (lua_type(L, src)) {
00617         case LUA_TNUMBER:
00618         case LUA_TSTRING:
00619             dst += lua_tostring(L, src);
00620             break;
00621         case LUA_TUSERDATA:
00622             // Compare its metatable with t_string's metatable.
00623             if (!lua_getmetatable(L, src) || !lua_rawequal(L, -1, -2))
00624                 luaL_typerror(L, src, "string");
00625             dst += *static_cast<t_string *>(lua_touserdata(L, src));
00626             lua_pop(L, 1);
00627             break;
00628         default:
00629             luaL_typerror(L, src, "string");
00630     }
00631 }
00632 
00633 /**
00634  * Appends a scalar to a t_string object (__concat metamethod).
00635  */
00636 static int impl_tstring_concat(lua_State *L)
00637 {
00638     // Create a new t_string.
00639     t_string *t = new(lua_newuserdata(L, sizeof(t_string))) t_string;
00640 
00641     lua_pushlightuserdata(L
00642             , static_cast<void *>(const_cast<char *>(&tstringKey)));
00643 
00644     lua_rawget(L, LUA_REGISTRYINDEX);
00645 
00646     // Append both arguments to t.
00647     tstring_concat_aux(L, *t, 1);
00648     tstring_concat_aux(L, *t, 2);
00649 
00650     lua_setmetatable(L, -2);
00651     return 1;
00652 }
00653 
00654 /**
00655  * Destroys a t_string object before it is collected (__gc metamethod).
00656  */
00657 static int impl_tstring_collect(lua_State *L)
00658 {
00659     t_string *t = static_cast<t_string *>(lua_touserdata(L, 1));
00660     t->t_string::~t_string();
00661     return 0;
00662 }
00663 
00664 /**
00665  * Converts a t_string object to a string (__tostring metamethod);
00666  * that is, performs a translation.
00667  */
00668 static int impl_tstring_tostring(lua_State *L)
00669 {
00670     t_string *t = static_cast<t_string *>(lua_touserdata(L, 1));
00671     lua_pushstring(L, t->c_str());
00672     return 1;
00673 }
00674 
00675 /**
00676  * Gets the parsed field of a vconfig object (_index metamethod).
00677  * Special fields __literal, __shallow_literal, __parsed, and
00678  * __shallow_parsed, return Lua tables.
00679  */
00680 static int impl_vconfig_get(lua_State *L)
00681 {
00682     vconfig *v = static_cast<vconfig *>(lua_touserdata(L, 1));
00683 
00684     if (lua_isnumber(L, 2))
00685     {
00686         vconfig::all_children_iterator i = v->ordered_begin();
00687         unsigned len = std::distance(i, v->ordered_end());
00688         unsigned pos = lua_tointeger(L, 2) - 1;
00689         if (pos >= len) return 0;
00690         std::advance(i, pos);
00691         lua_createtable(L, 2, 0);
00692         lua_pushstring(L, i.get_key().c_str());
00693         lua_rawseti(L, -2, 1);
00694         new(lua_newuserdata(L, sizeof(vconfig))) vconfig(i.get_child());
00695         lua_pushlightuserdata(L
00696                 , static_cast<void *>(const_cast<char *>(&vconfigKey)));
00697 
00698         lua_rawget(L, LUA_REGISTRYINDEX);
00699         lua_setmetatable(L, -2);
00700         lua_rawseti(L, -2, 2);
00701         return 1;
00702     }
00703 
00704     char const *m = luaL_checkstring(L, 2);
00705     if (strcmp(m, "__literal") == 0) {
00706         luaW_pushconfig(L, v->get_config());
00707         return 1;
00708     }
00709     if (strcmp(m, "__parsed") == 0) {
00710         luaW_pushconfig(L, v->get_parsed_config());
00711         return 1;
00712     }
00713 
00714     bool shallow_literal = strcmp(m, "__shallow_literal") == 0;
00715     if (shallow_literal || strcmp(m, "__shallow_parsed") == 0)
00716     {
00717         lua_newtable(L);
00718         foreach (const config::attribute &a, v->get_config().attribute_range()) {
00719             if (shallow_literal)
00720                 luaW_pushscalar(L, a.second);
00721             else
00722                 luaW_pushscalar(L, v->expand(a.first));
00723             lua_setfield(L, -2, a.first.c_str());
00724         }
00725         vconfig::all_children_iterator i = v->ordered_begin(),
00726             i_end = v->ordered_end();
00727         if (shallow_literal) {
00728             i.disable_insertion();
00729             i_end.disable_insertion();
00730         }
00731         for (int j = 1; i != i_end; ++i, ++j)
00732         {
00733             lua_createtable(L, 2, 0);
00734             lua_pushstring(L, i.get_key().c_str());
00735             lua_rawseti(L, -2, 1);
00736             luaW_pushvconfig(L, i.get_child());
00737             lua_rawseti(L, -2, 2);
00738             lua_rawseti(L, -2, j);
00739         }
00740         return 1;
00741     }
00742 
00743     if (v->null() || !v->has_attribute(m)) return 0;
00744     luaW_pushscalar(L, (*v)[m]);
00745     return 1;
00746 }
00747 
00748 /**
00749  * Returns the number of a child of a vconfig object.
00750  */
00751 static int impl_vconfig_size(lua_State *L)
00752 {
00753     vconfig *v = static_cast<vconfig *>(lua_touserdata(L, 1));
00754     lua_pushinteger(L, v->null() ? 0 :
00755         std::distance(v->ordered_begin(), v->ordered_end()));
00756     return 1;
00757 }
00758 
00759 /**
00760  * Destroys a vconfig object before it is collected (__gc metamethod).
00761  */
00762 static int impl_vconfig_collect(lua_State *L)
00763 {
00764     vconfig *v = static_cast<vconfig *>(lua_touserdata(L, 1));
00765     v->vconfig::~vconfig();
00766     return 0;
00767 }
00768 
00769 #define return_tstring_attrib(name, accessor) \
00770     if (strcmp(m, name) == 0) { \
00771         luaW_pushtstring(L, accessor); \
00772         return 1; \
00773     }
00774 
00775 #define return_cstring_attrib(name, accessor) \
00776     if (strcmp(m, name) == 0) { \
00777         lua_pushstring(L, accessor); \
00778         return 1; \
00779     }
00780 
00781 #define return_string_attrib(name, accessor) \
00782     return_cstring_attrib(name, accessor.c_str())
00783 
00784 #define return_int_attrib(name, accessor) \
00785     if (strcmp(m, name) == 0) { \
00786         lua_pushinteger(L, accessor); \
00787         return 1; \
00788     }
00789 
00790 #define return_float_attrib(name, accessor) \
00791     if (strcmp(m, name) == 0) { \
00792         lua_pushnumber(L, accessor); \
00793         return 1; \
00794     }
00795 
00796 #define return_bool_attrib(name, accessor) \
00797     if (strcmp(m, name) == 0) { \
00798         lua_pushboolean(L, accessor); \
00799         return 1; \
00800     }
00801 
00802 #define return_cfg_attrib(name, accessor) \
00803     if (strcmp(m, name) == 0) { \
00804         config cfg; \
00805         accessor; \
00806         luaW_pushconfig(L, cfg); \
00807         return 1; \
00808     }
00809 
00810 #define return_cfgref_attrib(name, accessor) \
00811     if (strcmp(m, name) == 0) { \
00812         luaW_pushconfig(L, accessor); \
00813         return 1; \
00814     }
00815 
00816 #define return_vector_string_attrib(name, accessor) \
00817     if (strcmp(m, name) == 0) { \
00818         const std::vector<std::string>& vector = accessor; \
00819         lua_createtable(L, vector.size(), 0); \
00820         int i = 1; \
00821         foreach (const std::string& s, vector) { \
00822             lua_pushstring(L, s.c_str()); \
00823             lua_rawseti(L, -2, i); \
00824             ++i; \
00825         } \
00826         return 1; \
00827     }
00828 
00829 #define modify_tstring_attrib(name, accessor) \
00830     if (strcmp(m, name) == 0) { \
00831         t_string value = luaW_checktstring(L, 3); \
00832         accessor; \
00833         return 0; \
00834     }
00835 
00836 #define modify_string_attrib(name, accessor) \
00837     if (strcmp(m, name) == 0) { \
00838         const char *value = luaL_checkstring(L, 3); \
00839         accessor; \
00840         return 0; \
00841     }
00842 
00843 #define modify_int_attrib(name, accessor) \
00844     if (strcmp(m, name) == 0) { \
00845         int value = luaL_checkinteger(L, 3); \
00846         accessor; \
00847         return 0; \
00848     }
00849 
00850 #define modify_int_attrib_check_range(name, accessor, allowed_min, allowed_max) \
00851     if (strcmp(m, name) == 0) { \
00852         int value = luaL_checkinteger(L, 3); \
00853         if (value < allowed_min || allowed_max < value) return luaL_argerror(L, 3, "out of bounds"); \
00854         accessor; \
00855         return 0; \
00856     }
00857 
00858 #define modify_bool_attrib(name, accessor) \
00859     if (strcmp(m, name) == 0) { \
00860         bool value = lua_toboolean(L, 3); \
00861         accessor; \
00862         return 0; \
00863     }
00864 
00865 #define modify_vector_string_attrib(name, accessor) \
00866     if (strcmp(m, name) == 0) { \
00867         std::vector<std::string> vector; \
00868         char const* message = "table with unnamed indices holding strings expected"; \
00869         if (!lua_istable(L, 3)) return luaL_argerror(L, 3, message); \
00870         unsigned length = lua_rawlen(L, 3); \
00871         for (unsigned i = 1; i <= length; ++i) { \
00872             lua_rawgeti(L, 3, i); \
00873             char const* string = lua_tostring(L, 4); \
00874             if(!string) return luaL_argerror(L, 2 + i, message); \
00875             vector.push_back(string); \
00876             lua_pop(L, 1); \
00877         } \
00878         accessor; \
00879         return 0; \
00880     }
00881 
00882 /**
00883  * Gets some data on a unit type (__index metamethod).
00884  * - Arg 1: table containing an "id" field.
00885  * - Arg 2: string containing the name of the property.
00886  * - Ret 1: something containing the attribute.
00887  */
00888 static int impl_unit_type_get(lua_State *L)
00889 {
00890     char const *m = luaL_checkstring(L, 2);
00891     lua_pushstring(L, "id");
00892     lua_rawget(L, 1);
00893     const unit_type *utp = unit_types.find(lua_tostring(L, -1));
00894     if (!utp) return luaL_argerror(L, 1, "unknown unit type");
00895     unit_type const &ut = *utp;
00896 
00897     // Find the corresponding attribute.
00898     return_tstring_attrib("name", ut.type_name());
00899     return_int_attrib("max_hitpoints", ut.hitpoints());
00900     return_int_attrib("max_moves", ut.movement());
00901     return_int_attrib("max_experience", ut.experience_needed());
00902     return_int_attrib("cost", ut.cost());
00903     return_int_attrib("level", ut.level());
00904     return_cfgref_attrib("__cfg", ut.get_cfg());
00905     return 0;
00906 }
00907 
00908 /**
00909  * Gets some data on a race (__index metamethod).
00910  * - Arg 1: table containing an "id" field.
00911  * - Arg 2: string containing the name of the property.
00912  * - Ret 1: something containing the attribute.
00913  */
00914 static int impl_race_get(lua_State* L)
00915 {
00916     char const* m = luaL_checkstring(L, 2);
00917     lua_pushstring(L, "id");
00918     lua_rawget(L, 1);
00919     const unit_race* raceptr = unit_types.find_race(lua_tostring(L, -1));
00920     if(!raceptr) return luaL_argerror(L, 1, "unknown race");
00921     unit_race const &race = *raceptr;
00922 
00923     return_tstring_attrib("description", race.description());
00924     return_tstring_attrib("name", race.name());
00925     return_int_attrib("num_traits", race.num_traits());
00926     return_tstring_attrib("plural_name", race.plural_name());
00927     return_bool_attrib("ignore_global_traits", !race.uses_global_traits());
00928     return_string_attrib("undead_variation", race.undead_variation());
00929     return_cfgref_attrib("__cfg", race.get_cfg());
00930 
00931     return 0;
00932 }
00933 
00934 /**
00935  * Destroys a unit object before it is collected (__gc metamethod).
00936  */
00937 static int impl_unit_collect(lua_State *L)
00938 {
00939     lua_unit *u = static_cast<lua_unit *>(lua_touserdata(L, 1));
00940     u->lua_unit::~lua_unit();
00941     return 0;
00942 }
00943 
00944 /**
00945  * Gets some data on a unit (__index metamethod).
00946  * - Arg 1: full userdata containing the unit id.
00947  * - Arg 2: string containing the name of the property.
00948  * - Ret 1: something containing the attribute.
00949  */
00950 static int impl_unit_get(lua_State *L)
00951 {
00952     lua_unit *lu = static_cast<lua_unit *>(lua_touserdata(L, 1));
00953     char const *m = luaL_checkstring(L, 2);
00954     unit const *pu = lu->get();
00955 
00956     if (strcmp(m, "valid") == 0)
00957     {
00958         if (!pu) return 0;
00959         if (lu->on_map())
00960             lua_pushstring(L, "map");
00961         else if (lu->on_recall_list())
00962             lua_pushstring(L, "recall");
00963         else
00964             lua_pushstring(L, "private");
00965         return 1;
00966     }
00967 
00968     if (!pu) return luaL_argerror(L, 1, "unknown unit");
00969     unit const &u = *pu;
00970 
00971     // Find the corresponding attribute.
00972     return_int_attrib("x", u.get_location().x + 1);
00973     return_int_attrib("y", u.get_location().y + 1);
00974     if (strcmp(m, "loc") == 0) {
00975         lua_pushinteger(L, u.get_location().x + 1);
00976         lua_pushinteger(L, u.get_location().y + 1);
00977         return 2;
00978     }
00979     return_int_attrib("side", u.side());
00980     return_string_attrib("id", u.id());
00981     return_string_attrib("type", u.type_id());
00982     return_string_attrib("image_mods", u.effect_image_mods());
00983     return_int_attrib("hitpoints", u.hitpoints());
00984     return_int_attrib("max_hitpoints", u.max_hitpoints());
00985     return_int_attrib("experience", u.experience());
00986     return_int_attrib("max_experience", u.max_experience());
00987     return_int_attrib("moves", u.movement_left());
00988     return_int_attrib("max_moves", u.total_movement());
00989     return_int_attrib("max_attacks", u.max_attacks());
00990     return_int_attrib("attacks_left", u.attacks_left());
00991     return_tstring_attrib("name", u.name());
00992     return_bool_attrib("canrecruit", u.can_recruit());
00993 
00994     return_vector_string_attrib("extra_recruit", u.recruits());
00995     return_vector_string_attrib("advances_to", u.advances_to());
00996 
00997     if (strcmp(m, "status") == 0) {
00998         lua_createtable(L, 1, 0);
00999         lua_pushvalue(L, 1);
01000         lua_rawseti(L, -2, 1);
01001         lua_pushlightuserdata(L
01002                 , static_cast<void *>(const_cast<char *>(&ustatusKey)));
01003         lua_rawget(L, LUA_REGISTRYINDEX);
01004         lua_setmetatable(L, -2);
01005         return 1;
01006     }
01007     if (strcmp(m, "variables") == 0) {
01008         lua_createtable(L, 1, 0);
01009         lua_pushvalue(L, 1);
01010         lua_rawseti(L, -2, 1);
01011         lua_pushlightuserdata(L
01012                 , static_cast<void *>(const_cast<char *>(&unitvarKey)));
01013         lua_rawget(L, LUA_REGISTRYINDEX);
01014         lua_setmetatable(L, -2);
01015         return 1;
01016     }
01017     return_bool_attrib("hidden", u.get_hidden());
01018     return_bool_attrib("petrified", u.incapacitated());
01019     return_bool_attrib("resting", u.resting());
01020     return_string_attrib("role", u.get_role());
01021     return_string_attrib("facing", map_location::write_direction(u.facing()));
01022     return_cfg_attrib("__cfg", u.write(cfg); u.get_location().write(cfg));
01023     return 0;
01024 }
01025 
01026 /**
01027  * Sets some data on a unit (__newindex metamethod).
01028  * - Arg 1: full userdata containing the unit id.
01029  * - Arg 2: string containing the name of the property.
01030  * - Arg 3: something containing the attribute.
01031  */
01032 static int impl_unit_set(lua_State *L)
01033 {
01034     lua_unit *lu = static_cast<lua_unit *>(lua_touserdata(L, 1));
01035     char const *m = luaL_checkstring(L, 2);
01036     unit *pu = lu->get();
01037     if (!pu) return luaL_argerror(L, 1, "unknown unit");
01038     unit &u = *pu;
01039 
01040     // Find the corresponding attribute.
01041     modify_int_attrib_check_range("side", u.set_side(value), 1, static_cast<int>(resources::teams->size()));
01042     modify_int_attrib("moves", u.set_movement(value));
01043     modify_int_attrib("hitpoints", u.set_hitpoints(value));
01044     modify_int_attrib("experience", u.set_experience(value));
01045     modify_int_attrib("attacks_left", u.set_attacks(value));
01046     modify_bool_attrib("resting", u.set_resting(value));
01047     modify_tstring_attrib("name", u.set_name(value));
01048     modify_string_attrib("role", u.set_role(value));
01049     modify_string_attrib("facing", u.set_facing(map_location::parse_direction(value)));
01050     modify_bool_attrib("hidden", u.set_hidden(value));
01051 
01052     modify_vector_string_attrib("extra_recruit", u.set_recruits(vector));
01053     modify_vector_string_attrib("advances_to", u.set_advances_to(vector));
01054 
01055     if (!lu->on_map()) {
01056         map_location loc = u.get_location();
01057         modify_int_attrib("x", loc.x = value - 1; u.set_location(loc));
01058         modify_int_attrib("y", loc.y = value - 1; u.set_location(loc));
01059     }
01060 
01061     return luaL_argerror(L, 2, "unknown modifiable property");
01062 }
01063 
01064 /**
01065  * Gets the status of a unit (__index metamethod).
01066  * - Arg 1: table containing the userdata containing the unit id.
01067  * - Arg 2: string containing the name of the status.
01068  * - Ret 1: boolean.
01069  */
01070 static int impl_unit_status_get(lua_State *L)
01071 {
01072     if (!lua_istable(L, 1))
01073         return luaL_typerror(L, 1, "unit status");
01074     lua_rawgeti(L, 1, 1);
01075     unit const *u = luaW_tounit(L, -1);
01076     if (!u) return luaL_argerror(L, 1, "unknown unit");
01077     char const *m = luaL_checkstring(L, 2);
01078     lua_pushboolean(L, u->get_state(m));
01079     return 1;
01080 }
01081 
01082 /**
01083  * Sets the status of a unit (__newindex metamethod).
01084  * - Arg 1: table containing the userdata containing the unit id.
01085  * - Arg 2: string containing the name of the status.
01086  * - Arg 3: boolean.
01087  */
01088 static int impl_unit_status_set(lua_State *L)
01089 {
01090     if (!lua_istable(L, 1))
01091         return luaL_typerror(L, 1, "unit status");
01092     lua_rawgeti(L, 1, 1);
01093     unit *u = luaW_tounit(L, -1);
01094     if (!u) return luaL_argerror(L, 1, "unknown unit");
01095     char const *m = luaL_checkstring(L, 2);
01096     u->set_state(m, lua_toboolean(L, 3));
01097     return 0;
01098 }
01099 
01100 /**
01101  * Gets the variable of a unit (__index metamethod).
01102  * - Arg 1: table containing the userdata containing the unit id.
01103  * - Arg 2: string containing the name of the status.
01104  * - Ret 1: boolean.
01105  */
01106 static int impl_unit_variables_get(lua_State *L)
01107 {
01108     if (!lua_istable(L, 1))
01109         return luaL_typerror(L, 1, "unit variables");
01110     lua_rawgeti(L, 1, 1);
01111     unit const *u = luaW_tounit(L, -1);
01112     if (!u) return luaL_argerror(L, 1, "unknown unit");
01113     char const *m = luaL_checkstring(L, 2);
01114     return_cfgref_attrib("__cfg", u->variables());
01115     luaW_pushscalar(L, u->variables()[m]);
01116     return 1;
01117 }
01118 
01119 /**
01120  * Sets the variable of a unit (__newindex metamethod).
01121  * - Arg 1: table containing the userdata containing the unit id.
01122  * - Arg 2: string containing the name of the status.
01123  * - Arg 3: scalar.
01124  */
01125 static int impl_unit_variables_set(lua_State *L)
01126 {
01127     if (!lua_istable(L, 1))
01128         return luaL_typerror(L, 1, "unit variables");
01129     lua_rawgeti(L, 1, 1);
01130     unit *u = luaW_tounit(L, -1);
01131     if (!u) return luaL_argerror(L, 1, "unknown unit");
01132     char const *m = luaL_checkstring(L, 2);
01133     if (strcmp(m, "__cfg") == 0) {
01134         u->variables() = luaW_checkconfig(L, 3);
01135         return 0;
01136     }
01137     config::attribute_value &v = u->variables()[m];
01138     switch (lua_type(L, 3)) {
01139         case LUA_TNIL:
01140             u->variables().remove_attribute(m);
01141             break;
01142         case LUA_TBOOLEAN:
01143             v = bool(lua_toboolean(L, 3));
01144             break;
01145         case LUA_TNUMBER:
01146             v = lua_tonumber(L, 3);
01147             break;
01148         case LUA_TSTRING:
01149             v = lua_tostring(L, 3);
01150             break;
01151         case LUA_TUSERDATA:
01152             if (luaW_hasmetatable(L, 3, tstringKey)) {
01153                 v = *static_cast<t_string *>(lua_touserdata(L, 3));
01154                 break;
01155             }
01156             // no break
01157         default:
01158             return luaL_typerror(L, 3, "WML scalar");
01159     }
01160     return 0;
01161 }
01162 
01163 /**
01164  * Gets the unit at the given location or with the given id.
01165  * - Arg 1: integer.
01166  * - Args 1, 2: location.
01167  * - Ret 1: full userdata with __index pointing to impl_unit_get and
01168  *          __newindex pointing to impl_unit_set.
01169  */
01170 static int intf_get_unit(lua_State *L)
01171 {
01172     int x = luaL_checkinteger(L, 1) - 1;
01173     int y = luaL_optint(L, 2, 0) - 1;
01174 
01175     unit_map &units = *resources::units;
01176     unit_map::const_iterator ui;
01177 
01178     if (lua_isnoneornil(L, 2)) {
01179         ui = units.find(x + 1);
01180     } else {
01181         ui = units.find(map_location(x, y));
01182     }
01183 
01184     if (!ui.valid()) return 0;
01185 
01186     new(lua_newuserdata(L, sizeof(lua_unit))) lua_unit(ui->underlying_id());
01187     lua_pushlightuserdata(L
01188             , static_cast<void *>(const_cast<char *>(&getunitKey)));
01189     lua_rawget(L, LUA_REGISTRYINDEX);
01190     lua_setmetatable(L, -2);
01191     return 1;
01192 }
01193 
01194 /**
01195  * Gets the unit displayed in the sidebar.
01196  * - Ret 1: full userdata with __index pointing to impl_unit_get and
01197  *          __newindex pointing to impl_unit_set.
01198  */
01199 static int intf_get_displayed_unit(lua_State *L)
01200 {
01201     unit_map::const_iterator ui = find_visible_unit(
01202         resources::screen->displayed_unit_hex(),
01203         (*resources::teams)[resources::screen->viewing_team()],
01204         resources::screen->show_everything());
01205     if (!ui.valid()) return 0;
01206 
01207     new(lua_newuserdata(L, sizeof(lua_unit))) lua_unit(ui->underlying_id());
01208     lua_pushlightuserdata(L
01209             , static_cast<void *>(const_cast<char *>(&getunitKey)));
01210     lua_rawget(L, LUA_REGISTRYINDEX);
01211     lua_setmetatable(L, -2);
01212     return 1;
01213 }
01214 
01215 /**
01216  * Gets all the units matching a given filter.
01217  * - Arg 1: optional table containing a filter
01218  * - Ret 1: table containing full userdata with __index pointing to
01219  *          impl_unit_get and __newindex pointing to impl_unit_set.
01220  */
01221 static int intf_get_units(lua_State *L)
01222 {
01223     vconfig filter = luaW_checkvconfig(L, 1, true);
01224 
01225     // Go through all the units while keeping the following stack:
01226     // 1: metatable, 2: return table, 3: userdata, 4: metatable copy
01227     lua_settop(L, 0);
01228     lua_pushlightuserdata(L
01229             , static_cast<void *>(const_cast<char *>(&getunitKey)));
01230     lua_rawget(L, LUA_REGISTRYINDEX);
01231     lua_newtable(L);
01232     int i = 1;
01233     unit_map &units = *resources::units;
01234     for (unit_map::const_unit_iterator ui = units.begin(), ui_end = units.end();
01235          ui != ui_end; ++ui)
01236     {
01237         if (!filter.null() && !ui->matches_filter(filter, ui->get_location()))
01238             continue;
01239         new(lua_newuserdata(L, sizeof(lua_unit))) lua_unit(ui->underlying_id());
01240         lua_pushvalue(L, 1);
01241         lua_setmetatable(L, 3);
01242         lua_rawseti(L, 2, i);
01243         ++i;
01244     }
01245     return 1;
01246 }
01247 
01248 /**
01249  * Matches a unit against the given filter.
01250  * - Arg 1: full userdata.
01251  * - Arg 2: table containing a filter
01252  * - Ret 1: boolean.
01253  */
01254 static int intf_match_unit(lua_State *L)
01255 {
01256     if (!luaW_hasmetatable(L, 1, getunitKey))
01257         return luaL_typerror(L, 1, "unit");
01258 
01259     lua_unit *lu = static_cast<lua_unit *>(lua_touserdata(L, 1));
01260     unit *u = lu->get();
01261     if (!u) return luaL_argerror(L, 1, "unit not found");
01262 
01263     vconfig filter = luaW_checkvconfig(L, 2, true);
01264 
01265     if (filter.null()) {
01266         lua_pushboolean(L, true);
01267         return 1;
01268     }
01269 
01270     if (int side = lu->on_recall_list()) {
01271         team &t = (*resources::teams)[side - 1];
01272         scoped_recall_unit auto_store("this_unit",
01273             t.save_id(), u - &t.recall_list()[0]);
01274         lua_pushboolean(L, u->matches_filter(filter, map_location()));
01275         return 1;
01276     }
01277 
01278     lua_pushboolean(L, u->matches_filter(filter, u->get_location()));
01279     return 1;
01280 }
01281 
01282 /**
01283  * Gets the numeric ids of all the units matching a given filter on the recall lists.
01284  * - Arg 1: optional table containing a filter
01285  * - Ret 1: table containing full userdata with __index pointing to
01286  *          impl_unit_get and __newindex pointing to impl_unit_set.
01287  */
01288 static int intf_get_recall_units(lua_State *L)
01289 {
01290     vconfig filter = luaW_checkvconfig(L, 1, true);
01291 
01292     // Go through all the units while keeping the following stack:
01293     // 1: metatable, 2: return table, 3: userdata, 4: metatable copy
01294     lua_settop(L, 0);
01295     lua_pushlightuserdata(L
01296             , static_cast<void *>(const_cast<char *>(&getunitKey)));
01297     lua_rawget(L, LUA_REGISTRYINDEX);
01298     lua_newtable(L);
01299     int i = 1, s = 1;
01300     foreach (team &t, *resources::teams)
01301     {
01302         foreach (unit &u, t.recall_list())
01303         {
01304             if (!filter.null()) {
01305                 scoped_recall_unit auto_store("this_unit",
01306                     t.save_id(), &u - &t.recall_list()[0]);
01307                 if (!u.matches_filter(filter, map_location()))
01308                     continue;
01309             }
01310             new(lua_newuserdata(L, sizeof(lua_unit))) lua_unit(s, u.underlying_id());
01311             lua_pushvalue(L, 1);
01312             lua_setmetatable(L, 3);
01313             lua_rawseti(L, 2, i);
01314             ++i;
01315         }
01316         ++s;
01317     }
01318     return 1;
01319 }
01320 
01321 /**
01322  * Fires an event.
01323  * - Arg 1: string containing the event name.
01324  * - Arg 2,3: optional first location.
01325  * - Arg 4,5: optional second location.
01326  * - Arg 6: optional WML table used as the [weapon] tag.
01327  * - Arg 7: optional WML table used as the [second_weapon] tag.
01328  * - Ret 1: boolean indicating whether the event was processed or not.
01329  */
01330 static int intf_fire_event(lua_State *L)
01331 {
01332     char const *m = luaL_checkstring(L, 1);
01333 
01334     int pos = 2;
01335     map_location l1, l2;
01336     config data;
01337 
01338     if (lua_isnumber(L, 2)) {
01339         l1.x = lua_tointeger(L, 2) - 1;
01340         l1.y = luaL_checkinteger(L, 3) - 1;
01341         if (lua_isnumber(L, 4)) {
01342             l2.x = lua_tointeger(L, 4) - 1;
01343             l2.y = luaL_checkinteger(L, 5) - 1;
01344             pos = 6;
01345         } else pos = 4;
01346     }
01347 
01348     if (!lua_isnoneornil(L, pos)) {
01349         data.add_child("first", luaW_checkconfig(L, pos));
01350     }
01351     ++pos;
01352     if (!lua_isnoneornil(L, pos)) {
01353         data.add_child("second", luaW_checkconfig(L, pos));
01354     }
01355 
01356     bool b = game_events::fire(m, l1, l2, data);
01357     lua_pushboolean(L, b);
01358     return 1;
01359 }
01360 
01361 /**
01362  * Gets a WML variable.
01363  * - Arg 1: string containing the variable name.
01364  * - Arg 2: optional bool indicating if tables for containers should be left empty.
01365  * - Ret 1: value of the variable, if any.
01366  */
01367 static int intf_get_variable(lua_State *L)
01368 {
01369     char const *m = luaL_checkstring(L, 1);
01370     variable_info v(m, false, variable_info::TYPE_SCALAR);
01371     if (v.is_valid) {
01372         luaW_pushscalar(L, v.as_scalar());
01373         return 1;
01374     } else {
01375         variable_info w(m, false, variable_info::TYPE_CONTAINER);
01376         if (w.is_valid) {
01377             lua_newtable(L);
01378             if (lua_toboolean(L, 2))
01379                 luaW_filltable(L, w.as_container());
01380             return 1;
01381         }
01382     }
01383     return 0;
01384 }
01385 
01386 /**
01387  * Sets a WML variable.
01388  * - Arg 1: string containing the variable name.
01389  * - Arg 2: boolean/integer/string/table containing the value.
01390  */
01391 static int intf_set_variable(lua_State *L)
01392 {
01393     const std::string& m = luaL_checkstring(L, 1);
01394     if(m.empty()) return luaL_argerror(L, 1, "empty variable name");
01395     if (lua_isnoneornil(L, 2)) {
01396         resources::state_of_game->clear_variable(m);
01397         return 0;
01398     }
01399 
01400     variable_info v(m);
01401     switch (lua_type(L, 2)) {
01402         case LUA_TBOOLEAN:
01403             v.as_scalar() = bool(lua_toboolean(L, 2));
01404             break;
01405         case LUA_TNUMBER:
01406             v.as_scalar() = lua_tonumber(L, 2);
01407             break;
01408         case LUA_TSTRING:
01409             v.as_scalar() = lua_tostring(L, 2);
01410             break;
01411         case LUA_TUSERDATA:
01412             if (luaW_hasmetatable(L, 2, tstringKey)) {
01413                 v.as_scalar() = *static_cast<t_string *>(lua_touserdata(L, 2));
01414                 break;
01415             }
01416             // no break
01417         case LUA_TTABLE:
01418         {
01419             config &cfg = v.as_container();
01420             cfg.clear();
01421             if (luaW_toconfig(L, 2, cfg))
01422                 break;
01423             // no break
01424         }
01425         default:
01426             return luaL_typerror(L, 2, "WML table or scalar");
01427     }
01428     return 0;
01429 }
01430 
01431 /**
01432  * Loads and executes a Lua file.
01433  * - Arg 1: string containing the file name.
01434  * - Ret *: values returned by executing the file body.
01435  */
01436 static int intf_dofile(lua_State *L)
01437 {
01438     char const *m = luaL_checkstring(L, 1);
01439     std::string p = get_wml_location(m);
01440     if (p.empty())
01441         return luaL_argerror(L, 1, "file not found");
01442 
01443     lua_settop(L, 0);
01444     if (luaL_loadfile(L, p.c_str()))
01445         return lua_error(L);
01446 
01447     lua_call(L, 0, LUA_MULTRET);
01448     return lua_gettop(L);
01449 }
01450 
01451 /**
01452  * Loads and executes a Lua file, if there is no corresponding entry in wesnoth.package.
01453  * Stores the result of the script in wesnoth.package and returns it.
01454  * - Arg 1: string containing the file name.
01455  * - Ret 1: value returned by the script.
01456  */
01457 static int intf_require(lua_State *L)
01458 {
01459     char const *m = luaL_checkstring(L, 1);
01460 
01461     // Check if there is already an entry.
01462     luaW_getglobal(L, "wesnoth", NULL);
01463     lua_pushstring(L, "package");
01464     lua_rawget(L, -2);
01465     lua_pushvalue(L, 1);
01466     lua_rawget(L, -2);
01467     if (!lua_isnil(L, -1)) return 1;
01468     lua_pop(L, 1);
01469 
01470     std::string p = get_wml_location(m);
01471     if (p.empty())
01472         return luaL_argerror(L, 1, "file not found");
01473 
01474     // Compile the file.
01475     int res = luaL_loadfile(L, p.c_str());
01476     if (res)
01477     {
01478         char const *m = lua_tostring(L, -1);
01479         chat_message("Lua error", m);
01480         ERR_LUA << m << '\n';
01481         return 0;
01482     }
01483 
01484     // Execute it.
01485     if (!luaW_pcall(L, 0, 1)) return 0;
01486 
01487     // Add the return value to the table.
01488     lua_pushvalue(L, 1);
01489     lua_pushvalue(L, -2);
01490     lua_settable(L, -4);
01491     return 1;
01492 }
01493 
01494 /**
01495  * Highlights the given location on the map.
01496  * - Args 1,2: location.
01497  */
01498 static int intf_highlight_hex(lua_State *L)
01499 {
01500     ERR_LUA << "wesnoth.highlight_hex is deprecated, use wesnoth.select_hex\n";
01501 
01502     int x = luaL_checkinteger(L, 1) - 1;
01503     int y = luaL_checkinteger(L, 2) - 1;
01504     const map_location loc(x, y);
01505     resources::screen->highlight_hex(loc);
01506     resources::screen->display_unit_hex(loc);
01507 
01508     unit_map::const_unit_iterator i = resources::units->find(loc);
01509     if(i != resources::units->end()) {
01510         resources::screen->highlight_reach(pathfind::paths(
01511             *resources::game_map, *resources::units, *i, *resources::teams, false,
01512             (*i).get_ability_bool("teleport"), resources::teams->front()));
01513     }
01514 
01515     return 0;
01516 }
01517 
01518 /**
01519  * Returns whether the first side is an enemy of the second one.
01520  * - Args 1,2: side numbers.
01521  * - Ret 1: boolean.
01522  */
01523 static int intf_is_enemy(lua_State *L)
01524 {
01525     unsigned side_1 = luaL_checkint(L, 1) - 1;
01526     unsigned side_2 = luaL_checkint(L, 2) - 1;
01527     std::vector<team> &teams = *resources::teams;
01528     if (side_1 >= teams.size() || side_2 >= teams.size()) return 0;
01529     lua_pushboolean(L, teams[side_1].is_enemy(side_2 + 1));
01530     return 1;
01531 }
01532 
01533 /**
01534  * Gets some data on a side (__index metamethod).
01535  * - Arg 1: full userdata containing the team.
01536  * - Arg 2: string containing the name of the property.
01537  * - Ret 1: something containing the attribute.
01538  */
01539 static int impl_side_get(lua_State *L)
01540 {
01541     // Hidden metamethod, so arg1 has to be a pointer to a team.
01542     team &t = **static_cast<team **>(lua_touserdata(L, 1));
01543     char const *m = luaL_checkstring(L, 2);
01544 
01545     // Find the corresponding attribute.
01546     return_int_attrib("side", t.side());
01547     return_int_attrib("gold", t.gold());
01548     return_tstring_attrib("objectives", t.objectives());
01549     return_int_attrib("village_gold", t.village_gold());
01550     return_int_attrib("recall_cost", t.recall_cost());
01551     return_int_attrib("base_income", t.base_income());
01552     return_int_attrib("total_income", t.total_income());
01553     return_bool_attrib("objectives_changed", t.objectives_changed());
01554     return_bool_attrib("fog", t.uses_fog());
01555     return_bool_attrib("shroud", t.uses_shroud());
01556     return_bool_attrib("hidden", t.hidden());
01557     return_tstring_attrib("user_team_name", t.user_team_name());
01558     return_string_attrib("team_name", t.team_name());
01559     return_string_attrib("name", t.name());
01560     return_string_attrib("color", t.color());
01561     return_cstring_attrib("controller", t.controller_string());
01562 
01563     if (strcmp(m, "recruit") == 0) {
01564         std::set<std::string> const &recruits = t.recruits();
01565         lua_createtable(L, recruits.size(), 0);
01566         int i = 1;
01567         foreach (std::string const &r, t.recruits()) {
01568             lua_pushstring(L, r.c_str());
01569             lua_rawseti(L, -2, i++);
01570         }
01571         return 1;
01572     }
01573 
01574     return_cfg_attrib("__cfg", t.write(cfg));
01575     return 0;
01576 }
01577 
01578 /**
01579  * Sets some data on a side (__newindex metamethod).
01580  * - Arg 1: full userdata containing the team.
01581  * - Arg 2: string containing the name of the property.
01582  * - Arg 3: something containing the attribute.
01583  */
01584 static int impl_side_set(lua_State *L)
01585 {
01586     // Hidden metamethod, so arg1 has to be a pointer to a team.
01587     team &t = **static_cast<team **>(lua_touserdata(L, 1));
01588     char const *m = luaL_checkstring(L, 2);
01589 
01590     // Find the corresponding attribute.
01591     modify_int_attrib("gold", t.set_gold(value));
01592     modify_tstring_attrib("objectives", t.set_objectives(value, true));
01593     modify_int_attrib("village_gold", t.set_village_gold(value));
01594     modify_int_attrib("recall_cost", t.set_recall_cost(value));
01595     modify_int_attrib("base_income", t.set_base_income(value));
01596     modify_bool_attrib("objectives_changed", t.set_objectives_changed(value));
01597     modify_tstring_attrib("user_team_name", t.change_team(t.team_name(), value));
01598     modify_string_attrib("team_name", t.change_team(value, t.user_team_name()));
01599     modify_string_attrib("controller", t.change_controller(value));
01600     modify_string_attrib("color", t.set_color(value));
01601 
01602     if (strcmp(m, "recruit") == 0) {
01603         t.set_recruits(std::set<std::string>());
01604         if (!lua_istable(L, 3)) return 0;
01605         for (int i = 1;; ++i) {
01606             lua_rawgeti(L, 3, i);
01607             if (lua_isnil(L, -1)) break;
01608             t.add_recruit(lua_tostring(L, -1));
01609             lua_pop(L, 1);
01610         }
01611         return 0;
01612     }
01613 
01614     return luaL_argerror(L, 2, "unknown modifiable property");
01615 }
01616 
01617 /**
01618  * Gets a terrain code.
01619  * - Args 1,2: map location.
01620  * - Ret 1: string.
01621  */
01622 static int intf_get_terrain(lua_State *L)
01623 {
01624     int x = luaL_checkint(L, 1);
01625     int y = luaL_checkint(L, 2);
01626 
01627     t_translation::t_terrain const &t = resources::game_map->
01628         get_terrain(map_location(x - 1, y - 1));
01629     lua_pushstring(L, t_translation::write_terrain_code(t).c_str());
01630     return 1;
01631 }
01632 
01633 /**
01634  * Sets a terrain code.
01635  * - Args 1,2: map location.
01636  * - Arg 3: terrain code string.
01637  * - Arg 4: layer: (overlay|base|both, default=both)
01638  * - Arg 5: replace_if_failed, default = no
01639  */
01640 static int intf_set_terrain(lua_State *L)
01641 {
01642     int x = luaL_checkint(L, 1);
01643     int y = luaL_checkint(L, 2);
01644     t_translation::t_terrain terrain = t_translation::read_terrain_code(luaL_checkstring(L, 3));
01645     if (terrain == t_translation::NONE_TERRAIN) return 0;
01646 
01647     gamemap::tmerge_mode mode = gamemap::BOTH;
01648     bool replace_if_failed = false;
01649     if (!lua_isnone(L, 4)) {
01650         if (!lua_isnil(L, 4)) {
01651             const char* const layer = luaL_checkstring(L, 4);
01652             if (strcmp(layer, "base") == 0) mode = gamemap::BASE;
01653             else if (strcmp(layer, "overlay") == 0) mode = gamemap::OVERLAY;
01654         }
01655 
01656         if(!lua_isnoneornil(L, 5)) {
01657             replace_if_failed = lua_toboolean(L, 5);
01658         }
01659     }
01660 
01661     change_terrain(map_location(x - 1, y - 1), terrain, mode, replace_if_failed);
01662     return 0;
01663 }
01664 
01665 /**
01666  * Gets details about a terrain.
01667  * - Arg 1: terrain code string.
01668  * - Ret 1: table.
01669  */
01670 static int intf_get_terrain_info(lua_State *L)
01671 {
01672     char const *m = luaL_checkstring(L, 1);
01673     t_translation::t_terrain t = t_translation::read_terrain_code(m);
01674     if (t == t_translation::NONE_TERRAIN) return 0;
01675     terrain_type const &info = resources::game_map->get_terrain_info(t);
01676 
01677     lua_newtable(L);
01678     lua_pushstring(L, info.id().c_str());
01679     lua_setfield(L, -2, "id");
01680     luaW_pushtstring(L, info.name());
01681     lua_setfield(L, -2, "name");
01682     luaW_pushtstring(L, info.editor_name());
01683     lua_setfield(L, -2, "editor_name");
01684     luaW_pushtstring(L, info.description());
01685     lua_setfield(L, -2, "description");
01686     lua_pushboolean(L, info.is_village());
01687     lua_setfield(L, -2, "village");
01688     lua_pushboolean(L, info.is_castle());
01689     lua_setfield(L, -2, "castle");
01690     lua_pushboolean(L, info.is_keep());
01691     lua_setfield(L, -2, "keep");
01692     lua_pushinteger(L, info.gives_healing());
01693     lua_setfield(L, -2, "healing");
01694 
01695     return 1;
01696 }
01697 
01698 /**
01699  * Gets time of day information.
01700  * - Arg 1: optional turn number
01701  * - Arg 2: optional table
01702  * - Ret 1: table.
01703  */
01704 static int intf_get_time_of_day(lua_State *L)
01705 {
01706     unsigned arg = 1;
01707 
01708     int for_turn = resources::tod_manager->turn();
01709     map_location loc = map_location();
01710     bool consider_illuminates = false;
01711 
01712     if(lua_isnumber(L, arg)) {
01713         ++arg;
01714         for_turn = luaL_checkint(L, 1);
01715         if(for_turn < 1 || for_turn > resources::tod_manager->number_of_turns()) {
01716             return luaL_argerror(L, 1, "turn number out of range");
01717         }
01718     }
01719     else if(lua_isnil(L, arg)) ++arg;
01720 
01721     if(lua_istable(L, arg)) {
01722         lua_rawgeti(L, arg, 1);
01723         lua_rawgeti(L, arg, 2);
01724         loc = map_location(luaL_checkinteger(L, -2) - 1, luaL_checkinteger(L, -1) - 1);
01725         if(!resources::game_map->on_board(loc)) return luaL_argerror(L, arg, "coordinates are not on board");
01726         lua_pop(L, 2);
01727 
01728         lua_rawgeti(L, arg, 3);
01729         consider_illuminates = lua_toboolean(L, -1);
01730         lua_pop(L, 1);
01731     }
01732 
01733     const time_of_day& tod = consider_illuminates ?
01734         resources::tod_manager->get_illuminated_time_of_day(loc, for_turn) :
01735         resources::tod_manager->get_time_of_day(loc, for_turn);
01736 
01737     lua_newtable(L);
01738     lua_pushstring(L, tod.id.c_str());
01739     lua_setfield(L, -2, "id");
01740     lua_pushinteger(L, tod.lawful_bonus);
01741     lua_setfield(L, -2, "lawful_bonus");
01742     lua_pushinteger(L, tod.bonus_modified);
01743     lua_setfield(L, -2, "bonus_modified");
01744     lua_pushstring(L, tod.image.c_str());
01745     lua_setfield(L, -2, "image");
01746     luaW_pushtstring(L, tod.name);
01747     lua_setfield(L, -2, "name");
01748 
01749     lua_pushinteger(L, tod.color.r);
01750     lua_setfield(L, -2, "red");
01751     lua_pushinteger(L, tod.color.g);
01752     lua_setfield(L, -2, "green");
01753     lua_pushinteger(L, tod.color.b);
01754     lua_setfield(L, -2, "blue");
01755 
01756     return 1;
01757 }
01758 
01759 /**
01760  * Gets the side of a village owner.
01761  * - Args 1,2: map location.
01762  * - Ret 1: integer.
01763  */
01764 static int intf_get_village_owner(lua_State *L)
01765 {
01766     int x = luaL_checkint(L, 1);
01767     int y = luaL_checkint(L, 2);
01768 
01769     map_location loc(x - 1, y - 1);
01770     if (!resources::game_map->is_village(loc))
01771         return 0;
01772 
01773     int side = village_owner(loc, *resources::teams) + 1;
01774     if (!side) return 0;
01775     lua_pushinteger(L, side);
01776     return 1;
01777 }
01778 
01779 /**
01780  * Sets the owner of a village.
01781  * - Args 1,2: map location.
01782  * - Arg 3: integer for the side or empty to remove ownership.
01783  */
01784 static int intf_set_village_owner(lua_State *L)
01785 {
01786     int x = luaL_checkint(L, 1);
01787     int y = luaL_checkint(L, 2);
01788     int new_side = lua_isnoneornil(L, 3) ? 0 : luaL_checkint(L, 3);
01789 
01790     std::vector<team> &teams = *resources::teams;
01791     map_location loc(x - 1, y - 1);
01792     if (!resources::game_map->is_village(loc))
01793         return 0;
01794 
01795     int old_side = village_owner(loc, teams) + 1;
01796     if (new_side == old_side
01797             || new_side < 0
01798             || new_side > static_cast<int>(teams.size())
01799             || (new_side && !resources::units->find_leader(new_side).valid()))
01800         return 0;
01801 
01802     if (old_side) teams[old_side - 1].lose_village(loc);
01803     if (new_side) teams[new_side - 1].get_village(loc, old_side, lua_toboolean(L, 4));
01804     return 0;
01805 }
01806 
01807 /**
01808  * Returns the map size.
01809  * - Ret 1: width.
01810  * - Ret 2: height.
01811  * - Ret 3: border size.
01812  */
01813 static int intf_get_map_size(lua_State *L)
01814 {
01815     const gamemap &map = *resources::game_map;
01816     lua_pushinteger(L, map.w());
01817     lua_pushinteger(L, map.h());
01818     lua_pushinteger(L, map.border_size());
01819     return 3;
01820 }
01821 
01822 /**
01823  * Returns the currently overed tile.
01824  * - Ret 1: x.
01825  * - Ret 2: y.
01826  */
01827 static int intf_get_mouseover_tile(lua_State *L)
01828 {
01829     const map_location &loc = resources::screen->mouseover_hex();
01830     if (!resources::game_map->on_board(loc)) return 0;
01831     lua_pushinteger(L, loc.x + 1);
01832     lua_pushinteger(L, loc.y + 1);
01833     return 2;
01834 }
01835 
01836 /**
01837  * Returns the currently selected tile.
01838  * - Ret 1: x.
01839  * - Ret 2: y.
01840  */
01841 static int intf_get_selected_tile(lua_State *L)
01842 {
01843     const map_location &loc = resources::screen->selected_hex();
01844     if (!resources::game_map->on_board(loc)) return 0;
01845     lua_pushinteger(L, loc.x + 1);
01846     lua_pushinteger(L, loc.y + 1);
01847     return 2;
01848 }
01849 
01850 /**
01851  * Returns the starting position of a side.
01852  * Arg 1: side number
01853  * Ret 1: table with unnamed indices holding wml coordinates x and y
01854 */
01855 static int intf_get_starting_location(lua_State* L)
01856 {
01857     const int side = luaL_checkint(L, 1);
01858     if(side < 1 || static_cast<int>(resources::teams->size()) < side)
01859         return luaL_argerror(L, 1, "out of bounds");
01860     const map_location& starting_pos = resources::game_map->starting_position(side);
01861     if(!resources::game_map->on_board(starting_pos)) return 0;
01862 
01863     lua_createtable(L, 2, 0);
01864     lua_pushinteger(L, starting_pos.x + 1);
01865     lua_rawseti(L, -2, 1);
01866     lua_pushinteger(L, starting_pos.y + 1);
01867     lua_rawseti(L, -2, 2);
01868 
01869     return 1;
01870 }
01871 
01872 /**
01873  * Gets some game_config data (__index metamethod).
01874  * - Arg 1: userdata (ignored).
01875  * - Arg 2: string containing the name of the property.
01876  * - Ret 1: something containing the attribute.
01877  */
01878 static int impl_game_config_get(lua_State *L)
01879 {
01880     char const *m = luaL_checkstring(L, 2);
01881 
01882     // Find the corresponding attribute.
01883     return_int_attrib("base_income", game_config::base_income);
01884     return_int_attrib("village_income", game_config::village_income);
01885     return_int_attrib("village_support", game_config::village_support);
01886     return_int_attrib("poison_amount", game_config::poison_amount);
01887     return_int_attrib("rest_heal_amount", game_config::rest_heal_amount);
01888     return_int_attrib("recall_cost", game_config::recall_cost);
01889     return_int_attrib("kill_experience", game_config::kill_experience);
01890     return_int_attrib("last_turn", resources::tod_manager->number_of_turns());
01891     return_string_attrib("version", game_config::version);
01892     return_bool_attrib("debug", game_config::debug);
01893     return_bool_attrib("mp_debug", game_config::mp_debug);
01894     return 0;
01895 }
01896 
01897 /**
01898  * Sets some game_config data (__newindex metamethod).
01899  * - Arg 1: userdata (ignored).
01900  * - Arg 2: string containing the name of the property.
01901  * - Arg 3: something containing the attribute.
01902  */
01903 static int impl_game_config_set(lua_State *L)
01904 {
01905     char const *m = luaL_checkstring(L, 2);
01906 
01907     // Find the corresponding attribute.
01908     modify_int_attrib("base_income", game_config::base_income = value);
01909     modify_int_attrib("village_income", game_config::village_income = value);
01910     modify_int_attrib("village_support", game_config::village_support = value);
01911     modify_int_attrib("poison_amount", game_config::poison_amount = value);
01912     modify_int_attrib("rest_heal_amount", game_config::rest_heal_amount = value);
01913     modify_int_attrib("recall_cost", game_config::recall_cost = value);
01914     modify_int_attrib("kill_experience", game_config::kill_experience = value);
01915     modify_int_attrib("last_turn", resources::tod_manager->set_number_of_turns(value));
01916     return luaL_argerror(L, 2, "unknown modifiable property");
01917 }
01918 
01919 /**
01920  * Gets some data about current point of game (__index metamethod).
01921  * - Arg 1: userdata (ignored).
01922  * - Arg 2: string containing the name of the property.
01923  * - Ret 1: something containing the attribute.
01924  */
01925 static int impl_current_get(lua_State *L)
01926 {
01927     char const *m = luaL_checkstring(L, 2);
01928 
01929     // Find the corresponding attribute.
01930     return_int_attrib("side", resources::controller->current_side());
01931     return_int_attrib("turn", resources::controller->turn());
01932 
01933     if (strcmp(m, "event_context") == 0)
01934     {
01935         const game_events::queued_event &ev = queued_event_context::get();
01936         config cfg;
01937         cfg["name"] = ev.name;
01938         if (const config &weapon = ev.data.child("first")) {
01939             cfg.add_child("weapon", weapon);
01940         }
01941         if (const config &weapon = ev.data.child("second")) {
01942             cfg.add_child("second_weapon", weapon);
01943         }
01944         if (ev.loc1.valid()) {
01945             cfg["x1"] = ev.loc1.x + 1;
01946             cfg["y1"] = ev.loc1.y + 1;
01947         }
01948         if (ev.loc2.valid()) {
01949             cfg["x2"] = ev.loc2.x + 1;
01950             cfg["y2"] = ev.loc2.y + 1;
01951         }
01952         luaW_pushconfig(L, cfg);
01953         return 1;
01954     }
01955 
01956     return 0;
01957 }
01958 
01959 /**
01960  * Displays a message in the chat window and in the logs.
01961  * - Arg 1: optional message header.
01962  * - Arg 2 (or 1): message.
01963  */
01964 static int intf_message(lua_State *L)
01965 {
01966     char const *m = luaL_checkstring(L, 1);
01967     char const *h = m;
01968     if (lua_isnone(L, 2)) {
01969         h = "Lua";
01970     } else {
01971         m = luaL_checkstring(L, 2);
01972     }
01973     chat_message(h, m);
01974     LOG_LUA << "Script says: \"" << m << "\"\n";
01975     return 0;
01976 }
01977 
01978 /**
01979  * Dumps a wml table or userdata wml object into a pretty string.
01980  * - Arg 1: wml table or vconfig userdata
01981  * - Ret 1: string
01982  */
01983 static int intf_debug(lua_State* L)
01984 {
01985     const config& arg = luaW_checkconfig(L, 1);
01986     const std::string& result = arg.debug();
01987     lua_pushstring(L, result.c_str());
01988     return 1;
01989 }
01990 
01991 /**
01992  * Removes all messages from the chat window.
01993  */
01994 static int intf_clear_messages(lua_State*)
01995 {
01996     resources::screen->clear_chat_messages();
01997     return 0;
01998 }
01999 
02000 /**
02001  * Evaluates a boolean WML conditional.
02002  * - Arg 1: WML table.
02003  * - Ret 1: boolean.
02004  */
02005 static int intf_eval_conditional(lua_State *L)
02006 {
02007     vconfig cond = luaW_checkvconfig(L, 1);
02008     bool b = game_events::conditional_passed(cond);
02009     lua_pushboolean(L, b);
02010     return 1;
02011 }
02012 
02013 /**
02014  * Cost function object relying on a Lua function.
02015  * @note The stack index of the Lua function must be valid each time the cost is computed.
02016  */
02017 struct lua_calculator : pathfind::cost_calculator
02018 {
02019     lua_State *L;
02020     int index;
02021 
02022     lua_calculator(lua_State *L_, int i): L(L_), index(i) {}
02023     double cost(const map_location &loc, const double so_far) const;
02024 };
02025 
02026 double lua_calculator::cost(const map_location &loc, const double so_far) const
02027 {
02028     // Copy the user function and push the location and current cost.
02029     lua_pushvalue(L, index);
02030     lua_pushinteger(L, loc.x + 1);
02031     lua_pushinteger(L, loc.y + 1);
02032     lua_pushnumber(L, so_far);
02033 
02034     // Execute the user function.
02035     if (!luaW_pcall(L, 3, 1)) return 1.;
02036 
02037     // Return a cost of at least 1 mp to avoid issues in pathfinder.
02038     // (Condition is inverted to detect NaNs.)
02039     double cost = lua_tonumber(L, -1);
02040     lua_pop(L, 1);
02041     return !(cost >= 1.) ? 1. : cost;
02042 }
02043 
02044 /**
02045  * Finds a path between two locations.
02046  * - Args 1,2: source location. (Or Arg 1: unit.)
02047  * - Args 3,4: destination.
02048  * - Arg 5: optional cost function or
02049  *          table (optional fields: ignore_units, ignore_teleport, max_cost, viewing_side).
02050  * - Ret 1: array of pairs containing path steps.
02051  * - Ret 2: path cost.
02052  */
02053 static int intf_find_path(lua_State *L)
02054 {
02055     int arg = 1;
02056     map_location src, dst;
02057     unit_map &units = *resources::units;
02058     const unit *u = NULL;
02059 
02060     if (lua_isuserdata(L, arg))
02061     {
02062         u = luaW_checkunit(L, 1);
02063         src = u->get_location();
02064         ++arg;
02065     }
02066     else
02067     {
02068         src.x = luaL_checkinteger(L, arg) - 1;
02069         ++arg;
02070         src.y = luaL_checkinteger(L, arg) - 1;
02071         unit_map::const_unit_iterator ui = units.find(src);
02072         if (ui.valid()) u = &*ui;
02073         ++arg;
02074     }
02075 
02076     dst.x = luaL_checkinteger(L, arg) - 1;
02077     ++arg;
02078     dst.y = luaL_checkinteger(L, arg) - 1;
02079     ++arg;
02080 
02081     if (!resources::game_map->on_board(src))
02082         return luaL_argerror(L, 1, "invalid location");
02083     if (!resources::game_map->on_board(dst))
02084         return luaL_argerror(L, arg - 2, "invalid location");
02085 
02086     std::vector<team> &teams = *resources::teams;
02087     gamemap &map = *resources::game_map;
02088     int viewing_side = 0;
02089     bool ignore_units = false, see_all = false, ignore_teleport = false;
02090     double stop_at = 10000;
02091     pathfind::cost_calculator *calc = NULL;
02092 
02093     if (lua_istable(L, arg))
02094     {
02095         lua_pushstring(L, "ignore_units");
02096         lua_rawget(L, arg);
02097         ignore_units = lua_toboolean(L, -1);
02098         lua_pop(L, 1);
02099 
02100         lua_pushstring(L, "ignore_teleport");
02101         lua_rawget(L, arg);
02102         ignore_teleport = lua_toboolean(L, -1);
02103         lua_pop(L, 1);
02104 
02105         lua_pushstring(L, "max_cost");
02106         lua_rawget(L, arg);
02107         if (!lua_isnil(L, -1))
02108             stop_at = luaL_checknumber(L, -1);
02109         lua_pop(L, 1);
02110 
02111         lua_pushstring(L, "viewing_side");
02112         lua_rawget(L, arg);
02113         if (!lua_isnil(L, -1)) {
02114             int i = luaL_checkinteger(L, -1);
02115             if (i >= 1 && i <= int(teams.size())) viewing_side = i;
02116             else see_all = true;
02117         }
02118         lua_pop(L, 1);
02119     }
02120     else if (lua_isfunction(L, arg))
02121     {
02122         calc = new lua_calculator(L, arg);
02123     }
02124 
02125     pathfind::teleport_map teleport_locations;
02126 
02127     if (!calc) {
02128         if (!u) return luaL_argerror(L, 1, "unit not found");
02129 
02130         team &viewing_team = teams[(viewing_side ? viewing_side : u->side()) - 1];
02131         if (!ignore_teleport) {
02132             teleport_locations = pathfind::get_teleport_locations(
02133                 *u, viewing_team, see_all, ignore_units);
02134         }
02135         calc = new pathfind::shortest_path_calculator(*u, viewing_team,
02136             units, teams, map, ignore_units, false, see_all);
02137     }
02138 
02139     pathfind::plain_route res = pathfind::a_star_search(src, dst, stop_at, calc, map.w(), map.h(),
02140         &teleport_locations);
02141     delete calc;
02142 
02143     int nb = res.steps.size();
02144     lua_createtable(L, nb, 0);
02145     for (int i = 0; i < nb; ++i)
02146     {
02147         lua_createtable(L, 2, 0);
02148         lua_pushinteger(L, res.steps[i].x + 1);
02149         lua_rawseti(L, -2, 1);
02150         lua_pushinteger(L, res.steps[i].y + 1);
02151         lua_rawseti(L, -2, 2);
02152         lua_rawseti(L, -2, i + 1);
02153     }
02154     lua_pushinteger(L, res.move_cost);
02155 
02156     return 2;
02157 }
02158 
02159 /**
02160  * Finds all the locations reachable by a unit.
02161  * - Args 1,2: source location. (Or Arg 1: unit.)
02162  * - Arg 3: optional table (optional fields: ignore_units, ignore_teleport, additional_turns, viewing_side).
02163  * - Ret 1: array of triples (coordinates + remaining movement).
02164  */
02165 static int intf_find_reach(lua_State *L)
02166 {
02167     int arg = 1;
02168     unit_map &units = *resources::units;
02169     const unit *u = NULL;
02170 
02171     if (lua_isuserdata(L, arg))
02172     {
02173         u = luaW_checkunit(L, 1);
02174         ++arg;
02175     }
02176     else
02177     {
02178         map_location src;
02179         src.x = luaL_checkinteger(L, arg) - 1;
02180         ++arg;
02181         src.y = luaL_checkinteger(L, arg) - 1;
02182         unit_map::const_unit_iterator ui = units.find(src);
02183         if (!ui.valid())
02184             return luaL_argerror(L, 1, "unit not found");
02185         u = &*ui;
02186         ++arg;
02187     }
02188 
02189     std::vector<team> &teams = *resources::teams;
02190     gamemap &map = *resources::game_map;
02191     int viewing_side = 0;
02192     bool ignore_units = false, see_all = false, ignore_teleport = false;
02193     int additional_turns = 0;
02194 
02195     if (lua_istable(L, arg))
02196     {
02197         lua_pushstring(L, "ignore_units");
02198         lua_rawget(L, arg);
02199         ignore_units = lua_toboolean(L, -1);
02200         lua_pop(L, 1);
02201 
02202         lua_pushstring(L, "ignore_teleport");
02203         lua_rawget(L, arg);
02204         ignore_teleport = lua_toboolean(L, -1);
02205         lua_pop(L, 1);
02206 
02207         lua_pushstring(L, "additional_turns");
02208         lua_rawget(L, arg);
02209         additional_turns = lua_tointeger(L, -1);
02210         lua_pop(L, 1);
02211 
02212         lua_pushstring(L, "viewing_side");
02213         lua_rawget(L, arg);
02214         if (!lua_isnil(L, -1)) {
02215             int i = luaL_checkinteger(L, -1);
02216             if (i >= 1 && i <= int(teams.size())) viewing_side = i;
02217             else see_all = true;
02218         }
02219         lua_pop(L, 1);
02220     }
02221 
02222     team &viewing_team = teams[(viewing_side ? viewing_side : u->side()) - 1];
02223     pathfind::paths res(map, units, *u, teams, ignore_units, !ignore_teleport,
02224         viewing_team, additional_turns, see_all, ignore_units);
02225 
02226     int nb = res.destinations.size();
02227     lua_createtable(L, nb, 0);
02228     for (int i = 0; i < nb; ++i)
02229     {
02230         pathfind::paths::step &s = res.destinations[i];
02231         lua_createtable(L, 2, 0);
02232         lua_pushinteger(L, s.curr.x + 1);
02233         lua_rawseti(L, -2, 1);
02234         lua_pushinteger(L, s.curr.y + 1);
02235         lua_rawseti(L, -2, 2);
02236         lua_pushinteger(L, s.move_left);
02237         lua_rawseti(L, -2, 3);
02238         lua_rawseti(L, -2, i + 1);
02239     }
02240 
02241     return 1;
02242 }
02243 
02244 /**
02245  * Places a unit on the map.
02246  * - Args 1,2: (optional) location.
02247  * - Arg 3: WML table describing a unit, or nothing/nil to delete.
02248  */
02249 static int intf_put_unit(lua_State *L)
02250 {
02251     int unit_arg = 1;
02252 
02253     lua_unit *lu = NULL;
02254     unit *u = NULL;
02255     map_location loc;
02256     if (lua_isnumber(L, 1)) {
02257         unit_arg = 3;
02258         loc.x = lua_tointeger(L, 1) - 1;
02259         loc.y = luaL_checkinteger(L, 2) - 1;
02260         if (!resources::game_map->on_board(loc))
02261             return luaL_argerror(L, 1, "invalid location");
02262     }
02263 
02264     if (luaW_hasmetatable(L, unit_arg, getunitKey))
02265     {
02266         lu = static_cast<lua_unit *>(lua_touserdata(L, unit_arg));
02267         u = lu->get();
02268         if (!u) return luaL_argerror(L, unit_arg, "unit not found");
02269         if (lu->on_map()) {
02270             if (unit_arg == 1 || u->get_location() == loc) return 0;
02271             resources::units->erase(loc);
02272             resources::units->move(u->get_location(), loc);
02273             return 0;
02274         } else if (int side = lu->on_recall_list()) {
02275             team &t = (*resources::teams)[side - 1];
02276             unit *v = new unit(*u);
02277             std::vector<unit> &rl = t.recall_list();
02278             rl.erase(rl.begin() + (u - &rl[0]));
02279             u = v;
02280         }
02281         if (unit_arg == 1) {
02282             loc = u->get_location();
02283             if (!resources::game_map->on_board(loc))
02284                 return luaL_argerror(L, 1, "invalid location");
02285         }
02286     }
02287     else if (!lua_isnoneornil(L, unit_arg))
02288     {
02289         config cfg = luaW_checkconfig(L, unit_arg);
02290         if (unit_arg == 1) {
02291             loc.x = cfg["x"] - 1;
02292             loc.y = cfg["y"] - 1;
02293             if (!resources::game_map->on_board(loc))
02294                 return luaL_argerror(L, 1, "invalid location");
02295         }
02296         u = new unit(cfg, true, resources::state_of_game);
02297     }
02298 
02299     resources::units->erase(loc);
02300     if (!u) return 0;
02301 
02302     if (lu) {
02303         resources::units->add(loc, *u);
02304         size_t uid = u->underlying_id();
02305         lu->lua_unit::~lua_unit();
02306         new(lu) lua_unit(uid);
02307     } else {
02308         u->set_location(loc);
02309         resources::units->insert(u);
02310     }
02311 
02312     return 0;
02313 }
02314 
02315 /**
02316  * Puts a unit on a recall list.
02317  * - Arg 1: WML table or unit.
02318  * - Arg 2: (optional) side.
02319  */
02320 static int intf_put_recall_unit(lua_State *L)
02321 {
02322     lua_unit *lu = NULL;
02323     unit *u = NULL;
02324     int side = lua_tointeger(L, 2);
02325     if (unsigned(side) > resources::teams->size()) side = 0;
02326 
02327     if (luaW_hasmetatable(L, 1, getunitKey))
02328     {
02329         lu = static_cast<lua_unit *>(lua_touserdata(L, 1));
02330         u = lu->get();
02331         if (!u || lu->on_recall_list())
02332             return luaL_argerror(L, 1, "unit not found");
02333     }
02334     else
02335     {
02336         config cfg = luaW_checkconfig(L, 1);
02337         u = new unit(cfg, true, resources::state_of_game);
02338     }
02339 
02340     if (!side) side = u->side();
02341     team &t = (*resources::teams)[side - 1];
02342     if (!t.persistent())
02343         return luaL_argerror(L, 2, "nonpersistent side");
02344     std::vector<unit> &rl = t.recall_list();
02345 
02346     // Avoid duplicates in the recall list.
02347     size_t uid = u->underlying_id();
02348     std::vector<unit>::iterator i = rl.begin();
02349     while (i != rl.end()) {
02350         if (i->underlying_id() == u->underlying_id()) {
02351             i = rl.erase(i);
02352         } else ++i;
02353     }
02354 
02355     rl.push_back(*u);
02356     if (lu) {
02357         if (lu->on_map())
02358             resources::units->erase(u->get_location());
02359         lu->lua_unit::~lua_unit();
02360         new(lu) lua_unit(side, uid);
02361     }
02362 
02363     return 0;
02364 }
02365 
02366 /**
02367  * Extracts a unit from the map or a recall list and gives it to Lua.
02368  * - Arg 1: unit userdata.
02369  */
02370 static int intf_extract_unit(lua_State *L)
02371 {
02372     if (!luaW_hasmetatable(L, 1, getunitKey))
02373         return luaL_typerror(L, 1, "unit");
02374     lua_unit *lu = static_cast<lua_unit *>(lua_touserdata(L, 1));
02375     unit *u = lu->get();
02376     if (!u) return luaL_argerror(L, 1, "unit not found");
02377 
02378     if (lu->on_map()) {
02379         u = resources::units->extract(u->get_location());
02380         assert(u);
02381     } else if (int side = lu->on_recall_list()) {
02382         team &t = (*resources::teams)[side - 1];
02383         unit *v = new unit(*u);
02384         std::vector<unit> &rl = t.recall_list();
02385         rl.erase(rl.begin() + (u - &rl[0]));
02386         u = v;
02387     } else {
02388         return 0;
02389     }
02390 
02391     lu->lua_unit::~lua_unit();
02392     new(lu) lua_unit(u);
02393     return 0;
02394 }
02395 
02396 /**
02397  * Finds a vacant tile.
02398  * - Args 1,2: location.
02399  * - Arg 3: optional unit for checking movement type.
02400  * - Rets 1,2: location.
02401  */
02402 static int intf_find_vacant_tile(lua_State *L)
02403 {
02404     int x = luaL_checkint(L, 1) - 1, y = luaL_checkint(L, 2) - 1;
02405 
02406     const unit *u = NULL;
02407     bool fake_unit = false;
02408     if (!lua_isnoneornil(L, 3)) {
02409         if (luaW_hasmetatable(L, 3, getunitKey)) {
02410             u = static_cast<lua_unit *>(lua_touserdata(L, 3))->get();
02411         } else {
02412             config cfg = luaW_checkconfig(L, 3);
02413             u = new unit(cfg, false, resources::state_of_game);
02414             fake_unit = true;
02415         }
02416     }
02417 
02418     map_location res = find_vacant_tile(*resources::game_map,
02419         *resources::units, map_location(x, y), pathfind::VACANT_ANY, u);
02420 
02421     if (fake_unit) delete u;
02422 
02423     if (!res.valid()) return 0;
02424     lua_pushinteger(L, res.x + 1);
02425     lua_pushinteger(L, res.y + 1);
02426     return 2;
02427 }
02428 
02429 /**
02430  * Floats some text on the map.
02431  * - Args 1,2: location.
02432  * - Arg 3: string.
02433  */
02434 static int intf_float_label(lua_State *L)
02435 {
02436     map_location loc;
02437     loc.x = luaL_checkinteger(L, 1) - 1;
02438     loc.y = luaL_checkinteger(L, 2) - 1;
02439 
02440     t_string text = luaW_checktstring(L, 3);
02441     resources::screen->float_label(loc, text, font::LABEL_COLOR.r,
02442         font::LABEL_COLOR.g, font::LABEL_COLOR.b);
02443     return 0;
02444 }
02445 
02446 /**
02447  * Creates a unit from its WML description.
02448  * - Arg 1: WML table.
02449  * - Ret 1: unit userdata.
02450  */
02451 static int intf_create_unit(lua_State *L)
02452 {
02453     config cfg = luaW_checkconfig(L, 1);
02454     unit *u = new unit(cfg, true, resources::state_of_game);
02455     new(lua_newuserdata(L, sizeof(lua_unit))) lua_unit(u);
02456     lua_pushlightuserdata(L
02457             , static_cast<void *>(const_cast<char *>(&getunitKey)));
02458     lua_rawget(L, LUA_REGISTRYINDEX);
02459     lua_setmetatable(L, -2);
02460     return 1;
02461 }
02462 
02463 /**
02464  * Copies a unit.
02465  * - Arg 1: unit userdata.
02466  * - Ret 1: unit userdata.
02467  */
02468 static int intf_copy_unit(lua_State *L)
02469 {
02470     unit const *u = luaW_checkunit(L, 1);
02471     new(lua_newuserdata(L, sizeof(lua_unit))) lua_unit(new unit(*u));
02472     lua_pushlightuserdata(L
02473             , static_cast<void *>(const_cast<char *>(&getunitKey)));
02474     lua_rawget(L, LUA_REGISTRYINDEX);
02475     lua_setmetatable(L, -2);
02476     return 1;
02477 }
02478 
02479 /**
02480  * Returns unit resistance against a given attack type.
02481  * - Arg 1: unit userdata.
02482  * - Arg 2: string containing the attack type.
02483  * - Arg 3: boolean indicating if attacker.
02484  * - Args 4,5: optional location.
02485  * - Ret 1: integer.
02486  */
02487 static int intf_unit_resistance(lua_State *L)
02488 {
02489     unit const *u = luaW_checkunit(L, 1);
02490     char const *m = luaL_checkstring(L, 2);
02491     bool a = lua_toboolean(L, 3);
02492 
02493     map_location loc = u->get_location();
02494     if (!lua_isnoneornil(L, 4)) {
02495         loc.x = luaL_checkinteger(L, 4) - 1;
02496         loc.y = luaL_checkinteger(L, 5) - 1;
02497     }
02498 
02499     lua_pushinteger(L, u->resistance_against(m, a, loc));
02500     return 1;
02501 }
02502 
02503 /**
02504  * Returns unit movement cost on a given terrain.
02505  * - Arg 1: unit userdata.
02506  * - Arg 2: string containing the terrain type.
02507  * - Ret 1: integer.
02508  */
02509 static int intf_unit_movement_cost(lua_State *L)
02510 {
02511     unit const *u = luaW_checkunit(L, 1);
02512     char const *m = luaL_checkstring(L, 2);
02513     t_translation::t_terrain t = t_translation::read_terrain_code(m);
02514     lua_pushinteger(L, u->movement_cost(t));
02515     return 1;
02516 }
02517 
02518 /**
02519  * Returns unit defense on a given terrain.
02520  * - Arg 1: unit userdata.
02521  * - Arg 2: string containing the terrain type.
02522  * - Ret 1: integer.
02523  */
02524 static int intf_unit_defense(lua_State *L)
02525 {
02526     unit const *u = luaW_checkunit(L, 1);
02527     char const *m = luaL_checkstring(L, 2);
02528     t_translation::t_terrain t = t_translation::read_terrain_code(m);
02529     lua_pushinteger(L, u->defense_modifier(t));
02530     return 1;
02531 }
02532 
02533 /**
02534  * Returns true if the unit has the given ability enabled.
02535  * - Arg 1: unit userdata.
02536  * - Arg 2: string.
02537  * - Ret 1: boolean.
02538  */
02539 static int intf_unit_ability(lua_State *L)
02540 {
02541     unit const *u = luaW_checkunit(L, 1);
02542     char const *m = luaL_checkstring(L, 2);
02543     lua_pushboolean(L, u->get_ability_bool(m));
02544     return 1;
02545 }
02546 
02547 /**
02548  * Changes a unit to the given unit type.
02549  * - Arg 1: unit userdata.
02550  * - Arg 2: string.
02551  */
02552 static int intf_transform_unit(lua_State *L)
02553 {
02554     unit *u = luaW_checkunit(L, 1);
02555     char const *m = luaL_checkstring(L, 2);
02556     const unit_type *utp = unit_types.find(m);
02557     if (!utp) return luaL_argerror(L, 2, "unknown unit type");
02558     u->advance_to(utp);
02559     return 0;
02560 }
02561 
02562 /**
02563  * Puts a table at the top of the stack with some combat result.
02564  */
02565 static void luaW_pushsimdata(lua_State *L, const combatant &cmb)
02566 {
02567     int n = cmb.hp_dist.size();
02568     lua_createtable(L, 0, 4);
02569     lua_pushnumber(L, cmb.poisoned);
02570     lua_setfield(L, -2, "poisoned");
02571     lua_pushnumber(L, cmb.slowed);
02572     lua_setfield(L, -2, "slowed");
02573     lua_pushnumber(L, cmb.average_hp());
02574     lua_setfield(L, -2, "average_hp");
02575     lua_createtable(L, n, 0);
02576     for (int i = 0; i < n; ++i) {
02577         lua_pushnumber(L, cmb.hp_dist[i]);
02578         lua_rawseti(L, -2, i);
02579     }
02580     lua_setfield(L, -2, "hp_chance");
02581 }
02582 
02583 /**
02584  * Simulates a combat between two units.
02585  * - Arg 1: attacker userdata.
02586  * - Arg 2: optional weapon index.
02587  * - Arg 3: defender userdata.
02588  * - Arg 4: optional weapon index.
02589  * - Ret 1: attacker results.
02590  * - Ret 2: defender results.
02591  */
02592 static int intf_simulate_combat(lua_State *L)
02593 {
02594     int arg_num = 1, att_w = -1, def_w = -1;
02595 
02596     unit const *att = luaW_checkunit(L, arg_num);
02597     ++arg_num;
02598     if (lua_isnumber(L, arg_num)) {
02599         att_w = lua_tointeger(L, arg_num) - 1;
02600         if (att_w < 0 || att_w >= int(att->attacks().size()))
02601             return luaL_argerror(L, arg_num, "weapon index out of bounds");
02602         ++arg_num;
02603     }
02604 
02605     unit const *def = luaW_checkunit(L, arg_num, true);
02606     ++arg_num;
02607     if (lua_isnumber(L, arg_num)) {
02608         def_w = lua_tointeger(L, arg_num) - 1;
02609         if (def_w < 0 || def_w >= int(def->attacks().size()))
02610             return luaL_argerror(L, arg_num, "weapon index out of bounds");
02611         ++arg_num;
02612     }
02613 
02614     battle_context context(*resources::units, att->get_location(),
02615         def->get_location(), att_w, def_w, 0.0, NULL, att);
02616 
02617     luaW_pushsimdata(L, context.get_attacker_combatant());
02618     luaW_pushsimdata(L, context.get_defender_combatant());
02619     return 2;
02620 }
02621 
02622 /**
02623  * Creates a vconfig containing the WML table.
02624  * - Arg 1: WML table.
02625  * - Ret 1: vconfig userdata.
02626  */
02627 static int intf_tovconfig(lua_State *L)
02628 {
02629     vconfig vcfg = luaW_checkvconfig(L, 1);
02630     luaW_pushvconfig(L, vcfg);
02631     return 1;
02632 }
02633 
02634 /**
02635  * Modifies the music playlist.
02636  * - Arg 1: WML table, or nil to force changes.
02637  */
02638 static int intf_set_music(lua_State *L)
02639 {
02640     if (lua_isnoneornil(L, 1)) {
02641         sound::commit_music_changes();
02642         return 0;
02643     }
02644 
02645     config cfg = luaW_checkconfig(L, 1);
02646     sound::play_music_config(cfg);
02647     return 0;
02648 }
02649 
02650 /**
02651  * Plays a sound, possibly repeated.
02652  * - Arg 1: string.
02653  * - Arg 2: optional integer.
02654  */
02655 static int intf_play_sound(lua_State *L)
02656 {
02657     char const *m = luaL_checkstring(L, 1);
02658     if (resources::controller->is_skipping_replay()) return 0;
02659     int repeats = lua_tointeger(L, 2);
02660     sound::play_sound(m, sound::SOUND_FX, repeats);
02661     return 0;
02662 }
02663 
02664 /**
02665  * Scrolls to given tile.
02666  * - Args 1,2: location.
02667  * - Arg 3: boolean preventing scroll to fog.
02668  */
02669 static int intf_scroll_to_tile(lua_State *L)
02670 {
02671     int x = luaL_checkinteger(L, 1) - 1;
02672     int y = luaL_checkinteger(L, 2) - 1;
02673     bool check_fogged = lua_toboolean(L, 3);
02674     resources::screen->scroll_to_tile(map_location(x, y),
02675         game_display::SCROLL, check_fogged);
02676     return 0;
02677 }
02678 
02679 /**
02680  * Compares 2 version strings - which is newer.
02681  * - Args 1,3: version strings
02682  * - Arg 2: comparison operator (string)
02683  * - Ret 1: comparison result
02684  */
02685 static int intf_compare_versions(lua_State* L)
02686 {
02687     char const *v1 = luaL_checkstring(L, 1);
02688 
02689     const VERSION_COMP_OP vop = parse_version_op(luaL_checkstring(L, 2));
02690     if(vop == OP_INVALID) return luaL_argerror(L, 2, "unknown version comparison operator - allowed are ==, !=, <, <=, > and >=");
02691 
02692     char const *v2 = luaL_checkstring(L, 3);
02693 
02694     const bool result = do_version_check(version_info(v1), vop, version_info(v2));
02695     lua_pushboolean(L, result);
02696 
02697     return 1;
02698 }
02699 
02700 /**
02701  * Selects and highlights the given location on the map.
02702  * - Args 1,2: location.
02703  * - Args 3,4: booleans
02704  */
02705 static int intf_select_hex(lua_State *L)
02706 {
02707     const int x = luaL_checkinteger(L, 1) - 1;
02708     const int y = luaL_checkinteger(L, 2) - 1;
02709 
02710     map_location loc(x, y);
02711     if(!resources::game_map->on_board(loc)) return luaL_argerror(L, 1, "not on board");
02712     bool highlight = true;
02713     if(!lua_isnoneornil(L, 3))
02714         highlight = lua_toboolean(L, 3);
02715     const bool fire_event = lua_toboolean(L, 4);
02716     resources::controller->get_mouse_handler_base().select_hex(
02717         map_location(x, y), false, highlight, fire_event);
02718     if(highlight)
02719         resources::screen->highlight_hex(loc);
02720     return 0;
02721 }
02722 
02723 struct lua_synchronize : mp_sync::user_choice
02724 {
02725     lua_State *L;
02726     lua_synchronize(lua_State *l): L(l) {}
02727 
02728     virtual config query_user() const
02729     {
02730         config cfg;
02731         int index = 1;
02732         if (!lua_isnoneornil(L, 2)) {
02733             int side = resources::controller->current_side();
02734             if ((*resources::teams)[side - 1].is_ai())
02735                 index = 2;
02736         }
02737         lua_settop(L, index);
02738         if (luaW_pcall(L, 0, 1, false))
02739             luaW_toconfig(L, -1, cfg);
02740         return cfg;
02741     }
02742 
02743     virtual config random_choice(rand_rng::simple_rng &) const
02744     {
02745         return config();
02746     }
02747 };
02748 
02749 /**
02750  * Ensures a value is synchronized among all the clients.
02751  * - Arg 1: function to compute the value, called if the client is the master.
02752  * - Arg 2: optional function, called instead of the first function if the user is not human.
02753  * - Ret 1: WML table returned by the function.
02754  */
02755 static int intf_synchronize_choice(lua_State *L)
02756 {
02757     config cfg = mp_sync::get_user_choice("input", lua_synchronize(L));
02758     luaW_pushconfig(L, cfg);
02759     return 1;
02760 }
02761 
02762 struct scoped_dialog
02763 {
02764     lua_State *L;
02765     scoped_dialog *prev;
02766     static scoped_dialog *current;
02767     gui2::twindow *window;
02768     typedef std::map<gui2::twidget *, int> callback_map;
02769     callback_map callbacks;
02770 
02771     scoped_dialog(lua_State *l, gui2::twindow *w);
02772     ~scoped_dialog();
02773 private:
02774     scoped_dialog(const scoped_dialog &);
02775 };
02776 
02777 scoped_dialog *scoped_dialog::current = NULL;
02778 
02779 scoped_dialog::scoped_dialog(lua_State *l, gui2::twindow *w)
02780     : L(l), prev(current), window(w), callbacks()
02781 {
02782     lua_pushlightuserdata(L
02783             , static_cast<void *>(const_cast<char *>(&dlgclbkKey)));
02784     lua_createtable(L, 1, 0);
02785     lua_pushvalue(L, -2);
02786     lua_rawget(L, LUA_REGISTRYINDEX);
02787     lua_rawseti(L, -2, 1);
02788     lua_rawset(L, LUA_REGISTRYINDEX);
02789     current = this;
02790 }
02791 
02792 scoped_dialog::~scoped_dialog()
02793 {
02794     delete window;
02795     current = prev;
02796     lua_pushlightuserdata(L
02797             , static_cast<void *>(const_cast<char *>(&dlgclbkKey)));
02798     lua_pushvalue(L, -1);
02799     lua_rawget(L, LUA_REGISTRYINDEX);
02800     lua_rawgeti(L, -1, 1);
02801     lua_remove(L, -2);
02802     lua_rawset(L, LUA_REGISTRYINDEX);
02803 }
02804 
02805 static gui2::twidget *find_widget(lua_State *L, int i, bool readonly)
02806 {
02807     if (!scoped_dialog::current) {
02808         luaL_error(L, "no visible dialog");
02809         error_call_destructors_1:
02810         luaL_argerror(L, i, "out of bounds");
02811         error_call_destructors_2:
02812         luaL_typerror(L, i, "string");
02813         error_call_destructors_3:
02814         luaL_argerror(L, i, "widget not found");
02815         return NULL;
02816     }
02817 
02818     gui2::twidget *w = scoped_dialog::current->window;
02819     for (; !lua_isnoneornil(L, i); ++i)
02820     {
02821 #ifdef GUI2_EXPERIMENTAL_LISTBOX
02822         if (gui2::tlist *l = dynamic_cast<gui2::tlist *>(w))
02823 #else
02824         if (gui2::tlistbox *l = dynamic_cast<gui2::tlistbox *>(w))
02825 #endif
02826         {
02827             int v = lua_tointeger(L, i);
02828             if (v < 1)
02829                 goto error_call_destructors_1;
02830             int n = l->get_item_count();
02831             if (v > n) {
02832                 if (readonly)
02833                     goto error_call_destructors_1;
02834                 utils::string_map dummy;
02835                 for (; n < v; ++n)
02836                     l->add_row(dummy);
02837             }
02838             w = l->get_row_grid(v - 1);
02839         }
02840         else if (gui2::tmulti_page *l = dynamic_cast<gui2::tmulti_page *>(w))
02841         {
02842             int v = lua_tointeger(L, i);
02843             if (v < 1)
02844                 goto error_call_destructors_1;
02845             int n = l->get_page_count();
02846             if (v > n) {
02847                 if (readonly)
02848                     goto error_call_destructors_1;
02849                 utils::string_map dummy;
02850                 for (; n < v; ++n)
02851                     l->add_page(dummy);
02852             }
02853             w = &l->page_grid(v - 1);
02854         }
02855         else
02856         {
02857             char const *m = lua_tostring(L, i);
02858             if (!m) goto error_call_destructors_2;
02859             w = w->find(m, false);
02860         }
02861         if (!w) goto error_call_destructors_3;
02862     }
02863 
02864     return w;
02865 }
02866 
02867 /**
02868  * Displays a window.
02869  * - Arg 1: WML table describing the window.
02870  * - Arg 2: function called at pre-show.
02871  * - Arg 3: function called at post-show.
02872  * - Ret 1: integer.
02873  */
02874 static int intf_show_dialog(lua_State *L)
02875 {
02876     config def_cfg = luaW_checkconfig(L, 1);
02877 
02878     gui2::twindow_builder::tresolution def(def_cfg);
02879     scoped_dialog w(L, gui2::build(resources::screen->video(), &def));
02880 
02881     if (!lua_isnoneornil(L, 2)) {
02882         lua_pushvalue(L, 2);
02883         lua_call(L, 0, 0);
02884     }
02885 
02886     int v = scoped_dialog::current->window->show(true, 0);
02887 
02888     if (!lua_isnoneornil(L, 3)) {
02889         lua_pushvalue(L, 3);
02890         lua_call(L, 0, 0);
02891     }
02892 
02893     lua_pushinteger(L, v);
02894     return 1;
02895 }
02896 
02897 /**
02898  * Sets the value of a widget on the current dialog.
02899  * - Arg 1: scalar.
02900  * - Args 2..n: path of strings and integers.
02901  */
02902 static int intf_set_dialog_value(lua_State *L)
02903 {
02904     gui2::twidget *w = find_widget(L, 2, false);
02905 
02906 #ifdef GUI2_EXPERIMENTAL_LISTBOX
02907     if (gui2::tlist *l = dynamic_cast<gui2::tlist *>(w))
02908 #else
02909     if (gui2::tlistbox *l = dynamic_cast<gui2::tlistbox *>(w))
02910 #endif
02911     {
02912         int v = luaL_checkinteger(L, 1);
02913         int n = l->get_item_count();
02914         if (1 <= v && v <= n)
02915             l->select_row(v - 1);
02916         else
02917             return luaL_argerror(L, 1, "out of bounds");
02918     }
02919     else if (gui2::tmulti_page *l = dynamic_cast<gui2::tmulti_page *>(w))
02920     {
02921         int v = luaL_checkinteger(L, 1);
02922         int n = l->get_page_count();
02923         if (1 <= v && v <= n)
02924             l->select_page(v - 1);
02925         else
02926             return luaL_argerror(L, 1, "out of bounds");
02927     }
02928     else if (gui2::tselectable_ *s = dynamic_cast<gui2::tselectable_ *>(w))
02929     {
02930         s->set_value(lua_toboolean(L, 1));
02931     }
02932     else if (gui2::ttext_box *t = dynamic_cast<gui2::ttext_box *>(w))
02933     {
02934         const t_string& text = luaW_checktstring(L, 1);
02935         t->set_value(text.str());
02936     }
02937     else if (gui2::tslider *s = dynamic_cast<gui2::tslider *>(w))
02938     {
02939         const int v = luaL_checkinteger(L, 1);
02940         const int m = s->get_minimum_value();
02941         const int n = s->get_maximum_value();
02942         if (m <= v && v <= n)
02943             s->set_value(v);
02944         else
02945             return luaL_argerror(L, 1, "out of bounds");
02946     }
02947     else if (gui2::tprogress_bar *p = dynamic_cast<gui2::tprogress_bar *>(w))
02948     {
02949         const int v = luaL_checkinteger(L, 1);
02950         if (0 <= v && v <= 100)
02951             p->set_percentage(v);
02952         else
02953             return luaL_argerror(L, 1, "out of bounds");
02954     }
02955     else
02956     {
02957         t_string v = luaW_checktstring(L, 1);
02958         gui2::tcontrol *c = dynamic_cast<gui2::tcontrol *>(w);
02959         if (!c) return luaL_argerror(L, lua_gettop(L), "unsupported widget");
02960         c->set_label(v);
02961     }
02962 
02963     return 0;
02964 }
02965 
02966 /**
02967  * Gets the value of a widget on the current dialog.
02968  * - Args 1..n: path of strings and integers.
02969  * - Ret 1: scalar.
02970  */
02971 static int intf_get_dialog_value(lua_State *L)
02972 {
02973     gui2::twidget *w = find_widget(L, 1, true);
02974 
02975 #ifdef GUI2_EXPERIMENTAL_LISTBOX
02976     if (gui2::tlist *l = dynamic_cast<gui2::tlist *>(w))
02977 #else
02978     if (gui2::tlistbox *l = dynamic_cast<gui2::tlistbox *>(w))
02979 #endif
02980     {
02981         lua_pushinteger(L, l->get_selected_row() + 1);
02982     } else if (gui2::tmulti_page *l = dynamic_cast<gui2::tmulti_page *>(w)) {
02983         lua_pushinteger(L, l->get_selected_page() + 1);
02984     } else if (gui2::tselectable_ *s = dynamic_cast<gui2::tselectable_ *>(w)) {
02985         lua_pushboolean(L, s->get_value());
02986     } else if (gui2::ttext_box *t = dynamic_cast<gui2::ttext_box *>(w)) {
02987         lua_pushstring(L, t->get_value().c_str());
02988     } else if (gui2::tslider *s = dynamic_cast<gui2::tslider *>(w)) {
02989         lua_pushinteger(L, s->get_value());
02990     } else if (gui2::tprogress_bar *p = dynamic_cast<gui2::tprogress_bar *>(w)) {
02991         lua_pushinteger(L, p->get_percentage());
02992     } else
02993         return luaL_argerror(L, lua_gettop(L), "unsupported widget");
02994 
02995     return 1;
02996 }
02997 
02998 static void dialog_callback(gui2::twidget *w)
02999 {
03000     int cb;
03001     {
03002         scoped_dialog::callback_map &m = scoped_dialog::current->callbacks;
03003         scoped_dialog::callback_map::const_iterator i = m.find(w);
03004         if (i == m.end()) return;
03005         cb = i->second;
03006     }
03007     lua_State *L = scoped_dialog::current->L;
03008     lua_pushlightuserdata(L
03009             , static_cast<void *>(const_cast<char *>(&dlgclbkKey)));
03010     lua_rawget(L, LUA_REGISTRYINDEX);
03011     lua_rawgeti(L, -1, cb);
03012     lua_remove(L, -2);
03013     lua_call(L, 0, 0);
03014 }
03015 
03016 /** Helper struct for intf_set_dialog_callback. */
03017 struct tdialog_callback_wrapper
03018 {
03019     void forward(gui2::twidget* widget)
03020     {
03021         dialog_callback(widget);
03022     }
03023 };
03024 /**
03025  * Sets a callback on a widget of the current dialog.
03026  * - Arg 1: function.
03027  * - Args 2..n: path of strings and integers.
03028  */
03029 static int intf_set_dialog_callback(lua_State *L)
03030 {
03031     gui2::twidget *w = find_widget(L, 2, true);
03032 
03033     scoped_dialog::callback_map &m = scoped_dialog::current->callbacks;
03034     scoped_dialog::callback_map::iterator i = m.find(w);
03035     if (i != m.end())
03036     {
03037         lua_pushlightuserdata(L
03038                 , static_cast<void *>(const_cast<char *>(&dlgclbkKey)));
03039         lua_rawget(L, LUA_REGISTRYINDEX);
03040         lua_pushnil(L);
03041         lua_rawseti(L, -2, i->second);
03042         lua_pop(L, 1);
03043         m.erase(i);
03044     }
03045 
03046     if (lua_isnil(L, 1)) return 0;
03047 
03048     if (gui2::tclickable_ *c = dynamic_cast<gui2::tclickable_ *>(w)) {
03049         static tdialog_callback_wrapper wrapper;
03050         c->connect_click_handler(boost::bind(
03051                                       &tdialog_callback_wrapper::forward
03052                                     , wrapper
03053                                     , w));
03054     } else if (gui2::tselectable_ *s = dynamic_cast<gui2::tselectable_ *>(w)) {
03055         s->set_callback_state_change(&dialog_callback);
03056     }
03057 #ifdef GUI2_EXPERIMENTAL_LISTBOX
03058     else if (gui2::tlist *l = dynamic_cast<gui2::tlist *>(w)) {
03059         static tdialog_callback_wrapper wrapper;
03060         connect_signal_notify_modified(*l
03061                 , boost::bind(
03062                       &tdialog_callback_wrapper::forward
03063                     , wrapper
03064                     , w));
03065     }
03066 #else
03067     else if (gui2::tlistbox *l = dynamic_cast<gui2::tlistbox *>(w)) {
03068         l->set_callback_value_change(&dialog_callback);
03069     }
03070 #endif
03071     else
03072         return luaL_argerror(L, lua_gettop(L), "unsupported widget");
03073 
03074     lua_pushlightuserdata(L
03075             , static_cast<void *>(const_cast<char *>(&dlgclbkKey)));
03076     lua_rawget(L, LUA_REGISTRYINDEX);
03077     int n = lua_rawlen(L, -1) + 1;
03078     m[w] = n;
03079     lua_pushvalue(L, 1);
03080     lua_rawseti(L, -2, n);
03081     lua_pop(L, 1);
03082 
03083     return 0;
03084 }
03085 
03086 /**
03087  * Sets a canvas on a widget of the current dialog.
03088  * - Arg 1: integer.
03089  * - Arg 2: WML table.
03090  * - Args 3..n: path of strings and integers.
03091  */
03092 static int intf_set_dialog_canvas(lua_State *L)
03093 {
03094     int i = luaL_checkinteger(L, 1);
03095     gui2::twidget *w = find_widget(L, 3, true);
03096     gui2::tcontrol *c = dynamic_cast<gui2::tcontrol *>(w);
03097     if (!c) return luaL_argerror(L, lua_gettop(L), "unsupported widget");
03098 
03099     std::vector<gui2::tcanvas> &cv = c->canvas();
03100     if (i < 1 || unsigned(i) > cv.size())
03101         return luaL_argerror(L, 1, "out of bounds");
03102 
03103     config cfg = luaW_checkconfig(L, 2);
03104     cv[i - 1].set_cfg(cfg);
03105     return 0;
03106 }
03107 
03108 /**
03109  * Sets a widget's state to active or inactive
03110  * - Arg 1: boolean.
03111  * - Args 2..n: path of strings and integers.
03112  */
03113 static int intf_set_dialog_active(lua_State *L)
03114 {
03115     const bool b = lua_toboolean(L, 1);
03116     gui2::twidget *w = find_widget(L, 2, true);
03117     gui2::tcontrol *c = dynamic_cast<gui2::tcontrol *>(w);
03118     if (!c) return luaL_argerror(L, lua_gettop(L), "unsupported widget");
03119 
03120     c->set_active(b);
03121     return 0;
03122 }
03123 
03124 /**
03125  * Gets all the locations matching a given filter.
03126  * - Arg 1: WML table.
03127  * - Ret 1: array of integer pairs.
03128  */
03129 static int intf_get_locations(lua_State *L)
03130 {
03131     vconfig filter = luaW_checkvconfig(L, 1);
03132 
03133     std::set<map_location> res;
03134     const terrain_filter t_filter(filter, *resources::units);
03135     t_filter.get_locations(res, true);
03136 
03137     lua_createtable(L, res.size(), 0);
03138     int i = 1;
03139     foreach (map_location const &loc, res)
03140     {
03141         lua_createtable(L, 2, 0);
03142         lua_pushinteger(L, loc.x + 1);
03143         lua_rawseti(L, -2, 1);
03144         lua_pushinteger(L, loc.y + 1);
03145         lua_rawseti(L, -2, 2);
03146         lua_rawseti(L, -2, i);
03147         ++i;
03148     }
03149     return 1;
03150 }
03151 
03152 /**
03153  * Matches a location against the given filter.
03154  * - Args 1,2: integers.
03155  * - Arg 3: WML table.
03156  * - Ret 1: boolean.
03157  */
03158 static int intf_match_location(lua_State *L)
03159 {
03160     int x = luaL_checkinteger(L, 1) - 1;
03161     int y = luaL_checkinteger(L, 2) - 1;
03162     vconfig filter = luaW_checkvconfig(L, 3, true);
03163 
03164     if (filter.null()) {
03165         lua_pushboolean(L, true);
03166         return 1;
03167     }
03168 
03169     const terrain_filter t_filter(filter, *resources::units);
03170     lua_pushboolean(L, t_filter.match(map_location(x, y)));
03171     return 1;
03172 }
03173 
03174 
03175 
03176 /**
03177  * Matches a side against the given filter.
03178  * - Args 1: side number.
03179  * - Arg 2: WML table.
03180  * - Ret 1: boolean.
03181  */
03182 static int intf_match_side(lua_State *L)
03183 {
03184     unsigned side = luaL_checkinteger(L, 1) - 1;
03185     if (side >= resources::teams->size()) return 0;
03186     vconfig filter = luaW_checkvconfig(L, 2, true);
03187 
03188     if (filter.null()) {
03189         lua_pushboolean(L, true);
03190         return 1;
03191     }
03192 
03193     side_filter s_filter(filter);
03194     lua_pushboolean(L, s_filter.match(side + 1));
03195     return 1;
03196 }
03197 
03198 /**
03199  * Returns a proxy table array for all sides matching the given SSF.
03200  * - Arg 1: SSF
03201  * - Ret 1: proxy table array
03202  */
03203 static int intf_get_sides(lua_State* L)
03204 {
03205     std::vector<int> sides;
03206     const vconfig ssf = luaW_checkvconfig(L, 1, true);
03207     if(ssf.null()){
03208         for(unsigned side_number = 1; side_number <= resources::teams->size(); ++side_number)
03209             sides.push_back(side_number);
03210     } else {
03211         side_filter filter(ssf);
03212         sides = filter.get_teams();
03213     }
03214 
03215     //keep this stack in the loop:
03216     //1: getsideKey getmetatable
03217     //2: return table
03218     //3: userdata for a side
03219     //4: getsideKey metatable copy (of index 1)
03220 
03221     lua_settop(L, 0);
03222     lua_pushlightuserdata(L
03223             , static_cast<void*>(const_cast<char *>(&getsideKey)));
03224     lua_rawget(L, LUA_REGISTRYINDEX);
03225     lua_createtable(L, sides.size(), 0);
03226     unsigned index = 1;
03227     foreach(int side, sides) {
03228         // Create a full userdata containing a pointer to the team.
03229         team** t = static_cast<team**>(lua_newuserdata(L, sizeof(team*)));
03230         *t = &((*resources::teams)[side - 1]);
03231         lua_pushvalue(L, 1);
03232         lua_setmetatable(L, 3);
03233         lua_rawseti(L, 2, index);
03234         ++index;
03235     }
03236 
03237     return 1;
03238 }
03239 
03240 /**
03241  * .Returns information about the global traits known to the engine.
03242  * - Ret 1: Table with named fields holding wml tables describing the traits.
03243  */
03244 static int intf_get_traits(lua_State* L)
03245 {
03246     lua_newtable(L);
03247     foreach(const config& trait, unit_types.traits()) {
03248         const std::string& id = trait["id"];
03249         //It seems the engine does nowhere check the id field for emptyness or duplicates
03250         //(also not later on).
03251         //However, the worst thing to happen is that the trait read later overwrites the older one,
03252         //and this is not the right place for such checks.
03253         lua_pushstring(L, id.c_str());
03254         luaW_pushconfig(L, trait);
03255         lua_rawset(L, -3);
03256     }
03257     return 1;
03258 }
03259 
03260 /**
03261  * Adds a modification to a unit.
03262  * - Arg 1: unit.
03263  * - Arg 2: string.
03264  * - Arg 3: WML table.
03265  */
03266 static int intf_add_modification(lua_State *L)
03267 {
03268     unit *u = luaW_checkunit(L, 1);
03269     char const *m = luaL_checkstring(L, 2);
03270     std::string sm = m;
03271     if (sm != "advance" && sm != "object" && sm != "trait")
03272         return luaL_argerror(L, 2, "unknown modification type");
03273 
03274     config cfg = luaW_checkconfig(L, 3);
03275     u->add_modification(sm, cfg);
03276     return 0;
03277 }
03278 
03279 /**
03280  * Adds a new known unit type to the help system.
03281  * - Arg 1: string.
03282  */
03283 static int intf_add_known_unit(lua_State *L)
03284 {
03285     char const *ty = luaL_checkstring(L, 1);
03286     if(!unit_types.find(ty)) return luaL_argerror(L, 1, "unknown unit type");
03287     preferences::encountered_units().insert(ty);
03288     return 0;
03289 }
03290 
03291 /**
03292  * Adds an overlay on a tile.
03293  * - Args 1,2: location.
03294  * - Arg 3: WML table.
03295  */
03296 static int intf_add_tile_overlay(lua_State *L)
03297 {
03298     int x = luaL_checkinteger(L, 1) - 1;
03299     int y = luaL_checkinteger(L, 2) - 1;
03300     config cfg = luaW_checkconfig(L, 3);
03301 
03302     resources::screen->add_overlay(map_location(x, y), cfg["image"], cfg["halo"],
03303         cfg["team_name"], cfg["visible_in_fog"].to_bool(true));
03304     return 0;
03305 }
03306 
03307 /**
03308  * Adds an overlay on a tile.
03309  * - Args 1,2: location.
03310  * - Arg 3: optional string.
03311  */
03312 static int intf_remove_tile_overlay(lua_State *L)
03313 {
03314     int x = luaL_checkinteger(L, 1) - 1;
03315     int y = luaL_checkinteger(L, 2) - 1;
03316     char const *m = lua_tostring(L, 3);
03317 
03318     if (m) {
03319         resources::screen->remove_single_overlay(map_location(x, y), m);
03320     } else {
03321         resources::screen->remove_overlay(map_location(x, y));
03322     }
03323     return 0;
03324 }
03325 
03326 /**
03327  * Delays engine for a while.
03328  * - Arg 1: integer.
03329  */
03330 static int intf_delay(lua_State *L)
03331 {
03332     unsigned final = SDL_GetTicks() + luaL_checkinteger(L, 1);
03333     do {
03334         resources::controller->play_slice(false);
03335         resources::screen->delay(10);
03336     } while (int(final - SDL_GetTicks()) > 0);
03337     return 0;
03338 }
03339 
03340 /**
03341  * Gets the dimension of an image.
03342  * - Arg 1: string.
03343  * - Ret 1: width.
03344  * - Ret 2: height.
03345  */
03346 static int intf_get_image_size(lua_State *L)
03347 {
03348     char const *m = luaL_checkstring(L, 1);
03349     image::locator img(m);
03350     if (!img.file_exists()) return 0;
03351     surface s = get_image(img);
03352     lua_pushinteger(L, s->w);
03353     lua_pushinteger(L, s->h);
03354     return 2;
03355 }
03356 
03357 /**
03358  * Lua frontend to the modify_ai functionality
03359  * - Arg 1: config.
03360  */
03361 static int intf_modify_ai(lua_State *L)
03362 {
03363     config cfg;
03364     luaW_toconfig(L, 1, cfg);
03365     int side = cfg["side"];
03366     ai::manager::modify_active_ai_for_side(side, cfg);
03367     return 0;
03368 }
03369 
03370 struct lua_report_generator : reports::generator
03371 {
03372     lua_State *mState;
03373     std::string name;
03374     lua_report_generator(lua_State *L, const std::string &n)
03375         : mState(L), name(n) {}
03376     virtual config generate();
03377 };
03378 
03379 config lua_report_generator::generate()
03380 {
03381     lua_State *L = mState;
03382     config cfg;
03383     if (!luaW_getglobal(L, "wesnoth", "theme_items", name.c_str(), NULL))
03384         return cfg;
03385     if (!luaW_pcall(L, 0, 1)) return cfg;
03386     luaW_toconfig(L, -1, cfg);
03387     lua_pop(L, 1);
03388     return cfg;
03389 }
03390 
03391 /**
03392  * Executes its upvalue as a theme item generator.
03393  */
03394 static int cfun_theme_item(lua_State *L)
03395 {
03396     const char *m = lua_tostring(L, lua_upvalueindex(1));
03397     luaW_pushconfig(L, reports::generate_report(m, true));
03398     return 1;
03399 }
03400 
03401 /**
03402  * Creates a field of the theme_items table and returns it (__index metamethod).
03403  */
03404 static int impl_theme_items_get(lua_State *L)
03405 {
03406     char const *m = luaL_checkstring(L, 2);
03407     lua_pushvalue(L, 2);
03408     lua_pushcclosure(L, cfun_theme_item, 1);
03409     lua_pushvalue(L, 2);
03410     lua_pushvalue(L, -2);
03411     lua_rawset(L, 1);
03412     reports::register_generator(m, new lua_report_generator(L, m));
03413     return 1;
03414 }
03415 
03416 /**
03417  * Sets a field of the theme_items table (__newindex metamethod).
03418  */
03419 static int impl_theme_items_set(lua_State *L)
03420 {
03421     char const *m = luaL_checkstring(L, 2);
03422     lua_pushvalue(L, 2);
03423     lua_pushvalue(L, 3);
03424     lua_rawset(L, 1);
03425     reports::register_generator(m, new lua_report_generator(L, m));
03426     return 0;
03427 }
03428 
03429 
03430 LuaKernel::LuaKernel(const config &cfg)
03431     : mState(luaL_newstate()), level_(cfg)
03432 {
03433     lua_State *L = mState;
03434 
03435     // Open safe libraries.
03436     // Debug and OS are not, but most of their functions will be disabled below.
03437     static const luaL_Reg safe_libs[] = {
03438         { "",       luaopen_base   },
03439         { "table",  luaopen_table  },
03440         { "string", luaopen_string },
03441         { "math",   luaopen_math   },
03442         { "debug",  luaopen_debug  },
03443         { "os",     luaopen_os     },
03444         { NULL, NULL }
03445     };
03446     for (luaL_Reg const *lib = safe_libs; lib->func; ++lib)
03447     {
03448         luaL_requiref(L, lib->name, lib->func, 1);
03449         lua_pop(L, 1);  /* remove lib */
03450     }
03451 
03452     // Put some callback functions in the scripting environment.
03453     static luaL_Reg const callbacks[] = {
03454         { "add_known_unit",           &intf_add_known_unit           },
03455         { "add_modification",         &intf_add_modification         },
03456         { "add_tile_overlay",         &intf_add_tile_overlay         },
03457         { "clear_messages",           &intf_clear_messages           },
03458         { "compare_versions",         &intf_compare_versions         },
03459         { "copy_unit",                &intf_copy_unit                },
03460         { "create_unit",              &intf_create_unit              },
03461         { "debug",                    &intf_debug                    },
03462         { "delay",                    &intf_delay                    },
03463         { "dofile",                   &intf_dofile                   },
03464         { "eval_conditional",         &intf_eval_conditional         },
03465         { "extract_unit",             &intf_extract_unit             },
03466         { "find_path",                &intf_find_path                },
03467         { "find_reach",               &intf_find_reach               },
03468         { "find_vacant_tile",         &intf_find_vacant_tile         },
03469         { "fire_event",               &intf_fire_event               },
03470         { "float_label",              &intf_float_label              },
03471         { "get_dialog_value",         &intf_get_dialog_value         },
03472         { "get_displayed_unit",       &intf_get_displayed_unit       },
03473         { "get_image_size",           &intf_get_image_size           },
03474         { "get_locations",            &intf_get_locations            },
03475         { "get_map_size",             &intf_get_map_size             },
03476         { "get_mouseover_tile",       &intf_get_mouseover_tile       },
03477         { "get_recall_units",         &intf_get_recall_units         },
03478         { "get_selected_tile",        &intf_get_selected_tile        },
03479         { "get_sides",                &intf_get_sides                },
03480         { "get_starting_location",    &intf_get_starting_location    },
03481         { "get_terrain",              &intf_get_terrain              },
03482         { "get_terrain_info",         &intf_get_terrain_info         },
03483         { "get_time_of_day",          &intf_get_time_of_day          },
03484         { "get_traits",               &intf_get_traits               },
03485         { "get_unit",                 &intf_get_unit                 },
03486         { "get_units",                &intf_get_units                },
03487         { "get_variable",             &intf_get_variable             },
03488         { "get_village_owner",        &intf_get_village_owner        },
03489         { "highlight_hex",            &intf_highlight_hex            },
03490         { "is_enemy",                 &intf_is_enemy                 },
03491         { "match_location",           &intf_match_location           },
03492         { "match_side",               &intf_match_side               },
03493         { "match_unit",               &intf_match_unit               },
03494         { "message",                  &intf_message                  },
03495         { "modify_ai",                &intf_modify_ai                },
03496         { "play_sound",               &intf_play_sound               },
03497         { "put_recall_unit",          &intf_put_recall_unit          },
03498         { "put_unit",                 &intf_put_unit                 },
03499         { "remove_tile_overlay",      &intf_remove_tile_overlay      },
03500         { "require",                  &intf_require                  },
03501         { "scroll_to_tile",           &intf_scroll_to_tile           },
03502         { "select_hex",               &intf_select_hex               },
03503         { "set_dialog_active",        &intf_set_dialog_active        },
03504         { "set_dialog_callback",      &intf_set_dialog_callback      },
03505         { "set_dialog_canvas",        &intf_set_dialog_canvas        },
03506         { "set_dialog_value",         &intf_set_dialog_value         },
03507         { "set_music",                &intf_set_music                },
03508         { "set_terrain",              &intf_set_terrain              },
03509         { "set_variable",             &intf_set_variable             },
03510         { "set_village_owner",        &intf_set_village_owner        },
03511         { "show_dialog",              &intf_show_dialog              },
03512         { "simulate_combat",          &intf_simulate_combat          },
03513         { "synchronize_choice",       &intf_synchronize_choice       },
03514         { "textdomain",               &intf_textdomain               },
03515         { "tovconfig",                &intf_tovconfig                },
03516         { "transform_unit",           &intf_transform_unit           },
03517         { "unit_ability",             &intf_unit_ability             },
03518         { "unit_defense",             &intf_unit_defense             },
03519         { "unit_movement_cost",       &intf_unit_movement_cost       },
03520         { "unit_resistance",          &intf_unit_resistance          },
03521         { NULL, NULL }
03522     };
03523     luaL_register(L, "wesnoth", callbacks);
03524 
03525     // Create the getside metatable.
03526     lua_pushlightuserdata(L
03527             , static_cast<void *>(const_cast<char *>(&getsideKey)));
03528     lua_createtable(L, 0, 3);
03529     lua_pushcfunction(L, impl_side_get);
03530     lua_setfield(L, -2, "__index");
03531     lua_pushcfunction(L, impl_side_set);
03532     lua_setfield(L, -2, "__newindex");
03533     lua_pushstring(L, "side");
03534     lua_setfield(L, -2, "__metatable");
03535     lua_rawset(L, LUA_REGISTRYINDEX);
03536 
03537     // Create the gettext metatable.
03538     lua_pushlightuserdata(L
03539             , static_cast<void *>(const_cast<char *>(&gettextKey)));
03540     lua_createtable(L, 0, 2);
03541     lua_pushcfunction(L, impl_gettext);
03542     lua_setfield(L, -2, "__call");
03543     lua_pushstring(L, "message domain");
03544     lua_setfield(L, -2, "__metatable");
03545     lua_rawset(L, LUA_REGISTRYINDEX);
03546 
03547     // Create the gettype metatable.
03548     lua_pushlightuserdata(L
03549             , static_cast<void *>(const_cast<char *>(&gettypeKey)));
03550     lua_createtable(L, 0, 2);
03551     lua_pushcfunction(L, impl_unit_type_get);
03552     lua_setfield(L, -2, "__index");
03553     lua_pushstring(L, "unit type");
03554     lua_setfield(L, -2, "__metatable");
03555     lua_rawset(L, LUA_REGISTRYINDEX);
03556 
03557     //Create the getrace metatable
03558     lua_pushlightuserdata(L
03559             , static_cast<void *>(const_cast<char *>(&getraceKey)));
03560     lua_createtable(L, 0, 2);
03561     lua_pushcfunction(L, impl_race_get);
03562     lua_setfield(L, -2, "__index");
03563     lua_pushstring(L, "race");
03564     lua_setfield(L, -2, "__metatable");
03565     lua_rawset(L, LUA_REGISTRYINDEX);
03566 
03567     // Create the getunit metatable.
03568     lua_pushlightuserdata(L
03569             , static_cast<void *>(const_cast<char *>(&getunitKey)));
03570     lua_createtable(L, 0, 4);
03571     lua_pushcfunction(L, impl_unit_collect);
03572     lua_setfield(L, -2, "__gc");
03573     lua_pushcfunction(L, impl_unit_get);
03574     lua_setfield(L, -2, "__index");
03575     lua_pushcfunction(L, impl_unit_set);
03576     lua_setfield(L, -2, "__newindex");
03577     lua_pushstring(L, "unit");
03578     lua_setfield(L, -2, "__metatable");
03579     lua_rawset(L, LUA_REGISTRYINDEX);
03580 
03581     // Create the tstring metatable.
03582     lua_pushlightuserdata(L
03583             , static_cast<void *>(const_cast<char *>(&tstringKey)));
03584     lua_createtable(L, 0, 4);
03585     lua_pushcfunction(L, impl_tstring_concat);
03586     lua_setfield(L, -2, "__concat");
03587     lua_pushcfunction(L, impl_tstring_collect);
03588     lua_setfield(L, -2, "__gc");
03589     lua_pushcfunction(L, impl_tstring_tostring);
03590     lua_setfield(L, -2, "__tostring");
03591     lua_pushstring(L, "translatable string");
03592     lua_setfield(L, -2, "__metatable");
03593     lua_rawset(L, LUA_REGISTRYINDEX);
03594 
03595     // Create the unit status metatable.
03596     lua_pushlightuserdata(L
03597             , static_cast<void *>(const_cast<char *>(&ustatusKey)));
03598     lua_createtable(L, 0, 3);
03599     lua_pushcfunction(L, impl_unit_status_get);
03600     lua_setfield(L, -2, "__index");
03601     lua_pushcfunction(L, impl_unit_status_set);
03602     lua_setfield(L, -2, "__newindex");
03603     lua_pushstring(L, "unit status");
03604     lua_setfield(L, -2, "__metatable");
03605     lua_rawset(L, LUA_REGISTRYINDEX);
03606 
03607     // Create the unit variables metatable.
03608     lua_pushlightuserdata(L
03609             , static_cast<void *>(const_cast<char *>(&unitvarKey)));
03610     lua_createtable(L, 0, 3);
03611     lua_pushcfunction(L, impl_unit_variables_get);
03612     lua_setfield(L, -2, "__index");
03613     lua_pushcfunction(L, impl_unit_variables_set);
03614     lua_setfield(L, -2, "__newindex");
03615     lua_pushstring(L, "unit variables");
03616     lua_setfield(L, -2, "__metatable");
03617     lua_rawset(L, LUA_REGISTRYINDEX);
03618 
03619     // Create the vconfig metatable.
03620     lua_pushlightuserdata(L
03621             , static_cast<void *>(const_cast<char *>(&vconfigKey)));
03622     lua_createtable(L, 0, 4);
03623     lua_pushcfunction(L, impl_vconfig_collect);
03624     lua_setfield(L, -2, "__gc");
03625     lua_pushcfunction(L, impl_vconfig_get);
03626     lua_setfield(L, -2, "__index");
03627     lua_pushcfunction(L, impl_vconfig_size);
03628     lua_setfield(L, -2, "__len");
03629     lua_pushstring(L, "wml object");
03630     lua_setfield(L, -2, "__metatable");
03631     lua_rawset(L, LUA_REGISTRYINDEX);
03632 
03633     // Create the ai elements table.
03634     ai::lua_ai_context::init(L);
03635 
03636     // Delete dofile and loadfile.
03637     lua_pushnil(L);
03638     lua_setglobal(L, "dofile");
03639     lua_pushnil(L);
03640     lua_setglobal(L, "loadfile");
03641 
03642     // Create the game_config variable with its metatable.
03643     lua_getglobal(L, "wesnoth");
03644     lua_newuserdata(L, 0);
03645     lua_createtable(L, 0, 3);
03646     lua_pushcfunction(L, impl_game_config_get);
03647     lua_setfield(L, -2, "__index");
03648     lua_pushcfunction(L, impl_game_config_set);
03649     lua_setfield(L, -2, "__newindex");
03650     lua_pushstring(L, "game config");
03651     lua_setfield(L, -2, "__metatable");
03652     lua_setmetatable(L, -2);
03653     lua_setfield(L, -2, "game_config");
03654     lua_pop(L, 1);
03655 
03656     // Create the current variable with its metatable.
03657     lua_getglobal(L, "wesnoth");
03658     lua_newuserdata(L, 0);
03659     lua_createtable(L, 0, 2);
03660     lua_pushcfunction(L, impl_current_get);
03661     lua_setfield(L, -2, "__index");
03662     lua_pushstring(L, "current config");
03663     lua_setfield(L, -2, "__metatable");
03664     lua_setmetatable(L, -2);
03665     lua_setfield(L, -2, "current");
03666     lua_pop(L, 1);
03667 
03668     // Create the package table.
03669     lua_getglobal(L, "wesnoth");
03670     lua_newtable(L);
03671     lua_setfield(L, -2, "package");
03672     lua_pop(L, 1);
03673 
03674     // Create the wml_actions table.
03675     lua_getglobal(L, "wesnoth");
03676     lua_newtable(L);
03677     lua_setfield(L, -2, "wml_actions");
03678     lua_pop(L, 1);
03679 
03680     // Create the game_events table.
03681     lua_getglobal(L, "wesnoth");
03682     lua_newtable(L);
03683     lua_setfield(L, -2, "game_events");
03684     lua_pop(L, 1);
03685 
03686     // Create the theme_items table.
03687     lua_getglobal(L, "wesnoth");
03688     lua_newtable(L);
03689     lua_createtable(L, 0, 2);
03690     lua_pushcfunction(L, impl_theme_items_get);
03691     lua_setfield(L, -2, "__index");
03692     lua_pushcfunction(L, impl_theme_items_set);
03693     lua_setfield(L, -2, "__newindex");
03694     lua_setmetatable(L, -2);
03695     lua_setfield(L, -2, "theme_items");
03696     lua_pop(L, 1);
03697 
03698     // Store the error handler.
03699     lua_pushlightuserdata(L
03700             , static_cast<void *>(const_cast<char *>(&executeKey)));
03701     lua_getglobal(L, "debug");
03702     lua_getfield(L, -1, "traceback");
03703     lua_remove(L, -2);
03704     lua_rawset(L, LUA_REGISTRYINDEX);
03705 
03706     // Disable functions from os which we don't want.
03707     lua_getglobal(L, "os");
03708     lua_pushnil(L);
03709     while(lua_next(L, -2) != 0) {
03710         lua_pop(L, 1);
03711         char const* function = lua_tostring(L, -1);
03712         if(strcmp(function, "clock") == 0 || strcmp(function, "date") == 0
03713             || strcmp(function, "time") == 0 || strcmp(function, "difftime") == 0) continue;
03714         lua_pushnil(L);
03715         lua_setfield(L, -3, function);
03716     }
03717     lua_pop(L, 1);
03718 
03719     // Disable functions from debug which we don't want.
03720     lua_getglobal(L, "debug");
03721     lua_pushnil(L);
03722     while(lua_next(L, -2) != 0) {
03723         lua_pop(L, 1);
03724         char const* function = lua_tostring(L, -1);
03725         if(strcmp(function, "traceback") == 0) continue;
03726         lua_pushnil(L);
03727         lua_setfield(L, -3, function);
03728     }
03729     lua_pop(L, 1);
03730 
03731     lua_settop(L, 0);
03732 }
03733 
03734 void LuaKernel::initialize()
03735 {
03736     lua_State *L = mState;
03737 
03738     // Create the sides table.
03739     // note:
03740     // This table is redundant to the return value of wesnoth.get_sides({}).
03741     // Still needed for backwards compatibility.
03742     lua_getglobal(L, "wesnoth");
03743     std::vector<team> &teams = *resources::teams;
03744     lua_pushlightuserdata(L
03745             , static_cast<void *>(const_cast<char *>(&getsideKey)));
03746     lua_rawget(L, LUA_REGISTRYINDEX);
03747     lua_createtable(L, teams.size(), 0);
03748     for (unsigned i = 0; i != teams.size(); ++i)
03749     {
03750         // Create a full userdata containing a pointer to the team.
03751         team **p = static_cast<team **>(lua_newuserdata(L, sizeof(team *)));
03752         *p = &teams[i];
03753         lua_pushvalue(L, -3);
03754         lua_setmetatable(L, -2);
03755         lua_rawseti(L, -2, i + 1);
03756     }
03757     lua_setfield(L, -3, "sides");
03758     lua_pop(L, 2);
03759 
03760     // Create the unit_types table.
03761     lua_getglobal(L, "wesnoth");
03762     lua_pushlightuserdata(L
03763             , static_cast<void *>(const_cast<char *>(&gettypeKey)));
03764     lua_rawget(L, LUA_REGISTRYINDEX);
03765     lua_newtable(L);
03766     foreach (const unit_type_data::unit_type_map::value_type &ut, unit_types.types())
03767     {
03768         lua_createtable(L, 0, 1);
03769         lua_pushstring(L, ut.first.c_str());
03770         lua_setfield(L, -2, "id");
03771         lua_pushvalue(L, -3);
03772         lua_setmetatable(L, -2);
03773         lua_setfield(L, -2, ut.first.c_str());
03774     }
03775     lua_setfield(L, -3, "unit_types");
03776     lua_pop(L, 2);
03777 
03778     //Create the races table.
03779     lua_getglobal(L, "wesnoth");
03780     lua_pushlightuserdata(L
03781             , static_cast<void *>(const_cast<char *>(&getraceKey)));
03782     lua_rawget(L, LUA_REGISTRYINDEX);
03783     const race_map& races = unit_types.races();
03784     lua_createtable(L, 0, races.size());
03785     foreach(const race_map::value_type &race, races)
03786     {
03787         lua_createtable(L, 0, 1);
03788         char const* id = race.first.c_str();
03789         lua_pushstring(L, id);
03790         lua_setfield(L, -2, "id");
03791         lua_pushvalue(L, -3);
03792         lua_setmetatable(L, -2);
03793         lua_setfield(L, -2, id);
03794     }
03795     lua_setfield(L, -3, "races");
03796     lua_pop(L, 2);
03797 
03798     // Execute the preload scripts.
03799     game_config::load_config(preload_config);
03800     foreach (const config &cfg, preload_scripts) {
03801         execute(cfg["code"].str().c_str(), 0, 0);
03802     }
03803     foreach (const config &cfg, level_.child_range("lua")) {
03804         execute(cfg["code"].str().c_str(), 0, 0);
03805     }
03806 
03807     load_game();
03808 }
03809 
03810 static char const *handled_file_tags[] = {
03811     "color_palette", "color_range", "era", "event", "generator",
03812     "label", "lua", "map", "menu_item", "music", "side", "sound_source", "story",
03813     "terrain_graphics", "time", "time_area", "tunnel", "variables", "endlevel",
03814     "display",
03815     //TODO: These are only needed for MP campaigns and only for subsequent scenarios, see bug #18883
03816     "snapshot", "multiplayer", "replay_start"
03817 };
03818 
03819 static bool is_handled_file_tag(const std::string &s)
03820 {
03821     foreach (char const *t, handled_file_tags) {
03822         if (s == t) return true;
03823     }
03824     return false;
03825 }
03826 
03827 /**
03828  * Executes the game_events.on_load function and passes to it all the
03829  * scenario tags not yet handled.
03830  */
03831 void LuaKernel::load_game()
03832 {
03833     lua_State *L = mState;
03834 
03835     if (!luaW_getglobal(L, "wesnoth", "game_events", "on_load", NULL))
03836         return;
03837 
03838     lua_newtable(L);
03839     int k = 1;
03840     foreach (const config::any_child &v, level_.all_children_range())
03841     {
03842         if (is_handled_file_tag(v.key)) continue;
03843         lua_createtable(L, 2, 0);
03844         lua_pushstring(L, v.key.c_str());
03845         lua_rawseti(L, -2, 1);
03846         luaW_pushconfig(L, v.cfg);
03847         lua_rawseti(L, -2, 2);
03848         lua_rawseti(L, -2, k++);
03849     }
03850 
03851     luaW_pcall(L, 1, 0, true);
03852 }
03853 
03854 /**
03855  * Executes the game_events.on_save function and adds to @a cfg the
03856  * returned tags. Also flushes the [lua] tags.
03857  */
03858 void LuaKernel::save_game(config &cfg)
03859 {
03860     foreach (const config &v, level_.child_range("lua")) {
03861         cfg.add_child("lua", v);
03862     }
03863 
03864     lua_State *L = mState;
03865 
03866     if (!luaW_getglobal(L, "wesnoth", "game_events", "on_save", NULL))
03867         return;
03868 
03869     if (!luaW_pcall(L, 0, 1, false))
03870         return;
03871 
03872     config v;
03873     luaW_toconfig(L, -1, v);
03874     lua_pop(L, 1);
03875 
03876     for (;;)
03877     {
03878         config::all_children_iterator i = v.ordered_begin();
03879         if (i == v.ordered_end()) break;
03880         if (is_handled_file_tag(i->key))
03881         {
03882             /*
03883              * It seems the only tags appearing in the config v variable here
03884              * are the core-lua-handled (currently [item] and [objectives])
03885              * and the extra UMC ones.
03886              */
03887             const std::string m = "Tag is already used: [" + i->key + "]";
03888             chat_message("Lua error", m);
03889             ERR_LUA << m << '\n';
03890             v.erase(i);
03891             continue;
03892         }
03893         cfg.splice_children(v, i->key);
03894     }
03895 }
03896 
03897 /**
03898  * Executes the game_events.on_event function.
03899  * Returns false if there was no lua handler for this event
03900  */
03901 bool LuaKernel::run_event(game_events::queued_event const &ev)
03902 {
03903     lua_State *L = mState;
03904 
03905     if (!luaW_getglobal(L, "wesnoth", "game_events", "on_event", NULL))
03906         return false;
03907 
03908     queued_event_context dummy(&ev);
03909     lua_pushstring(L, ev.name.c_str());
03910     luaW_pcall(L, 1, 0, false);
03911     return true;
03912 }
03913 
03914 LuaKernel::~LuaKernel()
03915 {
03916     lua_close(mState);
03917 }
03918 
03919 /**
03920  * Executes its upvalue as a wml action.
03921  */
03922 static int cfun_wml_action(lua_State *L)
03923 {
03924     game_events::action_handler h = reinterpret_cast<game_events::action_handler>
03925         (lua_touserdata(L, lua_upvalueindex(1)));
03926 
03927     vconfig vcfg = luaW_checkvconfig(L, 1);
03928     h(queued_event_context::get(), vcfg);
03929     return 0;
03930 }
03931 
03932 /**
03933  * Registers a function for use as an action handler.
03934  */
03935 void LuaKernel::set_wml_action(std::string const &cmd, game_events::action_handler h)
03936 {
03937     lua_State *L = mState;
03938 
03939     lua_getglobal(L, "wesnoth");
03940     lua_pushstring(L, "wml_actions");
03941     lua_rawget(L, -2);
03942     lua_pushstring(L, cmd.c_str());
03943     lua_pushlightuserdata(L, reinterpret_cast<void *>(h));
03944     lua_pushcclosure(L, cfun_wml_action, 1);
03945     lua_rawset(L, -3);
03946     lua_pop(L, 2);
03947 }
03948 
03949 /**
03950  * Runs a command from an event handler.
03951  * @return true if there is a handler for the command.
03952  * @note @a cfg should be either volatile or long-lived since the Lua
03953  *       code may grab it for an arbitrary long time.
03954  */
03955 bool LuaKernel::run_wml_action(std::string const &cmd, vconfig const &cfg,
03956     game_events::queued_event const &ev)
03957 {
03958     lua_State *L = mState;
03959 
03960 
03961     if (!luaW_getglobal(L, "wesnoth", "wml_actions", cmd.c_str(), NULL))
03962         return false;
03963 
03964     queued_event_context dummy(&ev);
03965     luaW_pushvconfig(L, cfg);
03966     luaW_pcall(L, 1, 0, true);
03967     return true;
03968 }
03969 
03970 
03971 /**
03972  * Runs a script from a unit filter.
03973  * The script is an already compiled function given by its name.
03974  */
03975 bool LuaKernel::run_filter(char const *name, unit const &u)
03976 {
03977     lua_State *L = mState;
03978 
03979     unit_map::const_unit_iterator ui = resources::units->find(u.get_location());
03980     if (!ui.valid()) return false;
03981 
03982     // Get the user filter by name.
03983     luaW_getglobal(L, name, NULL);
03984 
03985     // Pass the unit as argument.
03986     new(lua_newuserdata(L, sizeof(lua_unit))) lua_unit(ui->underlying_id());
03987     lua_pushlightuserdata(L
03988             , static_cast<void *>(const_cast<char *>(&getunitKey)));
03989     lua_rawget(L, LUA_REGISTRYINDEX);
03990     lua_setmetatable(L, -2);
03991 
03992     if (!luaW_pcall(L, 1, 1)) return false;
03993 
03994     bool b = lua_toboolean(L, -1);
03995     lua_pop(L, 1);
03996     return b;
03997 }
03998 
03999 /**
04000  * Runs a script on a stack containing @a nArgs arguments.
04001  * @return true if the script was successful and @a nRets return values are available.
04002  */
04003 bool LuaKernel::execute(char const *prog, int nArgs, int nRets)
04004 {
04005     lua_State *L = mState;
04006 
04007     // Compile script into a variadic function.
04008     int res = luaL_loadstring(L, prog);
04009     if (res)
04010     {
04011         char const *m = lua_tostring(L, -1);
04012         chat_message("Lua error", m);
04013         ERR_LUA << m << '\n';
04014         lua_pop(L, 1);
04015         return false;
04016     }
04017 
04018     // Place the function before its arguments.
04019     if (nArgs)
04020         lua_insert(L, -1 - nArgs);
04021 
04022     return luaW_pcall(L, nArgs, nRets);
04023 }
04024 
04025 /**
04026  * Loads the "package" package into the Lua environment.
04027  * This action is inherently unsafe, as Lua scripts will now be able to
04028  * load C libraries on their own, hence granting them the same privileges
04029  * as the Wesnoth binary itsef.
04030  */
04031 void LuaKernel::load_package()
04032 {
04033     lua_State *L = mState;
04034     lua_pushcfunction(L, luaopen_package);
04035     lua_pushstring(L, "package");
04036     lua_call(L, 1, 0);
04037 }
04038 
04039 ai::lua_ai_context* LuaKernel::create_lua_ai_context(char const *code, ai::engine_lua *engine)
04040 {
04041     return ai::lua_ai_context::create(mState,code,engine);
04042 }
04043 
04044 ai::lua_ai_action_handler* LuaKernel::create_lua_ai_action_handler(char const *code, ai::lua_ai_context &context)
04045 {
04046     return ai::lua_ai_action_handler::create(mState,code,context);
04047 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Fri May 25 2012 01:03:09 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs