114 #include <functional>
126 #include <SDL2/SDL_timer.h>
133 #define DBG_LUA LOG_STREAM(debug, log_scripting_lua)
134 #define LOG_LUA LOG_STREAM(info, log_scripting_lua)
135 #define WRN_LUA LOG_STREAM(warn, log_scripting_lua)
136 #define ERR_LUA LOG_STREAM(err, log_scripting_lua)
139 #define ERR_WML LOG_STREAM(err, log_wml)
147 template <member_callback method>
149 return ((lua_kernel_base::get_lua_kernel<game_lua_kernel>(L)).*method)(L);
155 template <member_callback2 method,
bool b>
157 return ((lua_kernel_base::get_lua_kernel<game_lua_kernel>(L)).*method)(L,
b);
207 if(!sides.
empty()) {
WRN_LUA <<
"ignoring duplicate side filter information (inline side=)"; }
220 struct queued_event_context
223 std::stack<qe const *> & stack_;
225 queued_event_context(qe
const *new_qe, std::stack<qe const*> & stack)
231 ~queued_event_context()
246 lua_pushinteger(L, disp->viewing_team().side());
247 lua_pushboolean(L, disp->show_everything());
265 anim.~unit_animator();
274 std::string which = luaL_checkstring(L, 3);
276 std::string hits_str = luaL_checkstring(L, 4);
286 if(lua_istable(L, 5)) {
287 lua_getfield(L, 5,
"target");
290 return luaL_argerror(L, 5,
"target location must be different from animated unit's location");
292 return luaL_argerror(L, 5,
"target location must be adjacent to the animated unit");
297 if(!lua_isnoneornil(L, -1)) {
303 lua_getfield(L, 5,
"value");
304 if(lua_isnumber(L, -1)) {
305 v1 = lua_tointeger(L, -1);
306 }
else if(lua_istable(L, -1)) {
307 lua_rawgeti(L, -1, 1);
308 v1 = lua_tointeger(L, -1);
310 lua_rawgeti(L, -1, 2);
311 v2 = lua_tointeger(L, -1);
313 }
else if(!lua_isnoneornil(L, -1)) {
314 return luaW_type_error(L, 5,
"value",
"number or array of two numbers");
318 lua_getfield(L, 5,
"with_bars");
319 if(lua_isboolean(L, -1)) {
321 }
else if(!lua_isnoneornil(L, -1)) {
322 return luaW_type_error(L, 5,
"with_bars", lua_typename(L, LUA_TBOOLEAN));
326 lua_getfield(L, 5,
"text");
327 if(lua_isstring(L, -1)) {
328 text = lua_tostring(L, -1);
331 }
else if(!lua_isnoneornil(L, -1)) {
336 lua_getfield(L, 5,
"color");
337 if(lua_istable(L, -1) && lua_rawlen(L, -1) == 3) {
338 int idx = lua_absindex(L, -1);
339 lua_rawgeti(L, idx, 1);
340 lua_rawgeti(L, idx, 2);
341 lua_rawgeti(L, idx, 3);
342 color =
color_t(lua_tointeger(L, -3), lua_tointeger(L, -2), lua_tointeger(L, -1));
344 }
else if(!lua_isnoneornil(L, -1)) {
349 lua_getfield(L, 5,
"primary");
351 if(!primary && !lua_isnoneornil(L, -1)) {
356 lua_getfield(L, 5,
"secondary");
358 if(!secondary && !lua_isnoneornil(L, -1)) {
362 }
else if(!lua_isnoneornil(L, 5)) {
366 anim.
add_animation(up, which, u.
get_location(), dest, v1, bars, text, color, hits, primary, secondary, v2);
394 const char* m = lua_tostring(L, 2);
402 luaL_Reg metafuncs[] {
406 {
"run", &dispatch<&game_lua_kernel::impl_run_animation>},
410 luaL_setfuncs(L, metafuncs, 0);
411 lua_pushstring(L,
"__metatable");
414 lua_setmetatable(L, -2);
424 name = cfg[
"name"].str();
427 name = luaL_optstring(L, 1,
"");
445 if(lua_isstring(L, 1) && !lua_isnumber(L, 1)) {
446 std::string
id = luaL_checkstring(L, 1);
456 return luaL_argerror(L, 1,
"expected string or location");
460 if (!ui.
valid())
return 0;
481 if (!ui.
valid())
return 0;
499 std::vector<const unit*>
units;
503 return luaL_argerror(L, 2,
"unit not found");
506 }
else if(!lua_isnoneornil(L, 2)) {
510 return luaL_argerror(L, 2,
"invalid location");
525 lua_rawseti(L, 1,
i);
545 lua_pushboolean(L,
true);
551 WRN_LUA <<
"wesnoth.units.matches called with a secondary unit (3rd argument), ";
552 WRN_LUA <<
"but unit to match was on recall list. ";
553 WRN_LUA <<
"Thus the 3rd argument is ignored.";
560 return luaL_argerror(L, 3,
"unit not found");
562 lua_pushboolean(L,
unit_filter(filter).matches(*u, *u_adj));
568 lua_pushboolean(L,
unit_filter(filter).matches(*u, loc));
573 lua_pushboolean(L,
unit_filter(filter).matches(*u, loc));
598 if (!filter.
null()) {
600 t.save_id_or_number(),
t.recall_list().find_index(u->id()));
605 lua_rawseti(L, 1,
i);
624 char const *m = luaL_checkstring(L, 1);
641 if(
data.has_child(
"primary_attack")) {
642 data.add_child(
"first",
data.mandatory_child(
"primary_attack"));
643 data.remove_children(
"primary_attack");
645 if(
data.has_child(
"secondary_attack")) {
646 data.add_child(
"second",
data.mandatory_child(
"secondary_attack"));
647 data.remove_children(
"secondary_attack");
658 lua_pushboolean(L,
b);
676 char const *m = luaL_checkstring(L, 1);
681 lua_pushboolean(L,
b);
693 char const *m = luaL_checkstring(L, 1);
705 const std::string m = luaL_checkstring(L, 1);
706 if(m.empty())
return luaL_argerror(L, 1,
"empty variable name");
707 if (lua_isnoneornil(L, 2)) {
720 cfg[
"side"] =
teams().size() + 1;
735 std::string ids(luaL_checkstring(L, 1));
738 WRN_LUA <<
"[clear_menu_item] has been given an empty id=, ignoring";
755 if(lua_istable(L, 2)) {
767 return luaL_argerror(L, 2,
"expected list of locations");
786 if(lua_istable(L, 2)) {
814 if(!
map().on_board(loc))
return luaL_argerror(L, 1,
"not on board");
828 unsigned side_1, side_2;
832 side_1 = luaL_checkinteger(L, 1);
837 side_2 = luaL_checkinteger(L, 2);
840 lua_pushboolean(L,
board().get_team(side_1).is_enemy(side_2));
874 lua_pushstring(L, tod.
id.c_str());
875 lua_setfield(L, -2,
"id");
877 lua_setfield(L, -2,
"lawful_bonus");
879 lua_setfield(L, -2,
"bonus_modified");
880 lua_pushstring(L, tod.
image.c_str());
881 lua_setfield(L, -2,
"image");
883 lua_setfield(L, -2,
"name");
884 lua_pushstring(L, tod.
sounds.c_str());
885 lua_setfield(L, -2,
"sound");
887 lua_setfield(L, -2,
"mask");
889 lua_pushinteger(L, tod.
color.
r);
890 lua_setfield(L, -2,
"red");
891 lua_pushinteger(L, tod.
color.
g);
892 lua_setfield(L, -2,
"green");
893 lua_pushinteger(L, tod.
color.
b);
894 lua_setfield(L, -2,
"blue");
901 lua_newuserdatauv(L, 0, 1);
902 lua_pushinteger(L, area_index);
903 lua_setiuservalue(L, -2, 1);
904 if(luaL_newmetatable(L,
"schedule")) {
905 static luaL_Reg
const schedule_meta[] {
906 {
"__index", &dispatch<&game_lua_kernel::impl_schedule_get>},
907 {
"__newindex", &dispatch<&game_lua_kernel::impl_schedule_set>},
908 {
"__len", &dispatch<&game_lua_kernel::impl_schedule_len>},
911 luaL_setfuncs(L, schedule_meta, 0);
913 lua_setmetatable(L, -2);
918 int save_top = lua_gettop(L);
919 luaL_checkudata(L, idx,
"schedule");
920 lua_getiuservalue(L, idx, 1);
921 int i = luaL_checkinteger(L, -1);
922 lua_settop(L, save_top);
929 if(lua_isnumber(L, 2)) {
931 int i = lua_tointeger(L, 2) - 1;
932 if(i < 0 || i >=
static_cast<int>(times.size())) {
933 return luaL_argerror(L, 2,
"invalid time of day index");
938 const char* m = luaL_checkstring(L, 2);
939 if(area_index >= 0) {
942 if(strcmp(m,
"hexes") == 0) {
963 lua_pushinteger(L, times.size());
970 if(lua_isnumber(L, 2)) {
972 int i = lua_tointeger(L, 2) - 1;
973 if(i < 0 || i >=
static_cast<int>(times.size())) {
974 return luaL_argerror(L, 2,
"invalid time of day index");
984 const char* m = luaL_checkstring(L, 2);
985 if(strcmp(m,
"time_of_day") == 0) {
986 std::string value = luaL_checkstring(L, 3);
988 auto iter = std::find_if(times.begin(), times.end(), [&value](
const time_of_day& tod) {
989 return tod.id == value;
991 if(iter == times.end()) {
992 std::ostringstream
err;
993 err <<
"invalid time of day ID for ";
995 err <<
"global schedule";
1001 err <<
"anonymous empty time area";
1003 err <<
"anonymous time area at (" << hexes.begin()->wml_x() <<
',' << hexes.begin()->wml_y() <<
")";
1006 err <<
"time area with id=" <<
id;
1010 return lua_error(L);
1012 int n = std::distance(times.begin(), iter);
1013 if(area_index < 0) {
1019 if(area_index >= 0) {
1021 if(strcmp(m,
"hexes") == 0) {
1028 if(lua_isnil(L, 3) && strcmp(m,
"liminal_bonus") == 0) {
1045 char const *m = luaL_checkstring(L, 2);
1051 lua_pushstring(L,
info.id().c_str());
1052 lua_setfield(L, -2,
"id");
1054 lua_setfield(L, -2,
"name");
1056 lua_setfield(L, -2,
"editor_name");
1058 lua_setfield(L, -2,
"description");
1060 lua_setfield(L, -2,
"icon");
1062 lua_setfield(L, -2,
"editor_image");
1063 lua_pushinteger(L,
info.light_bonus(0));
1064 lua_setfield(L, -2,
"light");
1065 lua_pushboolean(L,
info.is_village());
1066 lua_setfield(L, -2,
"village");
1067 lua_pushboolean(L,
info.is_castle());
1068 lua_setfield(L, -2,
"castle");
1069 lua_pushboolean(L,
info.is_keep());
1070 lua_setfield(L, -2,
"keep");
1071 lua_pushinteger(L,
info.gives_healing());
1072 lua_setfield(L, -2,
"healing");
1083 template<
bool cons
ider_illuminates>
1090 if(!
board().
map().on_board_with_border(loc)) {
1091 return luaL_argerror(L, 1,
"coordinates are not on board");
1093 }
else if(lua_isstring(L, 1)) {
1096 return luaL_error(L,
"invalid or empty time_area ID");
1099 loc = *area.begin();
1100 }
else if(!lua_isnil(L, 1)) {
1103 return luaL_error(L,
"empty time_area");
1106 loc = *area.begin();
1109 if(lua_isnumber(L, 2)) {
1110 for_turn = luaL_checkinteger(L, 2);
1112 if(for_turn < 1 || (number_of_turns != -1 && for_turn > number_of_turns)) {
1113 return luaL_argerror(L, 2,
"turn number out of range");
1134 if (!
board().
map().is_village(loc))
1138 if (!side)
return 0;
1139 lua_pushinteger(L, side);
1151 if(!
board().
map().is_village(loc)) {
1156 const int new_side_num = lua_isnoneornil(L, 2) ? 0 : luaL_checkinteger(L, 2);
1158 team* old_side =
nullptr;
1159 team* new_side =
nullptr;
1161 if(old_side_num == new_side_num) {
1167 }
catch(
const std::out_of_range&) {
1174 }
catch(
const std::out_of_range&) {
1180 if(new_side &&
board().team_is_defeated(*new_side)) {
1213 if (!
board().
map().on_board(loc))
return 0;
1214 lua_pushinteger(L, loc.
wml_x());
1215 lua_pushinteger(L, loc.
wml_y());
1231 if (!
board().
map().on_board(loc))
return 0;
1232 lua_pushinteger(L, loc.
wml_x());
1233 lua_pushinteger(L, loc.
wml_y());
1246 std::string m = luaL_checkstring(L, 1);
1252 return luaL_argerror(L, 1, (
"Cannot find resource with id '" + m +
"'").c_str());
1264 std::string m = luaL_checkstring(L, 1);
1270 return luaL_argerror(L, 1, (
"Cannot find era with id '" + m +
"'").c_str());
1283 DBG_LUA <<
"impl_game_config_get";
1284 char const *m = luaL_checkstring(L, 2);
1299 if(strcmp(m,
"global_traits") == 0) {
1302 const std::string&
id = trait[
"id"];
1306 lua_pushstring(L,
id.c_str());
1335 DBG_LUA <<
"impl_game_config_set";
1336 char const *m = luaL_checkstring(L, 2);
1360 static config find_addon(
const std::string&
type,
const std::string&
id)
1369 const char* m = luaL_checkstring(L, 2);
1387 struct end_level_committer {
1389 ~end_level_committer() {
1390 pc_.set_end_level_data(data_);
1401 const char* m = luaL_checkstring(L, 2);
1417 data->~end_level_data();
1423 void*
p = lua_touserdata(L, lua_upvalueindex(1));
1425 if(lua_type(L, 2) == LUA_TNUMBER) {
1428 size_t i = luaL_checkinteger(L, 2);
1430 lua_createtable(L, 2, 0);
1431 lua_pushstring(L,
"options");
1439 auto iter =
settings.addons.begin();
1440 std::advance(iter,
i);
1442 iter->second.write(cfg);
1443 cfg[
"id"] = iter->first;
1445 lua_createtable(L, 2, 0);
1446 lua_pushstring(L,
"addon");
1454 char const *m = luaL_checkstring(L, 2);
1482 if(strcmp(m,
"savegame") == 0) {
1484 if(
savegame == saved_game_mode::type::no) {
1485 lua_pushboolean(L,
false);
1491 if(strcmp(m,
"side_players") == 0) {
1495 if(strcmp(m,
"addons") == 0) {
1496 for(
const auto& [
id, addon] :
settings.addons) {
1497 lua_createtable(L, 0, 4);
1499 lua_setfield(L, -2,
"id");
1501 lua_setfield(L, -2,
"name");
1502 lua_pushboolean(L, addon.required);
1503 lua_setfield(L, -2,
"required");
1504 if(addon.min_version) {
1506 lua_push(L, addon.min_version->str());
1508 lua_setfield(L, -2,
"min_version");
1514 lua_setfield(L, -2,
"version");
1516 lua_createtable(L, addon.content.size(), 0);
1517 for(
const auto& content : addon.content) {
1518 lua_createtable(L, 0, 3);
1520 lua_setfield(L, -2,
"id");
1522 lua_setfield(L, -2,
"name");
1524 lua_setfield(L, -2,
"type");
1525 lua_seti(L, -2, lua_rawlen(L, -2) + 1);
1527 lua_setfield(L, -2,
"content");
1543 void*
p = lua_touserdata(L, lua_upvalueindex(1));
1545 lua_pushinteger(L,
settings.addons.size() + 1);
1557 DBG_LUA <<
"impl_scenario_get";
1558 char const *m = luaL_checkstring(L, 2);
1567 if(strcmp(m,
"resources") == 0) {
1570 resources.push_back(find_addon(
"resource", rsrc));
1585 if(strcmp(m,
"modifications") == 0) {
1586 std::vector<config> mods;
1588 mods.push_back(find_addon(
"modification", mod));
1593 if(strcmp(m,
"end_level_data") == 0) {
1599 if(luaL_newmetatable(L,
"end level data")) {
1600 static luaL_Reg
const callbacks[] {
1602 {
"__newindex", &dispatch<&game_lua_kernel::impl_end_level_data_set>},
1604 {
nullptr,
nullptr }
1606 luaL_setfuncs(L, callbacks, 0);
1608 lua_setmetatable(L, -2);
1614 if(strcmp(m,
"mp_settings") == 0) {
1615 lua_newuserdatauv(L, 0, 0);
1616 if(luaL_newmetatable(L,
"mp settings")) {
1619 lua_setfield(L, -2,
"__index");
1622 lua_setfield(L, -2,
"__len");
1623 lua_pushstring(L,
"mp settings");
1624 lua_setfield(L, -2,
"__metatable");
1626 lua_setmetatable(L, -2);
1642 DBG_LUA <<
"impl_scenario_set";
1643 char const *m = luaL_checkstring(L, 2);
1655 if(strcmp(m,
"end_level_data") == 0) {
1659 data.proceed_to_next_level = cfg[
"proceed_to_next_level"].to_bool(
true);
1660 data.transient.carryover_report = cfg[
"carryover_report"].to_bool(
true);
1661 data.prescenario_save = cfg[
"save"].to_bool(
true);
1662 data.replay_save = cfg[
"replay_save"].to_bool(
true);
1663 data.transient.linger_mode = cfg[
"linger_mode"].to_bool(
true) && !
teams().empty();
1665 data.is_victory = cfg[
"result"] == level_result::victory;
1666 data.test_result = cfg[
"test_result"].str();
1687 return "local_choice";
1706 char const *m = luaL_checkstring(L, 2);
1714 if(strcmp(m,
"map") == 0) {
1717 if(strcmp(m,
"schedule") == 0) {
1722 if (strcmp(m,
"event_context") == 0)
1726 cfg[
"name"] = ev.
name;
1733 cfg.
add_child(
"second_weapon", *weapon);
1738 cfg[
"damage_inflicted"] = di;
1768 if (lua_isnone(L, 2)) {
1774 LOG_LUA <<
"Script says: \"" << m <<
"\"";
1783 double factor = luaL_checknumber(L, 1);
1810 if (!lua_isnoneornil(L, 1)) {
1811 int max = 2 *
teams().size();
1812 int npn = luaL_checkinteger(L, 1);
1813 if (npn <= 0 || npn > max) {
1814 return luaL_argerror(L, 1,
"side number out of range");
1831 lua_pushboolean(L,
b);
1849 const unit* u =
nullptr;
1850 int viewing_side = 0;
1852 if (lua_isuserdata(L, arg))
1856 viewing_side = u->
side();
1865 viewing_side = u->
side();
1873 return luaL_argerror(L, 1,
"invalid location");
1875 return luaL_argerror(L, arg,
"invalid location");
1879 bool ignore_units =
false, see_all =
false, ignore_teleport =
false;
1880 double stop_at = 10000;
1881 std::unique_ptr<pathfind::cost_calculator> calc;
1883 if (lua_istable(L, arg))
1885 ignore_units = luaW_table_get_def<bool>(L, arg,
"ignore_units",
false);
1886 see_all = luaW_table_get_def<bool>(L, arg,
"ignore_visibility",
false);
1887 ignore_teleport = luaW_table_get_def<bool>(L, arg,
"ignore_teleport",
false);
1889 stop_at = luaW_table_get_def<double>(L, arg,
"max_cost", stop_at);
1892 lua_pushstring(L,
"viewing_side");
1894 if (!lua_isnil(L, -1)) {
1895 int i = luaL_checkinteger(L, -1);
1896 if (
i >= 1 &&
i <=
static_cast<int>(
teams().
size())) viewing_side =
i;
1900 if(u) see_all =
true;
1901 deprecated_message(
"wesnoth.paths.find_path with viewing_side=0 (or an invalid side)",
DEP_LEVEL::FOR_REMOVAL, {1, 17, 0},
"To consider fogged and hidden units, use ignore_visibility=true instead.");
1906 lua_pushstring(L,
"calculate");
1908 if(lua_isfunction(L, -1)) {
1913 else if (lua_isfunction(L, arg))
1921 if(!ignore_teleport) {
1922 if(viewing_side == 0) {
1923 lua_warning(L,
"wesnoth.paths.find_path: ignore_teleport=false requires a valid viewing_side; continuing with ignore_teleport=true",
false);
1924 ignore_teleport =
true;
1932 return luaL_argerror(L, 1,
"unit not found OR custom cost function not provided");
1936 teams(),
map, ignore_units,
false, see_all));
1940 &teleport_locations);
1942 int nb = res.
steps.size();
1943 lua_createtable(L, nb, 0);
1944 for (
int i = 0;
i < nb; ++
i)
1947 lua_rawseti(L, -2,
i + 1);
1963 const unit* u =
nullptr;
1965 if (lua_isuserdata(L, arg))
1975 return luaL_argerror(L, 1,
"unit not found");
1980 int viewing_side = u->
side();
1981 bool ignore_units =
false, see_all =
false, ignore_teleport =
false;
1982 int additional_turns = 0;
1984 if (lua_istable(L, arg))
1986 ignore_units = luaW_table_get_def<bool>(L, arg,
"ignore_units",
false);
1987 see_all = luaW_table_get_def<bool>(L, arg,
"ignore_visibility",
false);
1988 ignore_teleport = luaW_table_get_def<bool>(L, arg,
"ignore_teleport",
false);
1989 additional_turns = luaW_table_get_def<int>(L, arg,
"max_cost", additional_turns);
1991 lua_pushstring(L,
"viewing_side");
1993 if (!lua_isnil(L, -1)) {
1994 int i = luaL_checkinteger(L, -1);
1995 if (
i >= 1 &&
i <=
static_cast<int>(
teams().
size())) viewing_side =
i;
1999 if(u) see_all =
true;
2009 viewing_team, additional_turns, see_all, ignore_units);
2012 lua_createtable(L, nb, 0);
2013 for (
int i = 0;
i < nb; ++
i)
2017 lua_pushinteger(L,
s.curr.wml_x());
2018 lua_rawseti(L, -2, 1);
2019 lua_pushinteger(L,
s.curr.wml_y());
2020 lua_rawseti(L, -2, 2);
2021 lua_pushinteger(L,
s.move_left);
2022 lua_rawseti(L, -2, 3);
2023 lua_rawseti(L, -2,
i + 1);
2038 const unit* u =
nullptr;
2040 if (lua_isuserdata(L, arg))
2050 return luaL_argerror(L, 1,
"unit not found");
2057 return luaL_error(L,
"wesnoth.find_vision_range: requires a valid unit");
2060 std::map<map_location, int> jamming_map;
2067 lua_pushinteger(L,
d.curr.wml_x());
2068 lua_rawseti(L, -2, 1);
2069 lua_pushinteger(L,
d.curr.wml_y());
2070 lua_rawseti(L, -2, 2);
2071 lua_pushinteger(L,
d.move_left);
2072 lua_rawseti(L, -2, 3);
2073 lua_rawseti(L, -2, lua_rawlen(L, -2) + 1);
2075 for(
const auto&
e : res.
edges) {
2077 lua_pushinteger(L,
e.wml_x());
2078 lua_rawseti(L, -2, 1);
2079 lua_pushinteger(L,
e.wml_y());
2080 lua_rawseti(L, -2, 2);
2081 lua_pushinteger(L, -1);
2082 lua_rawseti(L, -2, 3);
2083 lua_rawseti(L, -2, lua_rawlen(L, -2) + 1);
2088 template<
typename T>
2091 for (
int i = 1, i_end = lua_rawlen(L, arg);
i <= i_end; ++
i)
2094 lua_rawgeti(L, arg,
i);
2095 int entry = lua_gettop(L);
2096 if (!lua_istable(L, entry)) {
2104 lua_rawgeti(L, entry, 3);
2105 if (!lua_isnumber(L, -1)) {
2106 lua_getfield(L, entry,
"side");
2107 if (!lua_isnumber(L, -1)) {
2111 int side = lua_tointeger(L, -1);
2113 lua_rawgeti(L, entry, 4);
2114 if (!lua_isstring(L, -1)) {
2115 lua_getfield(L, entry,
"type");
2116 if (!lua_isstring(L, -1)) {
2120 std::string
unit_type = lua_tostring(L, -1);
2124 lua_settop(L, entry - 1);
2128 return luaL_argerror(L, arg,
"unit type table malformed - each entry should be either array of 4 elements or table with keys x, y, side, type");
2146 std::vector<const ::unit*> real_units;
2147 typedef std::vector<std::tuple<map_location, int, std::string>> unit_type_vector;
2153 real_units.push_back(
unit);
2155 else if (!filter.
null())
2157 for(const ::unit* match :
unit_filter(filter).all_matches_on_map()) {
2158 if(match->get_location().valid()) {
2159 real_units.push_back(match);
2169 real_units.push_back(&(*ui));
2174 if (lua_istable(L, arg))
2182 return luaL_argerror(L, 1,
"unit(s) not found");
2185 int viewing_side = 0;
2186 bool ignore_units =
true, see_all =
true, ignore_teleport =
false,
debug =
false, use_max_moves =
false;
2188 if (lua_istable(L, arg))
2190 lua_pushstring(L,
"ignore_units");
2192 if (!lua_isnil(L, -1))
2198 lua_pushstring(L,
"ignore_teleport");
2200 if (!lua_isnil(L, -1))
2206 lua_pushstring(L,
"viewing_side");
2208 if (!lua_isnil(L, -1))
2210 int i = luaL_checkinteger(L, -1);
2211 if (
i >= 1 &&
i <=
static_cast<int>(
teams().
size()))
2218 lua_pushstring(L,
"debug");
2220 if (!lua_isnil(L, -1))
2226 lua_pushstring(L,
"use_max_moves");
2228 if (!lua_isnil(L, -1))
2245 const terrain_filter t_filter(filter, &fc,
false);
2250 const team& viewing_team = viewing_side
2255 ignore_units, !ignore_teleport, viewing_team, see_all, ignore_units);
2257 for (const ::unit*
const u : real_units)
2259 cost_map.
add_unit(*u, use_max_moves);
2261 for (
const unit_type_vector::value_type& fu :
fake_units)
2264 cost_map.
add_unit(std::get<0>(fu), ut, std::get<1>(fu));
2273 std::stringstream
s;
2289 lua_pushinteger(L, loc.wml_x());
2290 lua_rawseti(L, -2, 1);
2292 lua_pushinteger(L, loc.wml_y());
2293 lua_rawseti(L, -2, 2);
2295 lua_pushinteger(L, cost_map.
get_pair_at(loc).first);
2296 lua_rawseti(L, -2, 3);
2298 lua_pushinteger(L, cost_map.
get_pair_at(loc).second);
2299 lua_rawseti(L, -2, 4);
2301 lua_rawseti(L, -2, counter);
2311 return reinterpret_cast<int*
>(luaL_checkudata(L, idx,
labelKey));
2316 const char* m = luaL_checkstring(L, 2);
2324 int fade = luaL_optinteger(L, 2, -1);
2362 double width_ratio = 0;
2364 int lifetime = 2'000, fadeout = 100;
2370 if(lua_istable(L, 2)) {
2372 size = luaL_checkinteger(L, -1);
2376 width = lua_tointegerx(L, -1, &found_number);
2380 if(!value.empty() && value.back() ==
'%') {
2381 value.remove_suffix(1);
2382 width_ratio = std::stoi(std::string(value)) / 100.0;
2383 }
else throw std::invalid_argument(value.data());
2384 }
catch(std::invalid_argument&) {
2385 return luaL_argerror(L, -1,
"max_width should be integer or percentage");
2391 if(lua_isstring(L, -1)) {
2394 auto vec = lua_check<std::vector<int>>(L, -1);
2395 if(vec.size() != 3) {
2396 int idx = lua_absindex(L, -1);
2398 color.r = luaL_checkinteger(L, -3);
2399 color.g = luaL_checkinteger(L, -2);
2400 color.b = luaL_checkinteger(L, -1);
2402 return luaL_error(L,
"floating label text color should be a hex string, an array of 3 integers, or a table with r,g,b keys");
2412 if(lua_isstring(L, -1)) {
2415 auto vec = lua_check<std::vector<int>>(L, -1);
2416 if(vec.size() != 3) {
2417 int idx = lua_absindex(L, -1);
2419 bgcolor.r = luaL_checkinteger(L, -3);
2420 bgcolor.g = luaL_checkinteger(L, -2);
2421 bgcolor.b = luaL_checkinteger(L, -1);
2423 return luaL_error(L,
"floating label background color should be a hex string, an array of 3 integers, or a table with r,g,b keys");
2433 bgcolor.a = luaL_checkinteger(L, -1);
2438 lifetime = lua_tointegerx(L, -1, &found_number);
2441 if(value ==
"unlimited") {
2444 return luaL_argerror(L, -1,
"duration should be integer or 'unlimited'");
2449 fadeout = lua_tointeger(L, -1);
2455 static const char* options[] = {
"left",
"center",
"right"};
2456 alignment =
font::ALIGN(luaL_checkoption(L, -1,
nullptr, options));
2459 static const char* options[] = {
"top",
"center",
"bottom"};
2460 vertical_alignment =
font::ALIGN(luaL_checkoption(L, -1,
nullptr, options));
2472 int handle_idx = lua_gettop(L);
2479 if(width_ratio > 0) {
2480 width =
static_cast<int>(std::round(
rect.w * width_ratio));
2485 x =
rect.x + loc.wml_x();
2491 x =
rect.x +
rect.w / 2 + loc.wml_x();
2505 switch(vertical_alignment) {
2507 y =
rect.y + loc.wml_y();
2510 y =
rect.y +
rect.h / 2 + loc.wml_y();
2517 y =
rect.y +
rect.h - loc.wml_y() -
static_cast<int>(
size * 1.5);
2531 lua_settop(L, handle_idx);
2532 if(luaL_newmetatable(L,
labelKey)) {
2534 static const luaL_Reg methods[] = {
2535 {
"remove", &dispatch<&game_lua_kernel::intf_remove_floating_label>},
2536 {
"move", &dispatch<&game_lua_kernel::intf_move_floating_label>},
2537 {
"replace", &dispatch2<&game_lua_kernel::intf_set_floating_label, false>},
2539 {
nullptr,
nullptr }
2541 luaL_setfuncs(L, methods, 0);
2544 lua_setmetatable(L, handle_idx);
2545 lua_settop(L, handle_idx);
2570 return luaL_error(L,
"Attempted to move a unit while the map is locked");
2575 if (!
map().on_board(loc)) {
2576 return luaL_argerror(L, 2,
"invalid location");
2587 if (!
map().on_board(loc))
2588 return luaL_argerror(L, 1,
"invalid location");
2594 }
else if(!lua_isnoneornil(L, 1)) {
2595 const vconfig* vcfg =
nullptr;
2597 if (!
map().on_board(loc)) {
2600 if (!
map().on_board(loc))
2601 return luaL_argerror(L, 2,
"invalid location");
2607 u->set_location(loc);
2626 return luaL_error(L,
"Attempted to remove a unit while the map is locked");
2634 if (!
map().on_board(loc)) {
2635 return luaL_argerror(L, 1,
"invalid location");
2640 t.recall_list().erase_if_matches_id(u->
id());
2642 return luaL_argerror(L, 1,
"can't erase private units");
2645 if (!
map().on_board(loc)) {
2646 return luaL_argerror(L, 1,
"invalid location");
2649 return luaL_argerror(L, 1,
"expected unit or location");
2665 return luaL_error(L,
"Attempted to move a unit while the map is locked");
2669 int side = lua_tointeger(L, 2);
2670 if (
static_cast<unsigned>(side) >
teams().
size()) side = 0;
2674 u = lu->get_shared();
2675 if(lu->on_recall_list() && lu->on_recall_list() == side) {
2676 return luaL_argerror(L, 1,
"unit already on recall list");
2679 const vconfig* vcfg =
nullptr;
2691 std::size_t uid = u->underlying_id();
2692 t.recall_list().erase_by_underlying_id(uid);
2693 t.recall_list().add(u);
2698 u->anim_comp().clear_haloes();
2700 lu->lua_unit::~lua_unit();
2714 return luaL_error(L,
"Attempted to remove a unit while the map is locked");
2722 u->anim_comp().clear_haloes();
2723 }
else if (
int side = lu->on_recall_list()) {
2726 t.recall_list().erase_if_matches_id(u->id());
2732 lu->lua_unit::~lua_unit();
2748 if (!lua_isnoneornil(L, 2)) {
2752 const vconfig* vcfg =
nullptr;
2760 if (!res.
valid())
return 0;
2761 lua_pushinteger(L, res.
wml_x());
2762 lua_pushinteger(L, res.
wml_y());
2778 if (!lua_isnoneornil(L, 3)) {
2795 const vconfig* vcfg =
nullptr;
2825 char const *m = luaL_checkstring(L, 2);
2829 if(lua_isboolean(L, 3)) {
2831 if(!lua_isnoneornil(L, 4)) {
2834 }
else if(!lua_isnoneornil(L, 3)) {
2855 }
else if(lua_isstring(L, 2)) {
2856 char const *m = luaL_checkstring(L, 2);
2876 }
else if(lua_isstring(L, 2)) {
2877 char const *m = luaL_checkstring(L, 2);
2897 }
else if(lua_isstring(L, 2)) {
2898 char const *m = luaL_checkstring(L, 2);
2918 }
else if(lua_isstring(L, 2)) {
2919 char const *m = luaL_checkstring(L, 2);
2935 char const *m = luaL_checkstring(L, 2);
2949 char const *m = luaL_checkstring(L, 2);
2951 if (!utp)
return luaL_argerror(L, 2,
"unknown unit type");
2952 if(lua_isstring(L, 3)) {
2953 const std::string& m2 = lua_tostring(L, 3);
2954 if(!utp->
has_variation(m2))
return luaL_argerror(L, 2,
"unknown unit variation");
2968 lua_createtable(L, 0, 4);
2970 lua_setfield(L, -2,
"poisoned");
2971 lua_pushnumber(L, cmb.
slowed);
2972 lua_setfield(L, -2,
"slowed");
2974 lua_setfield(L, -2,
"untouched");
2976 lua_setfield(L, -2,
"average_hp");
2977 lua_createtable(L,
n, 0);
2978 for (
int i = 0;
i <
n; ++
i) {
2980 lua_rawseti(L, -2,
i);
2982 lua_setfield(L, -2,
"hp_chance");
2991 lua_createtable(L, 0, 16);
2994 lua_setfield(L, -2,
"num_blows");
2995 lua_pushnumber(L, bcustats.
damage);
2996 lua_setfield(L, -2,
"damage");
2998 lua_setfield(L, -2,
"chance_to_hit");
2999 lua_pushboolean(L, bcustats.
poisons);
3000 lua_setfield(L, -2,
"poisons");
3001 lua_pushboolean(L, bcustats.
slows);
3002 lua_setfield(L, -2,
"slows");
3004 lua_setfield(L, -2,
"petrifies");
3005 lua_pushboolean(L, bcustats.
plagues);
3006 lua_setfield(L, -2,
"plagues");
3008 lua_setfield(L, -2,
"plague_type");
3009 lua_pushnumber(L, bcustats.
rounds);
3010 lua_setfield(L, -2,
"rounds");
3012 lua_setfield(L, -2,
"firststrike");
3013 lua_pushboolean(L, bcustats.
drains);
3014 lua_setfield(L, -2,
"drains");
3016 lua_setfield(L, -2,
"drain_constant");
3018 lua_setfield(L, -2,
"drain_percent");
3023 lua_setfield(L, -2,
"attack_num");
3025 lua_setfield(L, -2,
"number");
3027 if(bcustats.
weapon !=
nullptr)
3029 lua_pushstring(L, bcustats.
weapon->id().c_str());
3030 lua_setfield(L, -2,
"name");
3032 lua_setfield(L, -2,
"weapon");
3051 int arg_num = 1, att_w = -1, def_w = -1;
3055 if (lua_isnumber(L, arg_num)) {
3056 att_w = lua_tointeger(L, arg_num) - 1;
3057 if (att_w < 0 || att_w >=
static_cast<int>(att->attacks().size()))
3058 return luaL_argerror(L, arg_num,
"weapon index out of bounds");
3064 if (lua_isnumber(L, arg_num)) {
3065 def_w = lua_tointeger(L, arg_num) - 1;
3066 if (def_w < 0 || def_w >=
static_cast<int>(def->attacks().size()))
3067 return luaL_argerror(L, arg_num,
"weapon index out of bounds");
3072 def->get_location(), att_w, def_w, 0.0,
nullptr, att, def);
3089 char const *m = luaL_checkstring(L, 1);
3090 int repeats = luaL_optinteger(L, 2, 0);
3102 const char* content_for = luaL_checkstring(L, 1);
3103 const char*
id = luaL_checkstring(L, 2);
3106 if(group.content_for_ == content_for) {
3108 if(achieve.
id_ ==
id) {
3135 ERR_LUA <<
"Achievement " <<
id <<
" not found for achievement group " << content_for;
3141 ERR_LUA <<
"Achievement group " << content_for <<
" not found";
3153 const char* content_for = luaL_checkstring(L, 1);
3154 const char*
id = luaL_checkstring(L, 2);
3157 ERR_LUA <<
"Returning false for whether a player has completed an achievement due to being networked multiplayer.";
3158 lua_pushboolean(L,
false);
3174 const char* content_for = luaL_checkstring(L, 1);
3175 const char*
id = luaL_checkstring(L, 2);
3179 if(group.content_for_ == content_for) {
3180 for(
const auto& achieve : group.achievements_) {
3181 if(achieve.id_ ==
id) {
3183 cfg[
"id"] = achieve.id_;
3184 cfg[
"name"] = achieve.name_;
3185 cfg[
"name_completed"] = achieve.name_completed_;
3186 cfg[
"description"] = achieve.description_;
3187 cfg[
"description_completed"] = achieve.description_completed_;
3188 cfg[
"icon"] = achieve.icon_;
3189 cfg[
"icon_completed"] = achieve.icon_completed_;
3190 cfg[
"hidden"] = achieve.hidden_;
3191 cfg[
"achieved"] = achieve.achieved_;
3192 cfg[
"max_progress"] = achieve.max_progress_;
3193 cfg[
"current_progress"] = achieve.current_progress_;
3195 for(
const auto& sub_ach : achieve.sub_achievements_) {
3197 sub[
"id"] = sub_ach.id_;
3198 sub[
"description"] = sub_ach.description_;
3199 sub[
"icon"] = sub_ach.icon_;
3200 sub[
"achieved"] = sub_ach.achieved_;
3208 ERR_LUA <<
"Achievement " <<
id <<
" not found for achievement group " << content_for;
3214 ERR_LUA <<
"Achievement group " << content_for <<
" not found";
3230 const char* content_for = luaL_checkstring(L, 1);
3231 const char*
id = luaL_checkstring(L, 2);
3232 int amount = luaL_checkinteger(L, 3);
3233 int limit = luaL_optinteger(L, 4, 999999999);
3236 if(group.content_for_ == content_for) {
3238 if(achieve.
id_ ==
id) {
3241 ERR_LUA <<
"Attempted to progress achievement " <<
id <<
" for achievement group " << content_for <<
", is not a progressible achievement.";
3242 lua_pushinteger(L, -1);
3243 lua_pushinteger(L, -1);
3258 lua_pushinteger(L, progress);
3260 lua_pushinteger(L, -1);
3268 lua_push(L,
"Achievement " + std::string(
id) +
" not found for achievement group " + content_for);
3269 return lua_error(L);
3274 lua_push(L,
"Achievement group " + std::string(content_for) +
" not found");
3275 return lua_error(L);
3287 const char* content_for = luaL_checkstring(L, 1);
3288 const char*
id = luaL_checkstring(L, 2);
3289 const char* sub_id = luaL_checkstring(L, 3);
3292 ERR_LUA <<
"Returning false for whether a player has completed an achievement due to being networked multiplayer.";
3293 lua_pushboolean(L,
false);
3309 const char* content_for = luaL_checkstring(L, 1);
3310 const char*
id = luaL_checkstring(L, 2);
3311 const char* sub_id = luaL_checkstring(L, 3);
3314 if(group.content_for_ == content_for) {
3316 if(achieve.
id_ ==
id) {
3323 if(sub_ach.
id_ == sub_id) {
3341 lua_push(L,
"Sub-achievement " + std::string(
id) +
" not found for achievement" +
id +
" in achievement group " + content_for);
3342 return lua_error(L);
3346 lua_push(L,
"Achievement " + std::string(
id) +
" not found for achievement group " + content_for);
3347 return lua_error(L);
3352 lua_push(L,
"Achievement group " + std::string(content_for) +
" not found");
3353 return lua_error(L);
3389 if(lua_isnoneornil(L, 1)) {
3394 if(!
map().on_board(loc))
return luaL_argerror(L, 1,
"not on board");
3395 bool highlight =
true;
3396 if(!lua_isnoneornil(L, 2))
3426 lua_pushboolean(L, skipping);
3437 if (!lua_isnone(L, 1)) {
3449 int user_choice_index;
3450 int random_choice_index;
3451 int ai_choice_index;
3453 lua_synchronize(lua_State *l,
const std::string& descr,
int user_index,
int random_index = 0,
int ai_index = 0)
3455 , user_choice_index(user_index)
3456 , random_choice_index(random_index)
3457 , ai_choice_index(ai_index != 0 ? ai_index : user_index)
3463 bool is_local_ai = lua_kernel_base::get_lua_kernel<game_lua_kernel>(L).board().get_team(side).is_local_ai();
3465 query_lua(side, is_local_ai ? ai_choice_index : user_choice_index, cfg);
3472 if(random_choice_index != 0 && lua_isfunction(L, random_choice_index)) {
3473 query_lua(side, random_choice_index, cfg);
3483 void query_lua(
int side,
int function_index,
config& cfg)
const
3485 lua_pushvalue(L, function_index);
3486 lua_pushnumber(L, side);
3489 static const char*
msg =
"function returned to wesnoth.sync.[multi_]evaluate a table which was partially invalid";
3490 lua_kernel_base::get_lua_kernel<game_lua_kernel>(L).log_error(
msg);
3491 lua_warning(L,
msg,
false);
3498 virtual bool is_visible()
const override {
return false; }
3512 std::string tagname =
"input";
3519 if(!lua_isfunction(L, nextarg) &&
luaW_totstring(L, nextarg, desc) ) {
3522 if(lua_isfunction(L, nextarg)) {
3523 human_func = nextarg++;
3526 return luaL_argerror(L, nextarg,
"expected a function");
3528 if(lua_isfunction(L, nextarg)) {
3529 ai_func = nextarg++;
3531 side_for = lua_tointeger(L, nextarg);
3547 std::string tagname =
"input";
3551 std::vector<int> sides_for;
3554 if(!lua_isfunction(L, nextarg) &&
luaW_totstring(L, nextarg, desc) ) {
3557 if(lua_isfunction(L, nextarg)) {
3558 human_func = nextarg++;
3561 return luaL_argerror(L, nextarg,
"expected a function");
3563 if(lua_isfunction(L, nextarg)) {
3564 null_func = nextarg++;
3566 sides_for = lua_check<std::vector<int>>(L, nextarg++);
3581 lua_pushvalue(L, 1);
3596 std::set<map_location> res;
3598 const terrain_filter t_filter(filter, &fc,
false);
3600 t_filter.get_locations(res, *
luaW_tounit(L, 2),
true);
3602 t_filter.get_locations(res,
true);
3621 if (filter.
null()) {
3622 lua_pushboolean(L,
true);
3627 const terrain_filter t_filter(filter, &fc,
false);
3629 lua_pushboolean(L, t_filter.match(loc, *
luaW_tounit(L, 3)));
3631 lua_pushboolean(L, t_filter.match(loc));
3648 if (filter.
null()) {
3649 lua_pushboolean(L,
true);
3657 lua_pushboolean(L, s_filter.
match(*
t));
3659 unsigned side = luaL_checkinteger(L, 1) - 1;
3661 lua_pushboolean(L, s_filter.
match(side + 1));
3672 team_i = luaL_checkinteger(L, 1);
3674 std::string
flag = luaL_optlstring(L, 2,
"",
nullptr);
3675 std::string color = luaL_optlstring(L, 3,
"",
nullptr);
3677 if(
flag.empty() && color.empty()) {
3680 if(team_i < 1 ||
static_cast<std::size_t
>(team_i) >
teams().
size()) {
3681 return luaL_error(L,
"set_side_id: side number %d out of range", team_i);
3685 if(!color.empty()) {
3700 side_num =
t->side();
3702 side_num = luaL_checkinteger(L, 1);
3704 std::string
path = luaL_checkstring(L, 2);
3709 if(strcmp(action,
"delete") == 0) {
3714 std::size_t len = std::string::npos, open_brak =
path.find_last_of(
'[');
3715 std::size_t dot =
path.find_last_of(
'.');
3716 if(open_brak != len) {
3717 len = open_brak - dot - 1;
3728 side_num =
t->side();
3730 side_num = luaL_checkinteger(L, 1);
3732 if(lua_isstring(L, 2)) {
3733 std::string file = luaL_checkstring(L, 2);
3735 std::string
err =
formatter() <<
"Could not load AI for side " << side_num <<
" from file " << file;
3736 lua_pushlstring(L,
err.c_str(),
err.length());
3737 return lua_error(L);
3749 side_num =
t->side();
3751 side_num = luaL_checkinteger(L, 1);
3755 cfg =
config {
"ai", cfg};
3757 bool added_dummy_stage =
false;
3759 added_dummy_stage =
true;
3763 if(added_dummy_stage) {
3765 if(iter->key ==
"stage" && iter->cfg[
"name"] ==
"empty") {
3766 iter = cfg.
erase(iter);
3776 unsigned i = luaL_checkinteger(L, 1);
3777 if(i < 1 || i >
teams().
size())
return 0;
3789 LOG_LUA <<
"intf_get_sides called: this = " << std::hex <<
this << std::dec <<
" myname = " <<
my_name();
3790 std::vector<int> sides;
3794 sides.push_back(
t.side());
3804 lua_createtable(L, sides.size(), 0);
3806 for(
int side : sides) {
3808 lua_rawseti(L, -2,
index);
3825 char const *m = luaL_checkstring(L, 2);
3827 if (sm ==
"advance") {
3831 if (sm !=
"advancement" && sm !=
"object" && sm !=
"trait") {
3832 return luaL_argerror(L, 2,
"unknown modification type");
3834 bool write_to_mods =
true;
3835 if (!lua_isnone(L, 4)) {
3839 write_to_mods =
false;
3857 std::vector<std::string> tags;
3858 if(lua_isstring(L, 3)) {
3859 tags.push_back(lua_check<std::string>(L, 3));
3860 }
else if (lua_istable(L, 3)){
3861 tags = lua_check<std::vector<std::string>>(L, 3);
3863 tags.push_back(
"object");
3869 for(
const std::string& tag : tags) {
3871 if(obj.matches(filter)) {
3872 obj[
"duration"] =
"now";
3892 if(lua_isboolean(L, 2)) {
3895 if(lua_isboolean(L, 3)) {
3909 char const *ty = luaL_checkstring(L, 1);
3912 std::stringstream ss;
3913 ss <<
"unknown unit type: '" << ty <<
"'";
3914 return luaL_argerror(L, 1, ss.str().c_str());
3931 std::string team_name;
3934 std::vector<std::string> team_names;
3935 std::transform(
teams.begin(),
teams.end(), std::back_inserter(team_names),
3936 [&](
int team) { return game_state_.get_disp_context().get_team(team).team_name(); });
3939 team_name = cfg[
"team_name"].str();
3944 team_name, cfg[
"name"], cfg[
"visible_in_fog"].to_bool(
true),
3945 cfg[
"submerge"].to_double(0), cfg[
"z_order"].to_double(0));
3958 char const *m = lua_tostring(L, 2);
3975 const int nargs = lua_gettop(L);
3976 if(nargs < 2 || nargs > 3) {
3977 return luaL_error(L,
"Wrong number of arguments to ai.log_replay() - should be 2 or 3 arguments.");
3979 const std::string key = nargs == 2 ? luaL_checkstring(L, 1) : luaL_checkstring(L, 2);
3985 }
else if(!lua_isstring(L, 3)) {
3986 return luaL_argerror(L, 3,
"accepts only string or config");
4009 cfg.
add_child(
"filter_lua")[
"code"] =
"<function>";
4019 if(lua_isstring(L, idx)) {
4020 return lua_tostring(L, idx);
4060 using namespace std::literals;
4066 }
else if(is_menu_item) {
4068 return luaL_argerror(L, 1,
"non-empty id is required for a menu item");
4070 name =
"menu item " +
id;
4072 if(
id.empty() && name.empty()) {
4073 return luaL_argerror(L, 1,
"either a name or id is required");
4076 if(new_handler.valid()) {
4077 bool has_lua_filter =
false;
4081 int filterIdx = lua_gettop(L);
4084 if(lua_isfunction(L, filterIdx)) {
4085 int fcnIdx = lua_absindex(L, -1);
4086 new_handler->add_filter(std::make_unique<lua_event_filter>(*
this, fcnIdx,
luaW_table_get_def(L, 1,
"filter_args",
config())));
4087 has_lua_filter =
true;
4089 #define READ_ONE_FILTER(key, tag) \
4091 if(luaW_tableget(L, filterIdx, key)) { \
4092 if(lua_isstring(L, -1)) { \
4093 filters.add_child("insert_tag", config{ \
4095 "variable", luaL_checkstring(L, -1) \
4098 filters.add_child(tag, luaW_checkconfig(L, -1)); \
4108 #undef READ_ONE_FILTER
4110 filters[
"filter_formula"] = luaL_checkstring(L, -1);
4114 new_handler->read_filters(filters);
4120 if(has_lua_filter) {
4125 new_handler->register_wml_event(*
this);
4138 lua_pushvalue(L, lua_upvalueindex(1));
4155 template<
bool is_menu_item>
4161 double priority = luaL_optnumber(L, 3, 0.);
4163 return luaL_argerror(L, 1,
"must not be empty");
4167 name =
"menu item " + name;
4168 }
else if(lua_absindex(L, -1) > 2 && lua_isfunction(L, -1)) {
4171 lua_pushcclosure(L, &dispatch<&game_lua_kernel::cfun_undoable_event>, 2);
4174 if(new_handler.valid()) {
4191 bool delayed_variable_substitution = cfg[
"delayed_variable_substitution"].to_bool(
true);
4192 if(delayed_variable_substitution) {
4219 lua_pushinteger(L, color.r);
4220 lua_pushinteger(L, color.g);
4221 lua_pushinteger(L, color.b);
4230 auto vec = lua_check<std::vector<uint8_t>>(L, 1);
4231 if(vec.size() != 4) {
4234 color_t fade{vec[0], vec[1], vec[2], vec[3]};
4252 lua_Integer delay = luaL_checkinteger(L, 1);
4260 const unsigned final = SDL_GetTicks() + delay;
4264 }
while (
static_cast<int>(
final - SDL_GetTicks()) > 0);
4288 std::string team_name;
4291 if(lua_gettop(L) == 1 && lua_istable(L, 1)) {
4292 using namespace std::literals;
4295 team_name = luaL_optstring(L, 2,
"");
4309 switch(lua_type(L, 2)) {
4311 case LUA_TNONE:
case LUA_TNIL:
4316 if(
size_t n = luaL_checkinteger(L, 2);
n > 0 &&
n <=
teams().size()) {
4356 for (
const int side : filter.
get_teams()){
4378 int side = cfg[
"side"];
4388 lua_getfield(L, -1,
"ca_ptr");
4402 lua_getfield(L, -1,
"stg_ptr");
4411 lua_createtable(L, 0, 0);
4413 lua_pushstring(L,
"name");
4414 lua_pushstring(L,
c->get_name().c_str());
4417 lua_pushstring(L,
"engine");
4418 lua_pushstring(L,
c->get_engine().c_str());
4421 lua_pushstring(L,
"id");
4422 lua_pushstring(L,
c->get_id().c_str());
4425 if (ct ==
"candidate_action") {
4426 lua_pushstring(L,
"ca_ptr");
4427 lua_pushlightuserdata(L,
c);
4430 lua_pushstring(L,
"exec");
4435 if (ct ==
"stage") {
4436 lua_pushstring(L,
"stg_ptr");
4437 lua_pushlightuserdata(L,
c);
4440 lua_pushstring(L,
"exec");
4446 std::vector<std::string> c_types =
c->get_children_types();
4448 for (std::vector<std::string>::const_iterator
t = c_types.begin();
t != c_types.end(); ++
t)
4450 std::vector<ai::component*> children =
c->get_children(*
t);
4451 std::string
type = *
t;
4452 if (
type ==
"aspect" ||
type ==
"goal" ||
type ==
"engine")
4457 lua_pushstring(L,
type.c_str());
4458 lua_createtable(L, 0, 0);
4460 for (std::vector<ai::component*>::const_iterator
i = children.begin();
i != children.end(); ++
i)
4462 lua_pushstring(L, (*i)->get_name().c_str());
4493 side = luaL_checkinteger(L, 1);
4500 std::vector<ai::component*> engines =
c->get_children(
"engine");
4502 for (std::vector<ai::component*>::const_iterator
i = engines.begin();
i != engines.end(); ++
i)
4504 if ((*i)->get_name() ==
"lua")
4514 if (lua_engine ==
nullptr)
4527 LOG_LUA <<
"Created new dummy lua-engine for debug_ai().";
4537 lua_pushstring(L,
"components");
4563 if(lua_isboolean(L, 1)) {
4588 std::set<map_location> locs;
4591 if(lua_gettop(L) == 1) {
4594 id = cfg[
"id"].str();
4595 const terrain_filter filter(cfg, &
game_state_,
false);
4596 filter.get_locations(locs,
true);
4599 id = luaL_checkstring(L, 1);
4600 if(!lua_isnoneornil(L, 3))
4605 const terrain_filter filter(cfg, &
game_state_,
false);
4606 filter.get_locations(locs,
true);
4614 LOG_LUA <<
"Lua inserted time_area '" <<
id <<
"'";
4623 const char *
id = luaL_checkstring(L, 1);
4625 LOG_LUA <<
"Lua removed time_area '" <<
id <<
"'";
4635 if(area_index < 0) {
4642 std::string area_id = luaL_checkstring(L, 1);
4644 if(
auto iter = std::find(area_ids.begin(), area_ids.end(), area_id); iter == area_ids.end()) {
4658 if(luaL_testudata(L, 1,
"schedule")) {
4668 ERR_LUA <<
"attempted to to replace ToD schedule with empty schedule";
4674 LOG_LUA <<
"replaced ToD schedule";
4682 int x = luaL_checkinteger(L, 1);
4683 int y = luaL_checkinteger(L, 2);
4702 lua_report_generator(lua_State *L,
const std::string &
n)
4711 if (!
luaW_getglobal(L,
"wesnoth",
"interface",
"game_display", name))
4735 char const *m = luaL_checkstring(L, 2);
4737 lua_pushvalue(L, 2);
4738 lua_pushvalue(L, -2);
4749 char const *m = luaL_checkstring(L, 2);
4750 lua_pushvalue(L, 2);
4751 lua_pushvalue(L, 3);
4783 if (
dst == u->get_location() || !
map().on_board(
dst)) {
4787 if (!
map().on_board(vacant_dst)) {
4798 std::vector<map_location> teleport_path;
4799 teleport_path.push_back(src_loc);
4800 teleport_path.push_back(vacant_dst);
4807 u->anim_comp().set_standing();
4814 if (
map().is_village(vacant_dst)) {
4833 const std::string& logger = lua_isstring(L, 2) ? luaL_checkstring(L, 1) :
"";
4834 const std::string&
msg = lua_isstring(L, 2) ? luaL_checkstring(L, 2) : luaL_checkstring(L, 1);
4836 if(logger ==
"wml" || logger ==
"WML") {
4850 lua_pushboolean(L, fog ?
t.fogged(loc) :
t.shrouded(loc));
4868 bool affect_normal_fog =
false;
4869 if(lua_isboolean(L, -1)) {
4872 std::set<int> sides;
4874 sides.insert(
t->side());
4875 }
else if(lua_isnumber(L, 1)) {
4876 sides.insert(lua_tointeger(L, 1));
4877 }
else if(lua_istable(L, 1) && lua_istable(L, 2)) {
4878 const auto& v = lua_check<std::vector<int>>(L, 1);
4879 sides.insert(v.begin(), v.end());
4882 sides.insert(
t.side()+1);
4887 for(
const int &side_num : sides) {
4888 if(side_num < 1 ||
static_cast<std::size_t
>(side_num) >
teams().
size()) {
4894 t.remove_fog_override(locs);
4895 if(affect_normal_fog) {
4898 }
else if(!affect_normal_fog) {
4900 t.add_fog_override(locs);
4918 const std::string name = luaL_checkstring(L, 1);
4923 if(!
luaW_getglobal(L,
"wesnoth",
"custom_synced_commands", name)) {
4924 return luaL_argerror(L, 1,
"Unknown synced command");
4927 cmd_tag[
"name"] = name;
4928 if(!lua_isnoneornil(L, 2)) {
4989 cmd_log_ <<
"Registering game-specific wesnoth lib functions...\n";
4992 static luaL_Reg
const callbacks[] {
4997 {
"cancel_action", &dispatch<&game_lua_kernel::intf_cancel_action > },
4998 {
"log_replay", &dispatch<&game_lua_kernel::intf_log_replay > },
4999 {
"log", &dispatch<&game_lua_kernel::intf_log > },
5000 {
"redraw", &dispatch<&game_lua_kernel::intf_redraw > },
5001 {
"simulate_combat", &dispatch<&game_lua_kernel::intf_simulate_combat > },
5002 {
nullptr,
nullptr }
5003 };lua_getglobal(L,
"wesnoth");
5004 if (!lua_istable(L,-1)) {
5007 luaL_setfuncs(L, callbacks, 0);
5009 lua_setglobal(L,
"wesnoth");
5011 lua_getglobal(L,
"gui");
5012 lua_pushcfunction(L, &dispatch<&game_lua_kernel::intf_gamestate_inspector>);
5013 lua_setfield(L, -2,
"show_inspector");
5019 static luaL_Reg
const test_callbacks[] {
5020 {
"fire_wml_menu_item", &dispatch<&game_lua_kernel::intf_fire_wml_menu_item> },
5021 {
nullptr,
nullptr }
5023 luaL_setfuncs(L, test_callbacks, 0);
5024 lua_setglobal(L,
"unit_test");
5050 cmd_log_ <<
"Adding terrain_types table...\n";
5051 lua_getglobal(L,
"wesnoth");
5052 lua_newuserdatauv(L, 0, 0);
5053 lua_createtable(L, 0, 2);
5054 lua_pushcfunction(L, &dispatch<&game_lua_kernel::impl_get_terrain_info>);
5055 lua_setfield(L, -2,
"__index");
5056 lua_pushstring(L,
"terrain types");
5057 lua_setfield(L, -2,
"__metatable");
5058 lua_setmetatable(L, -2);
5059 lua_setfield(L, -2,
"terrain_types");
5063 cmd_log_ <<
"Adding ai elements table...\n";
5068 cmd_log_ <<
"Adding wesnoth current table...\n";
5070 lua_getglobal(L,
"wesnoth");
5071 lua_newuserdatauv(L, 0, 0);
5072 lua_createtable(L, 0, 2);
5073 lua_pushcfunction(L, &dispatch<&game_lua_kernel::impl_current_get>);
5074 lua_setfield(L, -2,
"__index");
5075 lua_pushstring(L,
"current config");
5076 lua_setfield(L, -2,
"__metatable");
5077 lua_setmetatable(L, -2);
5078 lua_setfield(L, -2,
"current");
5082 lua_getglobal(L,
"wml");
5083 static luaL_Reg
const wml_callbacks[] {
5087 {
"get_variable", &dispatch<&game_lua_kernel::intf_get_variable>},
5088 {
"set_variable", &dispatch<&game_lua_kernel::intf_set_variable>},
5089 {
"get_all_vars", &dispatch<&game_lua_kernel::intf_get_all_vars>},
5090 {
nullptr,
nullptr }
5092 luaL_setfuncs(L, wml_callbacks, 0);
5097 static luaL_Reg
const map_callbacks[] {
5104 {
"get_owner", &dispatch<&game_lua_kernel::intf_get_village_owner>},
5105 {
"set_owner", &dispatch<&game_lua_kernel::intf_set_village_owner>},
5107 {
"add_label", &dispatch<&game_lua_kernel::intf_add_label>},
5108 {
"remove_label", &dispatch<&game_lua_kernel::intf_remove_label>},
5109 {
"get_label", &dispatch<&game_lua_kernel::intf_get_label>},
5111 {
"place_area", &dispatch<&game_lua_kernel::intf_add_time_area>},
5112 {
"remove_area", &dispatch<&game_lua_kernel::intf_remove_time_area>},
5113 {
"get_area", &dispatch<&game_lua_kernel::intf_get_time_area>},
5115 {
"find", &dispatch<&game_lua_kernel::intf_get_locations>},
5116 {
"matches", &dispatch<&game_lua_kernel::intf_match_location>},
5118 {
nullptr,
nullptr }
5120 luaL_setfuncs(L, map_callbacks, 0);
5124 cmd_log_ <<
"Adding units module...\n";
5125 static luaL_Reg
const unit_callbacks[] {
5128 {
"erase", &dispatch<&game_lua_kernel::intf_erase_unit>},
5129 {
"extract", &dispatch<&game_lua_kernel::intf_extract_unit>},
5130 {
"matches", &dispatch<&game_lua_kernel::intf_match_unit>},
5131 {
"select", &dispatch<&game_lua_kernel::intf_select_unit>},
5132 {
"to_map", &dispatch<&game_lua_kernel::intf_put_unit>},
5133 {
"to_recall", &dispatch<&game_lua_kernel::intf_put_recall_unit>},
5135 {
"teleport", &dispatch<&game_lua_kernel::intf_teleport>},
5137 {
"ability", &dispatch<&game_lua_kernel::intf_unit_ability>},
5148 {
"find_on_map", &dispatch<&game_lua_kernel::intf_get_units>},
5149 {
"find_on_recall", &dispatch<&game_lua_kernel::intf_get_recall_units>},
5150 {
"get", &dispatch<&game_lua_kernel::intf_get_unit>},
5151 {
"get_hovered", &dispatch<&game_lua_kernel::intf_get_displayed_unit>},
5152 {
"create_animator", &dispatch<&game_lua_kernel::intf_create_animator>},
5155 {
nullptr,
nullptr }
5157 lua_getglobal(L,
"wesnoth");
5159 luaL_setfuncs(L, unit_callbacks, 0);
5160 lua_setfield(L, -2,
"units");
5164 cmd_log_ <<
"Adding sides module...\n";
5165 static luaL_Reg
const side_callbacks[] {
5166 {
"is_enemy", &dispatch<&game_lua_kernel::intf_is_enemy> },
5167 {
"matches", &dispatch<&game_lua_kernel::intf_match_side> },
5168 {
"set_id", &dispatch<&game_lua_kernel::intf_set_side_id> },
5173 {
"find", &dispatch<&game_lua_kernel::intf_get_sides> },
5174 {
"get", &dispatch<&game_lua_kernel::intf_get_side> },
5175 {
"create", &dispatch<&game_lua_kernel::intf_create_side> },
5177 {
"place_shroud", &dispatch2<&game_lua_kernel::intf_toggle_shroud, true>},
5178 {
"remove_shroud", &dispatch2<&game_lua_kernel::intf_toggle_shroud, false>},
5179 {
"override_shroud", &dispatch<&game_lua_kernel::intf_override_shroud>},
5180 {
"is_shrouded", &dispatch2<&game_lua_kernel::intf_get_fog_or_shroud, false>},
5182 {
"place_fog", &dispatch2<&game_lua_kernel::intf_toggle_fog, false>},
5183 {
"remove_fog", &dispatch2<&game_lua_kernel::intf_toggle_fog, true>},
5184 {
"is_fogged", &dispatch2<&game_lua_kernel::intf_get_fog_or_shroud, true>},
5185 {
nullptr,
nullptr }
5187 std::vector<lua_cpp::Reg>
const cpp_side_callbacks {
5188 {
"add_ai_component", std::bind(
intf_modify_ai, std::placeholders::_1,
"add")},
5189 {
"delete_ai_component", std::bind(
intf_modify_ai, std::placeholders::_1,
"delete")},
5190 {
"change_ai_component", std::bind(
intf_modify_ai, std::placeholders::_1,
"change")},
5194 lua_getglobal(L,
"wesnoth");
5196 luaL_setfuncs(L, side_callbacks, 0);
5198 lua_setfield(L, -2,
"sides");
5202 cmd_log_ <<
"Adding interface module...\n";
5203 static luaL_Reg
const intf_callbacks[] {
5204 {
"add_hex_overlay", &dispatch<&game_lua_kernel::intf_add_tile_overlay>},
5205 {
"remove_hex_overlay", &dispatch<&game_lua_kernel::intf_remove_tile_overlay>},
5206 {
"get_color_adjust", &dispatch<&game_lua_kernel::intf_get_color_adjust>},
5207 {
"color_adjust", &dispatch<&game_lua_kernel::intf_color_adjust>},
5208 {
"screen_fade", &dispatch<&game_lua_kernel::intf_screen_fade>},
5209 {
"delay", &dispatch<&game_lua_kernel::intf_delay>},
5210 {
"deselect_hex", &dispatch<&game_lua_kernel::intf_deselect_hex>},
5211 {
"highlight_hex", &dispatch<&game_lua_kernel::intf_highlight_hex>},
5212 {
"float_label", &dispatch<&game_lua_kernel::intf_float_label>},
5213 {
"get_displayed_unit", &dispatch<&game_lua_kernel::intf_get_displayed_unit>},
5214 {
"get_hovered_hex", &dispatch<&game_lua_kernel::intf_get_mouseover_tile>},
5215 {
"get_selected_hex", &dispatch<&game_lua_kernel::intf_get_selected_tile>},
5216 {
"lock", &dispatch<&game_lua_kernel::intf_lock_view>},
5217 {
"is_locked", &dispatch<&game_lua_kernel::intf_view_locked>},
5218 {
"scroll", &dispatch<&game_lua_kernel::intf_scroll>},
5219 {
"scroll_to_hex", &dispatch<&game_lua_kernel::intf_scroll_to_tile>},
5220 {
"skip_messages", &dispatch<&game_lua_kernel::intf_skip_messages>},
5221 {
"is_skipping_messages", &dispatch<&game_lua_kernel::intf_is_skipping_messages>},
5222 {
"zoom", &dispatch<&game_lua_kernel::intf_zoom>},
5223 {
"clear_menu_item", &dispatch<&game_lua_kernel::intf_clear_menu_item>},
5224 {
"set_menu_item", &dispatch<&game_lua_kernel::intf_set_menu_item>},
5225 {
"allow_end_turn", &dispatch<&game_lua_kernel::intf_allow_end_turn>},
5226 {
"clear_chat_messages", &dispatch<&game_lua_kernel::intf_clear_messages>},
5227 {
"end_turn", &dispatch<&game_lua_kernel::intf_end_turn>},
5229 {
"add_chat_message", &dispatch<&game_lua_kernel::intf_message>},
5230 {
"add_overlay_text", &dispatch2<&game_lua_kernel::intf_set_floating_label, true>},
5232 {
nullptr,
nullptr }
5234 lua_getglobal(L,
"wesnoth");
5236 luaL_setfuncs(L, intf_callbacks, 0);
5237 lua_setfield(L, -2,
"interface");
5241 cmd_log_ <<
"Adding achievements module...\n";
5242 static luaL_Reg
const achievement_callbacks[] {
5243 {
"set", &dispatch<&game_lua_kernel::intf_set_achievement> },
5244 {
"has", &dispatch<&game_lua_kernel::intf_has_achievement> },
5245 {
"get", &dispatch<&game_lua_kernel::intf_get_achievement> },
5246 {
"progress", &dispatch<&game_lua_kernel::intf_progress_achievement> },
5247 {
"has_sub_achievement", &dispatch<&game_lua_kernel::intf_has_sub_achievement> },
5248 {
"set_sub_achievement", &dispatch<&game_lua_kernel::intf_set_sub_achievement> },
5249 {
nullptr,
nullptr }
5251 lua_getglobal(L,
"wesnoth");
5253 luaL_setfuncs(L, achievement_callbacks, 0);
5254 lua_setfield(L, -2,
"achievements");
5258 cmd_log_ <<
"Adding audio module...\n";
5259 static luaL_Reg
const audio_callbacks[] {
5260 {
"play", &dispatch<&game_lua_kernel::intf_play_sound > },
5261 {
nullptr,
nullptr }
5263 lua_getglobal(L,
"wesnoth");
5265 luaL_setfuncs(L, audio_callbacks, 0);
5266 lua_setfield(L, -2,
"audio");
5270 cmd_log_ <<
"Adding paths module...\n";
5271 static luaL_Reg
const path_callbacks[] {
5272 {
"find_cost_map", &dispatch<&game_lua_kernel::intf_find_cost_map > },
5273 {
"find_path", &dispatch<&game_lua_kernel::intf_find_path > },
5274 {
"find_reach", &dispatch<&game_lua_kernel::intf_find_reach > },
5275 {
"find_vacant_hex", &dispatch<&game_lua_kernel::intf_find_vacant_tile > },
5276 {
"find_vision_range", &dispatch<&game_lua_kernel::intf_find_vision_range > },
5277 {
nullptr,
nullptr }
5279 lua_getglobal(L,
"wesnoth");
5281 luaL_setfuncs(L, path_callbacks, 0);
5282 lua_setfield(L, -2,
"paths");
5286 cmd_log_ <<
"Adding sync module...\n";
5287 static luaL_Reg
const sync_callbacks[] {
5292 {
nullptr,
nullptr }
5294 lua_getglobal(L,
"wesnoth");
5296 luaL_setfuncs(L, sync_callbacks, 0);
5297 lua_setfield(L, -2,
"sync");
5301 cmd_log_ <<
"Adding schedule module...\n";
5302 static luaL_Reg
const schedule_callbacks[] {
5303 {
"get_time_of_day", &dispatch<&game_lua_kernel::intf_get_time_of_day<false>>},
5304 {
"get_illumination", &dispatch<&game_lua_kernel::intf_get_time_of_day<true>>},
5305 {
"replace", &dispatch<&game_lua_kernel::intf_replace_schedule>},
5306 {
nullptr,
nullptr }
5308 lua_getglobal(L,
"wesnoth");
5310 luaL_setfuncs(L, schedule_callbacks, 0);
5311 lua_createtable(L, 0, 2);
5312 lua_setmetatable(L, -2);
5313 lua_setfield(L, -2,
"schedule");
5320 cmd_log_ <<
"Adding wml_actions table...\n";
5322 lua_getglobal(L,
"wesnoth");
5324 lua_setfield(L, -2,
"wml_actions");
5328 cmd_log_ <<
"Adding wml_conditionals table...\n";
5330 lua_getglobal(L,
"wesnoth");
5332 lua_setfield(L, -2,
"wml_conditionals");
5339 cmd_log_ <<
"Adding effects table...\n";
5341 lua_getglobal(L,
"wesnoth");
5343 lua_setfield(L, -2,
"effects");
5347 cmd_log_ <<
"Adding custom_synced_commands table...\n";
5349 lua_getglobal(L,
"wesnoth");
5351 lua_setfield(L, -2,
"custom_synced_commands");
5355 cmd_log_ <<
"Adding game_events module...\n";
5356 static luaL_Reg
const event_callbacks[] {
5357 {
"add", &dispatch<&game_lua_kernel::intf_add_event> },
5358 {
"add_repeating", &dispatch<&game_lua_kernel::intf_add_event_simple<false>> },
5359 {
"add_menu", &dispatch<&game_lua_kernel::intf_add_event_simple<true>> },
5360 {
"add_wml", &dispatch<&game_lua_kernel::intf_add_event_wml> },
5361 {
"remove", &dispatch<&game_lua_kernel::intf_remove_event> },
5362 {
"fire", &dispatch2<&game_lua_kernel::intf_fire_event, false> },
5363 {
"fire_by_id", &dispatch2<&game_lua_kernel::intf_fire_event, true> },
5364 {
"add_undo_actions", &dispatch<&game_lua_kernel::intf_add_undo_actions> },
5365 {
"set_undoable", &dispatch<&game_lua_kernel::intf_allow_undo > },
5366 {
nullptr,
nullptr }
5368 lua_getglobal(L,
"wesnoth");
5370 luaL_setfuncs(L, event_callbacks, 0);
5371 lua_setfield(L, -2,
"game_events");
5375 cmd_log_ <<
"Adding game_display table...\n";
5379 lua_createtable(L, 0, 2);
5380 lua_pushcfunction(L, &dispatch<&game_lua_kernel::impl_theme_items_get>);
5381 lua_setfield(L, -2,
"__index");
5382 lua_pushcfunction(L, &dispatch<&game_lua_kernel::impl_theme_items_set>);
5383 lua_setfield(L, -2,
"__newindex");
5384 lua_setmetatable(L, -2);
5385 lua_setfield(L, -2,
"game_display");
5389 cmd_log_ <<
"Adding scenario table...\n";
5393 lua_createtable(L, 0, 2);
5394 lua_pushcfunction(L, &dispatch<&game_lua_kernel::impl_scenario_get>);
5395 lua_setfield(L, -2,
"__index");
5396 lua_pushcfunction(L, &dispatch<&game_lua_kernel::impl_scenario_set>);
5397 lua_setfield(L, -2,
"__newindex");
5398 lua_setmetatable(L, -2);
5399 lua_setfield(L, -2,
"scenario");
5410 lua_pushstring(L, effect.c_str());
5428 cmd_log_ <<
"Adding races table...\n";
5431 lua_getglobal(L,
"wesnoth");
5433 lua_setfield(L, -2,
"races");
5437 cmd_log_ <<
"Running preload scripts...\n";
5460 using namespace std::literals::string_view_literals;
5461 static const std::array handled_file_tags {
5474 "modify_unit_type"sv,
5480 "terrain_graphics"sv,
5488 return std::binary_search(handled_file_tags.begin(), handled_file_tags.end(),
s);
5507 lua_createtable(L, 2, 0);
5508 lua_pushstring(L, v.key.c_str());
5509 lua_rawseti(L, -2, 1);
5511 lua_rawseti(L, -2, 2);
5512 lua_rawseti(L, -2, k++);