116 #include <functional>
128 #include <SDL2/SDL_timer.h>
136 #define LOG_LUA LOG_STREAM(info, log_scripting_lua)
137 #define WRN_LUA LOG_STREAM(warn, log_scripting_lua)
138 #define ERR_LUA LOG_STREAM(err, log_scripting_lua)
141 #define ERR_WML LOG_STREAM(err, log_wml)
149 template <member_callback method>
151 return ((lua_kernel_base::get_lua_kernel<game_lua_kernel>(L)).*method)(L);
157 template <member_callback2 method,
bool b>
159 return ((lua_kernel_base::get_lua_kernel<game_lua_kernel>(L)).*method)(L,
b);
209 if(!sides.
empty()) {
WRN_LUA <<
"ignoring duplicate side filter information (inline side=)"; }
222 struct queued_event_context
225 std::stack<qe const *> & stack_;
227 queued_event_context(qe
const *new_qe, std::stack<qe const*> & stack)
233 ~queued_event_context()
248 lua_pushinteger(L, disp->viewing_side());
249 lua_pushboolean(L, disp->show_everything());
267 anim.~unit_animator();
276 std::string which = luaL_checkstring(L, 3);
278 std::string hits_str = luaL_checkstring(L, 4);
288 if(lua_istable(L, 5)) {
289 lua_getfield(L, 5,
"target");
292 return luaL_argerror(L, 5,
"target location must be different from animated unit's location");
294 return luaL_argerror(L, 5,
"target location must be adjacent to the animated unit");
299 if(!lua_isnoneornil(L, -1)) {
305 lua_getfield(L, 5,
"value");
306 if(lua_isnumber(L, -1)) {
307 v1 = lua_tointeger(L, -1);
308 }
else if(lua_istable(L, -1)) {
309 lua_rawgeti(L, -1, 1);
310 v1 = lua_tointeger(L, -1);
312 lua_rawgeti(L, -1, 2);
313 v2 = lua_tointeger(L, -1);
315 }
else if(!lua_isnoneornil(L, -1)) {
316 return luaW_type_error(L, 5,
"value",
"number or array of two numbers");
320 lua_getfield(L, 5,
"with_bars");
321 if(lua_isboolean(L, -1)) {
323 }
else if(!lua_isnoneornil(L, -1)) {
324 return luaW_type_error(L, 5,
"with_bars", lua_typename(L, LUA_TBOOLEAN));
328 lua_getfield(L, 5,
"text");
329 if(lua_isstring(L, -1)) {
330 text = lua_tostring(L, -1);
333 }
else if(!lua_isnoneornil(L, -1)) {
338 lua_getfield(L, 5,
"color");
339 if(lua_istable(L, -1) && lua_rawlen(L, -1) == 3) {
340 int idx = lua_absindex(L, -1);
341 lua_rawgeti(L, idx, 1);
342 lua_rawgeti(L, idx, 2);
343 lua_rawgeti(L, idx, 3);
344 color =
color_t(lua_tointeger(L, -3), lua_tointeger(L, -2), lua_tointeger(L, -1));
346 }
else if(!lua_isnoneornil(L, -1)) {
351 lua_getfield(L, 5,
"primary");
353 if(!primary && !lua_isnoneornil(L, -1)) {
358 lua_getfield(L, 5,
"secondary");
360 if(!secondary && !lua_isnoneornil(L, -1)) {
364 }
else if(!lua_isnoneornil(L, 5)) {
368 anim.
add_animation(up, which, u.
get_location(), dest, v1, bars, text, color, hits, primary, secondary, v2);
396 const char* m = lua_tostring(L, 2);
404 luaL_Reg metafuncs[] {
408 {
"run", &dispatch<&game_lua_kernel::impl_run_animation>},
412 luaL_setfuncs(L, metafuncs, 0);
413 lua_pushstring(L,
"__metatable");
416 lua_setmetatable(L, -2);
439 if(lua_isstring(L, 1) && !lua_isnumber(L, 1)) {
440 std::string
id = luaL_checkstring(L, 1);
450 return luaL_argerror(L, 1,
"expected string or location");
454 if (!ui.
valid())
return 0;
475 if (!ui.
valid())
return 0;
493 std::vector<const unit*>
units;
497 return luaL_argerror(L, 2,
"unit not found");
500 }
else if(!lua_isnoneornil(L, 2)) {
504 return luaL_argerror(L, 2,
"invalid location");
519 lua_rawseti(L, 1,
i);
539 lua_pushboolean(L,
true);
545 WRN_LUA <<
"wesnoth.units.matches called with a secondary unit (3rd argument), ";
546 WRN_LUA <<
"but unit to match was on recall list. ";
547 WRN_LUA <<
"Thus the 3rd argument is ignored.";
554 return luaL_argerror(L, 3,
"unit not found");
556 lua_pushboolean(L,
unit_filter(filter).matches(*u, *u_adj));
562 lua_pushboolean(L,
unit_filter(filter).matches(*u, loc));
567 lua_pushboolean(L,
unit_filter(filter).matches(*u, loc));
592 if (!filter.
null()) {
594 t.save_id_or_number(),
t.recall_list().find_index(u->id()));
599 lua_rawseti(L, 1,
i);
618 char const *m = luaL_checkstring(L, 1);
635 if(
data.has_child(
"primary_attack")) {
636 data.add_child(
"first",
data.mandatory_child(
"primary_attack"));
637 data.remove_children(
"primary_attack");
639 if(
data.has_child(
"secondary_attack")) {
640 data.add_child(
"second",
data.mandatory_child(
"secondary_attack"));
641 data.remove_children(
"secondary_attack");
652 lua_pushboolean(L,
b);
670 char const *m = luaL_checkstring(L, 1);
675 lua_pushboolean(L,
b);
687 char const *m = luaL_checkstring(L, 1);
699 const std::string m = luaL_checkstring(L, 1);
700 if(m.empty())
return luaL_argerror(L, 1,
"empty variable name");
701 if (lua_isnoneornil(L, 2)) {
714 cfg[
"side"] =
teams().size() + 1;
729 std::string ids(luaL_checkstring(L, 1));
732 WRN_LUA <<
"[clear_menu_item] has been given an empty id=, ignoring";
749 if(lua_istable(L, 2)) {
761 return luaL_argerror(L, 2,
"expected list of locations");
780 if(lua_istable(L, 2)) {
808 if(!
map().on_board(loc))
return luaL_argerror(L, 1,
"not on board");
822 unsigned side_1, side_2;
826 side_1 = luaL_checkinteger(L, 1);
831 side_2 = luaL_checkinteger(L, 2);
834 lua_pushboolean(L,
board().get_team(side_1).is_enemy(side_2));
868 lua_pushstring(L, tod.
id.c_str());
869 lua_setfield(L, -2,
"id");
871 lua_setfield(L, -2,
"lawful_bonus");
873 lua_setfield(L, -2,
"bonus_modified");
874 lua_pushstring(L, tod.
image.c_str());
875 lua_setfield(L, -2,
"image");
877 lua_setfield(L, -2,
"name");
878 lua_pushstring(L, tod.
sounds.c_str());
879 lua_setfield(L, -2,
"sound");
881 lua_setfield(L, -2,
"mask");
883 lua_pushinteger(L, tod.
color.
r);
884 lua_setfield(L, -2,
"red");
885 lua_pushinteger(L, tod.
color.
g);
886 lua_setfield(L, -2,
"green");
887 lua_pushinteger(L, tod.
color.
b);
888 lua_setfield(L, -2,
"blue");
895 lua_newuserdatauv(L, 0, 1);
896 lua_pushinteger(L, area_index);
897 lua_setiuservalue(L, -2, 1);
898 if(luaL_newmetatable(L,
"schedule")) {
899 static luaL_Reg
const schedule_meta[] {
900 {
"__index", &dispatch<&game_lua_kernel::impl_schedule_get>},
901 {
"__newindex", &dispatch<&game_lua_kernel::impl_schedule_set>},
902 {
"__len", &dispatch<&game_lua_kernel::impl_schedule_len>},
905 luaL_setfuncs(L, schedule_meta, 0);
907 lua_setmetatable(L, -2);
912 int save_top = lua_gettop(L);
913 luaL_checkudata(L, idx,
"schedule");
914 lua_getiuservalue(L, idx, 1);
915 int i = luaL_checkinteger(L, -1);
916 lua_settop(L, save_top);
923 if(lua_isnumber(L, 2)) {
925 int i = lua_tointeger(L, 2) - 1;
926 if(i < 0 || i >=
static_cast<int>(times.size())) {
927 return luaL_argerror(L, 2,
"invalid time of day index");
932 const char* m = luaL_checkstring(L, 2);
933 if(area_index >= 0) {
936 if(strcmp(m,
"hexes") == 0) {
957 lua_pushinteger(L, times.size());
964 if(lua_isnumber(L, 2)) {
966 int i = lua_tointeger(L, 2) - 1;
967 if(i < 0 || i >=
static_cast<int>(times.size())) {
968 return luaL_argerror(L, 2,
"invalid time of day index");
978 const char* m = luaL_checkstring(L, 2);
979 if(strcmp(m,
"time_of_day") == 0) {
980 std::string value = luaL_checkstring(L, 3);
982 auto iter = std::find_if(times.begin(), times.end(), [&value](
const time_of_day& tod) {
983 return tod.id == value;
985 if(iter == times.end()) {
986 std::ostringstream
err;
987 err <<
"invalid time of day ID for ";
989 err <<
"global schedule";
995 err <<
"anonymous empty time area";
997 err <<
"anonymous time area at (" << hexes.begin()->wml_x() <<
',' << hexes.begin()->wml_y() <<
")";
1000 err <<
"time area with id=" <<
id;
1004 return lua_error(L);
1006 int n = std::distance(times.begin(), iter);
1007 if(area_index < 0) {
1013 if(area_index >= 0) {
1015 if(strcmp(m,
"hexes") == 0) {
1022 if(lua_isnil(L, 3) && strcmp(m,
"liminal_bonus") == 0) {
1039 char const *m = luaL_checkstring(L, 2);
1045 lua_pushstring(L,
info.id().c_str());
1046 lua_setfield(L, -2,
"id");
1048 lua_setfield(L, -2,
"name");
1050 lua_setfield(L, -2,
"editor_name");
1052 lua_setfield(L, -2,
"description");
1054 lua_setfield(L, -2,
"icon");
1056 lua_setfield(L, -2,
"editor_image");
1057 lua_pushinteger(L,
info.light_bonus(0));
1058 lua_setfield(L, -2,
"light");
1059 lua_pushboolean(L,
info.is_village());
1060 lua_setfield(L, -2,
"village");
1061 lua_pushboolean(L,
info.is_castle());
1062 lua_setfield(L, -2,
"castle");
1063 lua_pushboolean(L,
info.is_keep());
1064 lua_setfield(L, -2,
"keep");
1065 lua_pushinteger(L,
info.gives_healing());
1066 lua_setfield(L, -2,
"healing");
1077 template<
bool cons
ider_illuminates>
1084 if(!
board().
map().on_board_with_border(loc)) {
1085 return luaL_argerror(L, 1,
"coordinates are not on board");
1087 }
else if(lua_isstring(L, 1)) {
1090 return luaL_error(L,
"invalid or empty time_area ID");
1093 loc = *area.begin();
1094 }
else if(!lua_isnil(L, 1)) {
1097 return luaL_error(L,
"empty time_area");
1100 loc = *area.begin();
1103 if(lua_isnumber(L, 2)) {
1104 for_turn = luaL_checkinteger(L, 2);
1106 if(for_turn < 1 || (number_of_turns != -1 && for_turn > number_of_turns)) {
1107 return luaL_argerror(L, 2,
"turn number out of range");
1128 if (!
board().
map().is_village(loc))
1132 if (!side)
return 0;
1133 lua_pushinteger(L, side);
1145 if(!
board().
map().is_village(loc)) {
1150 const int new_side_num = lua_isnoneornil(L, 2) ? 0 : luaL_checkinteger(L, 2);
1152 team* old_side =
nullptr;
1153 team* new_side =
nullptr;
1155 if(old_side_num == new_side_num) {
1161 }
catch(
const std::out_of_range&) {
1168 }
catch(
const std::out_of_range&) {
1174 if(new_side &&
board().team_is_defeated(*new_side)) {
1207 if (!
board().
map().on_board(loc))
return 0;
1208 lua_pushinteger(L, loc.
wml_x());
1209 lua_pushinteger(L, loc.
wml_y());
1225 if (!
board().
map().on_board(loc))
return 0;
1226 lua_pushinteger(L, loc.
wml_x());
1227 lua_pushinteger(L, loc.
wml_y());
1240 std::string m = luaL_checkstring(L, 1);
1246 return luaL_argerror(L, 1, (
"Cannot find ressource with id '" + m +
"'").c_str());
1258 std::string m = luaL_checkstring(L, 1);
1264 return luaL_argerror(L, 1, (
"Cannot find era with id '" + m +
"'").c_str());
1277 LOG_LUA <<
"impl_game_config_get";
1278 char const *m = luaL_checkstring(L, 2);
1293 if(strcmp(m,
"global_traits") == 0) {
1296 const std::string&
id = trait[
"id"];
1300 lua_pushstring(L,
id.c_str());
1329 LOG_LUA <<
"impl_game_config_set";
1330 char const *m = luaL_checkstring(L, 2);
1354 static config find_addon(
const std::string&
type,
const std::string&
id)
1363 const char* m = luaL_checkstring(L, 2);
1381 struct end_level_committer {
1383 ~end_level_committer() {
1384 pc_.set_end_level_data(data_);
1395 const char* m = luaL_checkstring(L, 2);
1411 data->~end_level_data();
1417 void*
p = lua_touserdata(L, lua_upvalueindex(1));
1419 if(lua_type(L, 2) == LUA_TNUMBER) {
1422 size_t i = luaL_checkinteger(L, 2);
1424 lua_createtable(L, 2, 0);
1425 lua_pushstring(L,
"options");
1433 auto iter =
settings.addons.begin();
1434 std::advance(iter,
i);
1436 iter->second.write(cfg);
1437 cfg[
"id"] = iter->first;
1439 lua_createtable(L, 2, 0);
1440 lua_pushstring(L,
"addon");
1448 char const *m = luaL_checkstring(L, 2);
1476 if(strcmp(m,
"savegame") == 0) {
1478 if(
savegame == saved_game_mode::type::no) {
1479 lua_pushboolean(L,
false);
1485 if(strcmp(m,
"side_players") == 0) {
1489 if(strcmp(m,
"addons") == 0) {
1490 for(
const auto& [
id, addon] :
settings.addons) {
1491 lua_createtable(L, 0, 4);
1493 lua_setfield(L, -2,
"id");
1495 lua_setfield(L, -2,
"name");
1496 lua_pushboolean(L, addon.required);
1497 lua_setfield(L, -2,
"required");
1498 if(addon.min_version) {
1500 lua_push(L, addon.min_version->str());
1502 lua_setfield(L, -2,
"min_version");
1508 lua_setfield(L, -2,
"version");
1510 lua_createtable(L, addon.content.size(), 0);
1511 for(
const auto& content : addon.content) {
1512 lua_createtable(L, 0, 3);
1514 lua_setfield(L, -2,
"id");
1516 lua_setfield(L, -2,
"name");
1518 lua_setfield(L, -2,
"type");
1519 lua_seti(L, -2, lua_rawlen(L, -2) + 1);
1521 lua_setfield(L, -2,
"content");
1537 void*
p = lua_touserdata(L, lua_upvalueindex(1));
1539 lua_pushinteger(L,
settings.addons.size() + 1);
1551 LOG_LUA <<
"impl_scenario_get";
1552 char const *m = luaL_checkstring(L, 2);
1561 if(strcmp(m,
"resources") == 0) {
1564 resources.push_back(find_addon(
"resource", rsrc));
1579 if(strcmp(m,
"modifications") == 0) {
1580 std::vector<config> mods;
1582 mods.push_back(find_addon(
"modification", mod));
1587 if(strcmp(m,
"end_level_data") == 0) {
1593 if(luaL_newmetatable(L,
"end level data")) {
1594 static luaL_Reg
const callbacks[] {
1596 {
"__newindex", &dispatch<&game_lua_kernel::impl_end_level_data_set>},
1598 {
nullptr,
nullptr }
1600 luaL_setfuncs(L, callbacks, 0);
1602 lua_setmetatable(L, -2);
1608 if(strcmp(m,
"mp_settings") == 0) {
1609 lua_newuserdatauv(L, 0, 0);
1610 if(luaL_newmetatable(L,
"mp settings")) {
1613 lua_setfield(L, -2,
"__index");
1616 lua_setfield(L, -2,
"__len");
1617 lua_pushstring(L,
"mp settings");
1618 lua_setfield(L, -2,
"__metatable");
1620 lua_setmetatable(L, -2);
1636 LOG_LUA <<
"impl_scenario_set";
1637 char const *m = luaL_checkstring(L, 2);
1649 if(strcmp(m,
"end_level_data") == 0) {
1653 data.proceed_to_next_level = cfg[
"proceed_to_next_level"].to_bool(
true);
1654 data.transient.carryover_report = cfg[
"carryover_report"].to_bool(
true);
1655 data.prescenario_save = cfg[
"save"].to_bool(
true);
1656 data.replay_save = cfg[
"replay_save"].to_bool(
true);
1657 data.transient.linger_mode = cfg[
"linger_mode"].to_bool(
true) && !
teams().empty();
1658 data.transient.reveal_map = cfg[
"reveal_map"].to_bool(
true);
1659 data.is_victory = cfg[
"result"] == level_result::victory;
1660 data.test_result = cfg[
"test_result"].str();
1681 return "local_choice";
1700 char const *m = luaL_checkstring(L, 2);
1708 if(strcmp(m,
"map") == 0) {
1711 if(strcmp(m,
"schedule") == 0) {
1716 if (strcmp(m,
"event_context") == 0)
1720 cfg[
"name"] = ev.
name;
1727 cfg.
add_child(
"second_weapon", *weapon);
1732 cfg[
"damage_inflicted"] = di;
1762 if (lua_isnone(L, 2)) {
1768 LOG_LUA <<
"Script says: \"" << m <<
"\"";
1777 double factor = luaL_checknumber(L, 1);
1804 if (!lua_isnoneornil(L, 1)) {
1805 int max = 2 *
teams().size();
1806 int npn = luaL_checkinteger(L, 1);
1807 if (npn <= 0 || npn > max) {
1808 return luaL_argerror(L, 1,
"side number out of range");
1825 lua_pushboolean(L,
b);
1843 const unit* u =
nullptr;
1844 int viewing_side = 0;
1846 if (lua_isuserdata(L, arg))
1850 viewing_side = u->
side();
1859 viewing_side = u->
side();
1867 return luaL_argerror(L, 1,
"invalid location");
1869 return luaL_argerror(L, arg,
"invalid location");
1873 bool ignore_units =
false, see_all =
false, ignore_teleport =
false;
1874 double stop_at = 10000;
1875 std::unique_ptr<pathfind::cost_calculator> calc;
1877 if (lua_istable(L, arg))
1879 ignore_units = luaW_table_get_def<bool>(L, arg,
"ignore_units",
false);
1880 see_all = luaW_table_get_def<bool>(L, arg,
"ignore_visibility",
false);
1881 ignore_teleport = luaW_table_get_def<bool>(L, arg,
"ignore_teleport",
false);
1883 stop_at = luaW_table_get_def<double>(L, arg,
"max_cost", stop_at);
1886 lua_pushstring(L,
"viewing_side");
1888 if (!lua_isnil(L, -1)) {
1889 int i = luaL_checkinteger(L, -1);
1890 if (
i >= 1 &&
i <=
static_cast<int>(
teams().
size())) viewing_side =
i;
1894 if(u) see_all =
true;
1895 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.");
1900 lua_pushstring(L,
"calculate");
1902 if(lua_isfunction(L, -1)) {
1907 else if (lua_isfunction(L, arg))
1915 if(!ignore_teleport) {
1916 if(viewing_side == 0) {
1917 lua_warning(L,
"wesnoth.paths.find_path: ignore_teleport=false requires a valid viewing_side; continuing with ignore_teleport=true",
false);
1918 ignore_teleport =
true;
1926 return luaL_argerror(L, 1,
"unit not found OR custom cost function not provided");
1930 teams(),
map, ignore_units,
false, see_all));
1934 &teleport_locations);
1936 int nb = res.
steps.size();
1937 lua_createtable(L, nb, 0);
1938 for (
int i = 0;
i < nb; ++
i)
1941 lua_rawseti(L, -2,
i + 1);
1957 const unit* u =
nullptr;
1959 if (lua_isuserdata(L, arg))
1969 return luaL_argerror(L, 1,
"unit not found");
1974 int viewing_side = u->
side();
1975 bool ignore_units =
false, see_all =
false, ignore_teleport =
false;
1976 int additional_turns = 0;
1978 if (lua_istable(L, arg))
1980 ignore_units = luaW_table_get_def<bool>(L, arg,
"ignore_units",
false);
1981 see_all = luaW_table_get_def<bool>(L, arg,
"ignore_visibility",
false);
1982 ignore_teleport = luaW_table_get_def<bool>(L, arg,
"ignore_teleport",
false);
1983 additional_turns = luaW_table_get_def<int>(L, arg,
"max_cost", additional_turns);
1985 lua_pushstring(L,
"viewing_side");
1987 if (!lua_isnil(L, -1)) {
1988 int i = luaL_checkinteger(L, -1);
1989 if (
i >= 1 &&
i <=
static_cast<int>(
teams().
size())) viewing_side =
i;
1993 if(u) see_all =
true;
2003 viewing_team, additional_turns, see_all, ignore_units);
2006 lua_createtable(L, nb, 0);
2007 for (
int i = 0;
i < nb; ++
i)
2011 lua_pushinteger(L,
s.curr.wml_x());
2012 lua_rawseti(L, -2, 1);
2013 lua_pushinteger(L,
s.curr.wml_y());
2014 lua_rawseti(L, -2, 2);
2015 lua_pushinteger(L,
s.move_left);
2016 lua_rawseti(L, -2, 3);
2017 lua_rawseti(L, -2,
i + 1);
2032 const unit* u =
nullptr;
2034 if (lua_isuserdata(L, arg))
2044 return luaL_argerror(L, 1,
"unit not found");
2051 return luaL_error(L,
"wesnoth.find_vision_range: requires a valid unit");
2054 std::map<map_location, int> jamming_map;
2061 lua_pushinteger(L,
d.curr.wml_x());
2062 lua_rawseti(L, -2, 1);
2063 lua_pushinteger(L,
d.curr.wml_y());
2064 lua_rawseti(L, -2, 2);
2065 lua_pushinteger(L,
d.move_left);
2066 lua_rawseti(L, -2, 3);
2067 lua_rawseti(L, -2, lua_rawlen(L, -2) + 1);
2069 for(
const auto&
e : res.
edges) {
2071 lua_pushinteger(L,
e.wml_x());
2072 lua_rawseti(L, -2, 1);
2073 lua_pushinteger(L,
e.wml_y());
2074 lua_rawseti(L, -2, 2);
2075 lua_pushinteger(L, -1);
2076 lua_rawseti(L, -2, 3);
2077 lua_rawseti(L, -2, lua_rawlen(L, -2) + 1);
2082 template<
typename T>
2085 for (
int i = 1, i_end = lua_rawlen(L, arg);
i <= i_end; ++
i)
2088 lua_rawgeti(L, arg,
i);
2089 int entry = lua_gettop(L);
2090 if (!lua_istable(L, entry)) {
2098 lua_rawgeti(L, entry, 3);
2099 if (!lua_isnumber(L, -1)) {
2100 lua_getfield(L, entry,
"side");
2101 if (!lua_isnumber(L, -1)) {
2105 int side = lua_tointeger(L, -1);
2107 lua_rawgeti(L, entry, 4);
2108 if (!lua_isstring(L, -1)) {
2109 lua_getfield(L, entry,
"type");
2110 if (!lua_isstring(L, -1)) {
2114 std::string
unit_type = lua_tostring(L, -1);
2118 lua_settop(L, entry - 1);
2122 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");
2140 std::vector<const ::unit*> real_units;
2141 typedef std::vector<std::tuple<map_location, int, std::string>> unit_type_vector;
2147 real_units.push_back(
unit);
2149 else if (!filter.
null())
2151 for(const ::unit* match :
unit_filter(filter).all_matches_on_map()) {
2152 if(match->get_location().valid()) {
2153 real_units.push_back(match);
2163 real_units.push_back(&(*ui));
2168 if (lua_istable(L, arg))
2176 return luaL_argerror(L, 1,
"unit(s) not found");
2179 int viewing_side = 0;
2180 bool ignore_units =
true, see_all =
true, ignore_teleport =
false,
debug =
false, use_max_moves =
false;
2182 if (lua_istable(L, arg))
2184 lua_pushstring(L,
"ignore_units");
2186 if (!lua_isnil(L, -1))
2192 lua_pushstring(L,
"ignore_teleport");
2194 if (!lua_isnil(L, -1))
2200 lua_pushstring(L,
"viewing_side");
2202 if (!lua_isnil(L, -1))
2204 int i = luaL_checkinteger(L, -1);
2205 if (
i >= 1 &&
i <=
static_cast<int>(
teams().
size()))
2212 lua_pushstring(L,
"debug");
2214 if (!lua_isnil(L, -1))
2220 lua_pushstring(L,
"use_max_moves");
2222 if (!lua_isnil(L, -1))
2239 const terrain_filter t_filter(filter, &fc,
false);
2244 const team& viewing_team = viewing_side
2249 ignore_units, !ignore_teleport, viewing_team, see_all, ignore_units);
2251 for (const ::unit*
const u : real_units)
2253 cost_map.
add_unit(*u, use_max_moves);
2255 for (
const unit_type_vector::value_type& fu :
fake_units)
2258 cost_map.
add_unit(std::get<0>(fu), ut, std::get<1>(fu));
2267 std::stringstream
s;
2283 lua_pushinteger(L, loc.wml_x());
2284 lua_rawseti(L, -2, 1);
2286 lua_pushinteger(L, loc.wml_y());
2287 lua_rawseti(L, -2, 2);
2289 lua_pushinteger(L, cost_map.
get_pair_at(loc).first);
2290 lua_rawseti(L, -2, 3);
2292 lua_pushinteger(L, cost_map.
get_pair_at(loc).second);
2293 lua_rawseti(L, -2, 4);
2295 lua_rawseti(L, -2, counter);
2305 return reinterpret_cast<int*
>(luaL_checkudata(L, idx,
labelKey));
2310 const char* m = luaL_checkstring(L, 2);
2318 int fade = luaL_optinteger(L, 2, -1);
2356 double width_ratio = 0;
2358 int lifetime = 2'000, fadeout = 100;
2364 if(lua_istable(L, 2)) {
2366 size = luaL_checkinteger(L, -1);
2370 width = lua_tointegerx(L, -1, &found_number);
2374 if(!value.empty() && value.back() ==
'%') {
2375 value.remove_suffix(1);
2376 width_ratio = std::stoi(std::string(value)) / 100.0;
2377 }
else throw std::invalid_argument(value.data());
2378 }
catch(std::invalid_argument&) {
2379 return luaL_argerror(L, -1,
"max_width should be integer or percentage");
2385 if(lua_isstring(L, -1)) {
2388 auto vec = lua_check<std::vector<int>>(L, -1);
2389 if(vec.size() != 3) {
2390 return luaL_error(L,
"floating label text color should be a hex string or an array of 3 integers");
2398 if(lua_isstring(L, -1)) {
2401 auto vec = lua_check<std::vector<int>>(L, -1);
2402 if(vec.size() != 3) {
2403 return luaL_error(L,
"floating label background color should be a hex string or an array of 3 integers");
2411 bgcolor.a = luaL_checkinteger(L, -1);
2416 lifetime = lua_tointegerx(L, -1, &found_number);
2419 if(value ==
"unlimited") {
2422 return luaL_argerror(L, -1,
"duration should be integer or 'unlimited'");
2427 fadeout = lua_tointeger(L, -1);
2433 static const char*
options[] = {
"left",
"center",
"right"};
2437 static const char*
options[] = {
"top",
"center",
"bottom"};
2450 int handle_idx = lua_gettop(L);
2457 if(width_ratio > 0) {
2458 width =
static_cast<int>(std::round(
rect.w * width_ratio));
2463 x =
rect.x + loc.wml_x();
2469 x =
rect.x +
rect.w / 2 + loc.wml_x();
2483 switch(vertical_alignment) {
2485 y =
rect.y + loc.wml_y();
2488 y =
rect.y +
rect.h / 2 + loc.wml_y();
2495 y =
rect.y +
rect.h - loc.wml_y() -
static_cast<int>(
size * 1.5);
2509 lua_settop(L, handle_idx);
2510 if(luaL_newmetatable(L,
labelKey)) {
2512 static const luaL_Reg methods[] = {
2513 {
"remove", &dispatch<&game_lua_kernel::intf_remove_floating_label>},
2514 {
"move", &dispatch<&game_lua_kernel::intf_move_floating_label>},
2515 {
"replace", &dispatch2<&game_lua_kernel::intf_set_floating_label, false>},
2517 {
nullptr,
nullptr }
2519 luaL_setfuncs(L, methods, 0);
2522 lua_setmetatable(L, handle_idx);
2523 lua_settop(L, handle_idx);
2549 return luaL_error(L,
"Attempted to move a unit while the map is locked");
2554 if (!
map().on_board(loc)) {
2555 return luaL_argerror(L, 2,
"invalid location");
2566 if (!
map().on_board(loc))
2567 return luaL_argerror(L, 1,
"invalid location");
2573 }
else if(!lua_isnoneornil(L, 1)) {
2574 const vconfig* vcfg =
nullptr;
2576 if (!
map().on_board(loc)) {
2579 if (!
map().on_board(loc))
2580 return luaL_argerror(L, 2,
"invalid location");
2585 u->set_location(loc);
2604 return luaL_error(L,
"Attempted to remove a unit while the map is locked");
2612 if (!
map().on_board(loc)) {
2613 return luaL_argerror(L, 1,
"invalid location");
2618 t.recall_list().erase_if_matches_id(u->
id());
2620 return luaL_argerror(L, 1,
"can't erase private units");
2623 if (!
map().on_board(loc)) {
2624 return luaL_argerror(L, 1,
"invalid location");
2627 return luaL_argerror(L, 1,
"expected unit or location");
2643 return luaL_error(L,
"Attempted to move a unit while the map is locked");
2647 int side = lua_tointeger(L, 2);
2648 if (
static_cast<unsigned>(side) >
teams().
size()) side = 0;
2652 u = lu->get_shared();
2653 if(lu->on_recall_list() && lu->on_recall_list() == side) {
2654 return luaL_argerror(L, 1,
"unit already on recall list");
2657 const vconfig* vcfg =
nullptr;
2669 std::size_t uid = u->underlying_id();
2670 t.recall_list().erase_by_underlying_id(uid);
2671 t.recall_list().add(u);
2676 u->anim_comp().clear_haloes();
2678 lu->lua_unit::~lua_unit();
2692 return luaL_error(L,
"Attempted to remove a unit while the map is locked");
2700 u->anim_comp().clear_haloes();
2701 }
else if (
int side = lu->on_recall_list()) {
2704 t.recall_list().erase_if_matches_id(u->id());
2710 lu->lua_unit::~lua_unit();
2726 if (!lua_isnoneornil(L, 2)) {
2730 const vconfig* vcfg =
nullptr;
2738 if (!res.
valid())
return 0;
2739 lua_pushinteger(L, res.
wml_x());
2740 lua_pushinteger(L, res.
wml_y());
2756 if (!lua_isnoneornil(L, 3)) {
2773 const vconfig* vcfg =
nullptr;
2803 char const *m = luaL_checkstring(L, 2);
2807 if(lua_isboolean(L, 3)) {
2809 if(!lua_isnoneornil(L, 4)) {
2812 }
else if(!lua_isnoneornil(L, 3)) {
2833 }
else if(lua_isstring(L, 2)) {
2834 char const *m = luaL_checkstring(L, 2);
2854 }
else if(lua_isstring(L, 2)) {
2855 char const *m = luaL_checkstring(L, 2);
2875 }
else if(lua_isstring(L, 2)) {
2876 char const *m = luaL_checkstring(L, 2);
2896 }
else if(lua_isstring(L, 2)) {
2897 char const *m = luaL_checkstring(L, 2);
2913 char const *m = luaL_checkstring(L, 2);
2927 char const *m = luaL_checkstring(L, 2);
2929 if (!utp)
return luaL_argerror(L, 2,
"unknown unit type");
2930 if(lua_isstring(L, 3)) {
2931 const std::string& m2 = lua_tostring(L, 3);
2932 if(!utp->
has_variation(m2))
return luaL_argerror(L, 2,
"unknown unit variation");
2946 lua_createtable(L, 0, 4);
2948 lua_setfield(L, -2,
"poisoned");
2949 lua_pushnumber(L, cmb.
slowed);
2950 lua_setfield(L, -2,
"slowed");
2952 lua_setfield(L, -2,
"untouched");
2954 lua_setfield(L, -2,
"average_hp");
2955 lua_createtable(L,
n, 0);
2956 for (
int i = 0;
i <
n; ++
i) {
2958 lua_rawseti(L, -2,
i);
2960 lua_setfield(L, -2,
"hp_chance");
2969 lua_createtable(L, 0, 16);
2972 lua_setfield(L, -2,
"num_blows");
2973 lua_pushnumber(L, bcustats.
damage);
2974 lua_setfield(L, -2,
"damage");
2976 lua_setfield(L, -2,
"chance_to_hit");
2977 lua_pushboolean(L, bcustats.
poisons);
2978 lua_setfield(L, -2,
"poisons");
2979 lua_pushboolean(L, bcustats.
slows);
2980 lua_setfield(L, -2,
"slows");
2982 lua_setfield(L, -2,
"petrifies");
2983 lua_pushboolean(L, bcustats.
plagues);
2984 lua_setfield(L, -2,
"plagues");
2986 lua_setfield(L, -2,
"plague_type");
2987 lua_pushnumber(L, bcustats.
rounds);
2988 lua_setfield(L, -2,
"rounds");
2990 lua_setfield(L, -2,
"firststrike");
2991 lua_pushboolean(L, bcustats.
drains);
2992 lua_setfield(L, -2,
"drains");
2994 lua_setfield(L, -2,
"drain_constant");
2996 lua_setfield(L, -2,
"drain_percent");
3001 lua_setfield(L, -2,
"attack_num");
3003 lua_setfield(L, -2,
"number");
3005 if(bcustats.
weapon !=
nullptr)
3007 lua_pushstring(L, bcustats.
weapon->id().c_str());
3008 lua_setfield(L, -2,
"name");
3010 lua_setfield(L, -2,
"weapon");
3029 int arg_num = 1, att_w = -1, def_w = -1;
3033 if (lua_isnumber(L, arg_num)) {
3034 att_w = lua_tointeger(L, arg_num) - 1;
3035 if (att_w < 0 || att_w >=
static_cast<int>(att->attacks().size()))
3036 return luaL_argerror(L, arg_num,
"weapon index out of bounds");
3042 if (lua_isnumber(L, arg_num)) {
3043 def_w = lua_tointeger(L, arg_num) - 1;
3044 if (def_w < 0 || def_w >=
static_cast<int>(def->attacks().size()))
3045 return luaL_argerror(L, arg_num,
"weapon index out of bounds");
3050 def->get_location(), att_w, def_w, 0.0,
nullptr, att, def);
3067 char const *m = luaL_checkstring(L, 1);
3068 int repeats = luaL_optinteger(L, 2, 0);
3080 const char* content_for = luaL_checkstring(L, 1);
3081 const char*
id = luaL_checkstring(L, 2);
3084 if(group.content_for_ == content_for) {
3086 if(achieve.
id_ ==
id) {
3111 ERR_LUA <<
"Achievement " <<
id <<
" not found for achievement group " << content_for;
3117 ERR_LUA <<
"Achievement group " << content_for <<
" not found";
3129 const char* content_for = luaL_checkstring(L, 1);
3130 const char*
id = luaL_checkstring(L, 2);
3133 ERR_LUA <<
"Returning false for whether a player has completed an achievement due to being networked multiplayer.";
3134 lua_pushboolean(L,
false);
3150 const char* content_for = luaL_checkstring(L, 1);
3151 const char*
id = luaL_checkstring(L, 2);
3155 if(group.content_for_ == content_for) {
3156 for(
const auto& achieve : group.achievements_) {
3157 if(achieve.id_ ==
id) {
3159 cfg[
"id"] = achieve.id_;
3160 cfg[
"name"] = achieve.name_;
3161 cfg[
"name_completed"] = achieve.name_completed_;
3162 cfg[
"description"] = achieve.description_;
3163 cfg[
"description_completed"] = achieve.description_completed_;
3164 cfg[
"icon"] = achieve.icon_;
3165 cfg[
"icon_completed"] = achieve.icon_completed_;
3166 cfg[
"hidden"] = achieve.hidden_;
3167 cfg[
"achieved"] = achieve.achieved_;
3168 cfg[
"max_progress"] = achieve.max_progress_;
3169 cfg[
"current_progress"] = achieve.current_progress_;
3171 for(
const auto& sub_ach : achieve.sub_achievements_) {
3173 sub[
"id"] = sub_ach.id_;
3174 sub[
"description"] = sub_ach.description_;
3175 sub[
"icon"] = sub_ach.icon_;
3176 sub[
"achieved"] = sub_ach.achieved_;
3184 ERR_LUA <<
"Achievement " <<
id <<
" not found for achievement group " << content_for;
3190 ERR_LUA <<
"Achievement group " << content_for <<
" not found";
3206 const char* content_for = luaL_checkstring(L, 1);
3207 const char*
id = luaL_checkstring(L, 2);
3208 int amount = luaL_checkinteger(L, 3);
3209 int limit = luaL_optinteger(L, 4, 999999999);
3212 if(group.content_for_ == content_for) {
3214 if(achieve.
id_ ==
id) {
3217 ERR_LUA <<
"Attempted to progress achievement " <<
id <<
" for achievement group " << content_for <<
", is not a progressible achievement.";
3218 lua_pushinteger(L, -1);
3219 lua_pushinteger(L, -1);
3231 lua_pushinteger(L, progress);
3233 lua_pushinteger(L, -1);
3241 lua_push(L,
"Achievement " + std::string(
id) +
" not found for achievement group " + content_for);
3242 return lua_error(L);
3247 lua_push(L,
"Achievement group " + std::string(content_for) +
" not found");
3248 return lua_error(L);
3260 const char* content_for = luaL_checkstring(L, 1);
3261 const char*
id = luaL_checkstring(L, 2);
3262 const char* sub_id = luaL_checkstring(L, 3);
3265 ERR_LUA <<
"Returning false for whether a player has completed an achievement due to being networked multiplayer.";
3266 lua_pushboolean(L,
false);
3282 const char* content_for = luaL_checkstring(L, 1);
3283 const char*
id = luaL_checkstring(L, 2);
3284 const char* sub_id = luaL_checkstring(L, 3);
3287 if(group.content_for_ == content_for) {
3289 if(achieve.
id_ ==
id) {
3296 if(sub_ach.
id_ == sub_id) {
3312 lua_push(L,
"Sub-achievement " + std::string(
id) +
" not found for achievement" +
id +
" in achievement group " + content_for);
3313 return lua_error(L);
3317 lua_push(L,
"Achievement " + std::string(
id) +
" not found for achievement group " + content_for);
3318 return lua_error(L);
3323 lua_push(L,
"Achievement group " + std::string(content_for) +
" not found");
3324 return lua_error(L);
3360 if(lua_isnoneornil(L, 1)) {
3365 if(!
map().on_board(loc))
return luaL_argerror(L, 1,
"not on board");
3366 bool highlight =
true;
3367 if(!lua_isnoneornil(L, 2))
3397 lua_pushboolean(L, skipping);
3408 if (!lua_isnone(L, 1)) {
3420 int user_choice_index;
3421 int random_choice_index;
3422 int ai_choice_index;
3424 lua_synchronize(lua_State *l,
const std::string& descr,
int user_index,
int random_index = 0,
int ai_index = 0)
3426 , user_choice_index(user_index)
3427 , random_choice_index(random_index)
3428 , ai_choice_index(ai_index != 0 ? ai_index : user_index)
3434 bool is_local_ai = lua_kernel_base::get_lua_kernel<game_lua_kernel>(L).board().get_team(side).is_local_ai();
3436 query_lua(side, is_local_ai ? ai_choice_index : user_choice_index, cfg);
3443 if(random_choice_index != 0 && lua_isfunction(L, random_choice_index)) {
3444 query_lua(side, random_choice_index, cfg);
3454 void query_lua(
int side,
int function_index,
config& cfg)
const
3456 lua_pushvalue(L, function_index);
3457 lua_pushnumber(L, side);
3460 static const char*
msg =
"function returned to wesnoth.sync.[multi_]evaluate a table which was partially invalid";
3461 lua_kernel_base::get_lua_kernel<game_lua_kernel>(L).log_error(
msg);
3462 lua_warning(L,
msg,
false);
3469 virtual bool is_visible()
const override {
return false; }
3483 std::string tagname =
"input";
3490 if(!lua_isfunction(L, nextarg) &&
luaW_totstring(L, nextarg, desc) ) {
3493 if(lua_isfunction(L, nextarg)) {
3494 human_func = nextarg++;
3497 return luaL_argerror(L, nextarg,
"expected a function");
3499 if(lua_isfunction(L, nextarg)) {
3500 ai_func = nextarg++;
3502 side_for = lua_tointeger(L, nextarg);
3518 std::string tagname =
"input";
3522 std::vector<int> sides_for;
3525 if(!lua_isfunction(L, nextarg) &&
luaW_totstring(L, nextarg, desc) ) {
3528 if(lua_isfunction(L, nextarg)) {
3529 human_func = nextarg++;
3532 return luaL_argerror(L, nextarg,
"expected a function");
3534 if(lua_isfunction(L, nextarg)) {
3535 null_func = nextarg++;
3537 sides_for = lua_check<std::vector<int>>(L, nextarg++);
3552 lua_pushvalue(L, 1);
3567 std::set<map_location> res;
3569 const terrain_filter t_filter(filter, &fc,
false);
3571 t_filter.get_locations(res, *
luaW_tounit(L, 2),
true);
3573 t_filter.get_locations(res,
true);
3592 if (filter.
null()) {
3593 lua_pushboolean(L,
true);
3598 const terrain_filter t_filter(filter, &fc,
false);
3600 lua_pushboolean(L, t_filter.match(loc, *
luaW_tounit(L, 3)));
3602 lua_pushboolean(L, t_filter.match(loc));
3619 if (filter.
null()) {
3620 lua_pushboolean(L,
true);
3628 lua_pushboolean(L, s_filter.
match(*
t));
3630 unsigned side = luaL_checkinteger(L, 1) - 1;
3632 lua_pushboolean(L, s_filter.
match(side + 1));
3643 team_i = luaL_checkinteger(L, 1);
3645 std::string
flag = luaL_optlstring(L, 2,
"",
nullptr);
3646 std::string color = luaL_optlstring(L, 3,
"",
nullptr);
3648 if(
flag.empty() && color.empty()) {
3651 if(team_i < 1 ||
static_cast<std::size_t
>(team_i) >
teams().
size()) {
3652 return luaL_error(L,
"set_side_id: side number %d out of range", team_i);
3656 if(!color.empty()) {
3671 side_num =
t->side();
3673 side_num = luaL_checkinteger(L, 1);
3675 std::string
path = luaL_checkstring(L, 2);
3680 if(strcmp(action,
"delete") == 0) {
3685 std::size_t len = std::string::npos, open_brak =
path.find_last_of(
'[');
3686 std::size_t dot =
path.find_last_of(
'.');
3687 if(open_brak != len) {
3688 len = open_brak - dot - 1;
3699 side_num =
t->side();
3701 side_num = luaL_checkinteger(L, 1);
3703 if(lua_isstring(L, 2)) {
3704 std::string file = luaL_checkstring(L, 2);
3706 std::string
err =
formatter() <<
"Could not load AI for side " << side_num <<
" from file " << file;
3707 lua_pushlstring(L,
err.c_str(),
err.length());
3708 return lua_error(L);
3720 side_num =
t->side();
3722 side_num = luaL_checkinteger(L, 1);
3726 cfg =
config {
"ai", cfg};
3728 bool added_dummy_stage =
false;
3730 added_dummy_stage =
true;
3734 if(added_dummy_stage) {
3736 if(iter->key ==
"stage" && iter->cfg[
"name"] ==
"empty") {
3737 iter = cfg.
erase(iter);
3747 unsigned i = luaL_checkinteger(L, 1);
3748 if(i < 1 || i >
teams().
size())
return 0;
3760 LOG_LUA <<
"intf_get_sides called: this = " << std::hex <<
this << std::dec <<
" myname = " <<
my_name();
3761 std::vector<int> sides;
3775 lua_createtable(L, sides.size(), 0);
3777 for(
int side : sides) {
3779 lua_rawseti(L, -2,
index);
3796 char const *m = luaL_checkstring(L, 2);
3798 if (sm ==
"advance") {
3802 if (sm !=
"advancement" && sm !=
"object" && sm !=
"trait") {
3803 return luaL_argerror(L, 2,
"unknown modification type");
3805 bool write_to_mods =
true;
3806 if (!lua_isnone(L, 4)) {
3810 write_to_mods =
false;
3828 std::string tag = luaL_optstring(L, 3,
"object");
3834 if(obj.matches(filter)) {
3835 obj[
"duration"] =
"now";
3854 if(lua_isboolean(L, 2)) {
3857 if(lua_isboolean(L, 3)) {
3871 char const *ty = luaL_checkstring(L, 1);
3874 std::stringstream ss;
3875 ss <<
"unknown unit type: '" << ty <<
"'";
3876 return luaL_argerror(L, 1, ss.str().c_str());
3893 std::string team_name;
3896 std::vector<std::string> team_names;
3897 std::transform(
teams.begin(),
teams.end(), std::back_inserter(team_names),
3898 [&](
int team) { return game_state_.get_disp_context().get_team(team).team_name(); });
3901 team_name = cfg[
"team_name"].str();
3906 team_name, cfg[
"name"], cfg[
"visible_in_fog"].to_bool(
true),
3907 cfg[
"submerge"].to_double(0), cfg[
"z_order"].to_double(0));
3920 char const *m = lua_tostring(L, 2);
3937 const int nargs = lua_gettop(L);
3938 if(nargs < 2 || nargs > 3) {
3939 return luaL_error(L,
"Wrong number of arguments to ai.log_replay() - should be 2 or 3 arguments.");
3941 const std::string key = nargs == 2 ? luaL_checkstring(L, 1) : luaL_checkstring(L, 2);
3947 }
else if(!lua_isstring(L, 3)) {
3948 return luaL_argerror(L, 3,
"accepts only string or config");
3971 cfg.
add_child(
"filter_lua")[
"code"] =
"<function>";
3981 if(lua_isstring(L, idx)) {
3982 return lua_tostring(L, idx);
4001 using namespace std::literals;
4006 }
else if(is_menu_item) {
4008 return luaL_argerror(L, 1,
"non-empty id is required for a menu item");
4010 name =
"menu item " +
id;
4012 if(
id.empty() && name.empty()) {
4013 return luaL_argerror(L, 1,
"either a name or id is required");
4016 if(new_handler.valid()) {
4017 bool has_lua_filter =
false;
4021 int filterIdx = lua_gettop(L);
4024 if(lua_isfunction(L, filterIdx)) {
4025 int fcnIdx = lua_absindex(L, -1);
4026 new_handler->add_filter(std::make_unique<lua_event_filter>(*
this, fcnIdx,
luaW_table_get_def(L, 1,
"filter_args",
config())));
4027 has_lua_filter =
true;
4029 #define READ_ONE_FILTER(key) \
4031 if(luaW_tableget(L, filterIdx, key)) { \
4032 if(lua_isstring(L, -1)) { \
4033 filters.add_child("insert_tag", config{ \
4034 "name", "filter_" key, \
4035 "variable", luaL_checkstring(L, -1) \
4038 filters.add_child("filter_" key, luaW_checkconfig(L, -1)); \
4048 #undef READ_ONE_FILTER
4050 filters[
"filter_formula"] = luaL_checkstring(L, -1);
4054 new_handler->read_filters(filters);
4060 if(has_lua_filter) {
4065 new_handler->register_wml_event(*
this);
4075 template<
bool is_menu_item>
4082 return luaL_argerror(L, 1,
"must not be empty");
4086 name =
"menu item " + name;
4089 if(new_handler.valid()) {
4104 bool delayed_variable_substitution = cfg[
"delayed_variable_substitution"].to_bool(
true);
4105 if(delayed_variable_substitution) {
4132 lua_pushinteger(L, color.r);
4133 lua_pushinteger(L, color.g);
4134 lua_pushinteger(L, color.b);
4143 auto vec = lua_check<std::vector<uint8_t>>(L, 1);
4144 if(vec.size() != 4) {
4147 color_t fade{vec[0], vec[1], vec[2], vec[3]};
4165 lua_Integer delay = luaL_checkinteger(L, 1);
4173 const unsigned final = SDL_GetTicks() + delay;
4177 }
while (
static_cast<int>(
final - SDL_GetTicks()) > 0);
4201 std::string team_name;
4204 if(lua_gettop(L) == 1 && lua_istable(L, 1)) {
4205 using namespace std::literals;
4208 team_name = luaL_optstring(L, 2,
"");
4222 switch(lua_type(L, 2)) {
4224 case LUA_TNONE:
case LUA_TNIL:
4229 if(
size_t n = luaL_checkinteger(L, 2);
n > 0 &&
n <=
teams().size()) {
4269 for (
const int side : filter.
get_teams()){
4291 int side = cfg[
"side"];
4301 lua_getfield(L, -1,
"ca_ptr");
4315 lua_getfield(L, -1,
"stg_ptr");
4324 lua_createtable(L, 0, 0);
4326 lua_pushstring(L,
"name");
4327 lua_pushstring(L,
c->get_name().c_str());
4330 lua_pushstring(L,
"engine");
4331 lua_pushstring(L,
c->get_engine().c_str());
4334 lua_pushstring(L,
"id");
4335 lua_pushstring(L,
c->get_id().c_str());
4338 if (ct ==
"candidate_action") {
4339 lua_pushstring(L,
"ca_ptr");
4340 lua_pushlightuserdata(L,
c);
4343 lua_pushstring(L,
"exec");
4348 if (ct ==
"stage") {
4349 lua_pushstring(L,
"stg_ptr");
4350 lua_pushlightuserdata(L,
c);
4353 lua_pushstring(L,
"exec");
4359 std::vector<std::string> c_types =
c->get_children_types();
4361 for (std::vector<std::string>::const_iterator
t = c_types.begin();
t != c_types.end(); ++
t)
4363 std::vector<ai::component*> children =
c->get_children(*
t);
4364 std::string
type = *
t;
4365 if (
type ==
"aspect" ||
type ==
"goal" ||
type ==
"engine")
4370 lua_pushstring(L,
type.c_str());
4371 lua_createtable(L, 0, 0);
4373 for (std::vector<ai::component*>::const_iterator
i = children.begin();
i != children.end(); ++
i)
4375 lua_pushstring(L, (*i)->get_name().c_str());
4406 side = luaL_checkinteger(L, 1);
4413 std::vector<ai::component*> engines =
c->get_children(
"engine");
4415 for (std::vector<ai::component*>::const_iterator
i = engines.begin();
i != engines.end(); ++
i)
4417 if ((*i)->get_name() ==
"lua")
4427 if (lua_engine ==
nullptr)
4440 LOG_LUA <<
"Created new dummy lua-engine for debug_ai().";
4450 lua_pushstring(L,
"components");
4476 if(lua_isboolean(L, 1)) {
4501 std::set<map_location> locs;
4504 if(lua_gettop(L) == 1) {
4507 id = cfg[
"id"].str();
4508 const terrain_filter filter(cfg, &
game_state_,
false);
4509 filter.get_locations(locs,
true);
4512 id = luaL_checkstring(L, 1);
4513 if(!lua_isnoneornil(L, 3))
4518 const terrain_filter filter(cfg, &
game_state_,
false);
4519 filter.get_locations(locs,
true);
4527 LOG_LUA <<
"Lua inserted time_area '" <<
id <<
"'";
4536 const char *
id = luaL_checkstring(L, 1);
4538 LOG_LUA <<
"Lua removed time_area '" <<
id <<
"'";
4548 if(area_index < 0) {
4555 std::string area_id = luaL_checkstring(L, 1);
4557 if(
auto iter = std::find(area_ids.begin(), area_ids.end(), area_id); iter == area_ids.end()) {
4571 if(luaL_testudata(L, 1,
"schedule")) {
4581 ERR_LUA <<
"attempted to to replace ToD schedule with empty schedule";
4587 LOG_LUA <<
"replaced ToD schedule";
4595 int x = luaL_checkinteger(L, 1), y = luaL_checkinteger(L, 2);
4609 lua_report_generator(lua_State *L,
const std::string &
n)
4618 if (!
luaW_getglobal(L,
"wesnoth",
"interface",
"game_display", name))
4642 char const *m = luaL_checkstring(L, 2);
4644 lua_pushvalue(L, 2);
4645 lua_pushvalue(L, -2);
4656 char const *m = luaL_checkstring(L, 2);
4657 lua_pushvalue(L, 2);
4658 lua_pushvalue(L, 3);
4690 if (dst == u->get_location() || !
map().on_board(dst)) {
4694 if (!
map().on_board(vacant_dst)) {
4705 std::vector<map_location> teleport_path;
4706 teleport_path.push_back(src_loc);
4707 teleport_path.push_back(vacant_dst);
4714 u->anim_comp().set_standing();
4721 if (
map().is_village(vacant_dst)) {
4740 const std::string& logger = lua_isstring(L, 2) ? luaL_checkstring(L, 1) :
"";
4741 const std::string&
msg = lua_isstring(L, 2) ? luaL_checkstring(L, 2) : luaL_checkstring(L, 1);
4743 if(logger ==
"wml" || logger ==
"WML") {
4757 lua_pushboolean(L,
fog ?
t.fogged(loc) :
t.shrouded(loc));
4775 bool affect_normal_fog =
false;
4776 if(lua_isboolean(L, -1)) {
4779 std::set<int> sides;
4781 sides.insert(
t->side());
4782 }
else if(lua_isnumber(L, 1)) {
4783 sides.insert(lua_tointeger(L, 1));
4784 }
else if(lua_istable(L, 1) && lua_istable(L, 2)) {
4785 const auto& v = lua_check<std::vector<int>>(L, 1);
4786 sides.insert(v.begin(), v.end());
4789 sides.insert(
t.side()+1);
4794 for(
const int &side_num : sides) {
4795 if(side_num < 1 ||
static_cast<std::size_t
>(side_num) >
teams().
size()) {
4801 t.remove_fog_override(locs);
4802 if(affect_normal_fog) {
4805 }
else if(!affect_normal_fog) {
4807 t.add_fog_override(locs);
4825 const std::string name = luaL_checkstring(L, 1);
4830 if(!
luaW_getglobal(L,
"wesnoth",
"custom_synced_commands", name)) {
4831 return luaL_argerror(L, 1,
"Unknown synced command");
4834 cmd_tag[
"name"] = name;
4835 if(!lua_isnoneornil(L, 2)) {
4896 cmd_log_ <<
"Registering game-specific wesnoth lib functions...\n";
4899 static luaL_Reg
const callbacks[] {
4904 {
"allow_undo", &dispatch<&game_lua_kernel::intf_allow_undo > },
4905 {
"cancel_action", &dispatch<&game_lua_kernel::intf_cancel_action > },
4906 {
"log_replay", &dispatch<&game_lua_kernel::intf_log_replay > },
4907 {
"log", &dispatch<&game_lua_kernel::intf_log > },
4908 {
"redraw", &dispatch<&game_lua_kernel::intf_redraw > },
4909 {
"simulate_combat", &dispatch<&game_lua_kernel::intf_simulate_combat > },
4910 {
nullptr,
nullptr }
4911 };lua_getglobal(L,
"wesnoth");
4912 if (!lua_istable(L,-1)) {
4915 luaL_setfuncs(L, callbacks, 0);
4917 lua_setglobal(L,
"wesnoth");
4919 lua_getglobal(L,
"gui");
4920 lua_pushcfunction(L, &dispatch<&game_lua_kernel::intf_gamestate_inspector>);
4921 lua_setfield(L, -2,
"show_inspector");
4927 static luaL_Reg
const test_callbacks[] {
4928 {
"fire_wml_menu_item", &dispatch<&game_lua_kernel::intf_fire_wml_menu_item> },
4929 {
nullptr,
nullptr }
4931 luaL_setfuncs(L, test_callbacks, 0);
4932 lua_setglobal(L,
"unit_test");
4958 cmd_log_ <<
"Adding terrain_types table...\n";
4959 lua_getglobal(L,
"wesnoth");
4960 lua_newuserdatauv(L, 0, 0);
4961 lua_createtable(L, 0, 2);
4962 lua_pushcfunction(L, &dispatch<&game_lua_kernel::impl_get_terrain_info>);
4963 lua_setfield(L, -2,
"__index");
4964 lua_pushstring(L,
"terrain types");
4965 lua_setfield(L, -2,
"__metatable");
4966 lua_setmetatable(L, -2);
4967 lua_setfield(L, -2,
"terrain_types");
4971 cmd_log_ <<
"Adding ai elements table...\n";
4976 cmd_log_ <<
"Adding wesnoth current table...\n";
4978 lua_getglobal(L,
"wesnoth");
4979 lua_newuserdatauv(L, 0, 0);
4980 lua_createtable(L, 0, 2);
4981 lua_pushcfunction(L, &dispatch<&game_lua_kernel::impl_current_get>);
4982 lua_setfield(L, -2,
"__index");
4983 lua_pushstring(L,
"current config");
4984 lua_setfield(L, -2,
"__metatable");
4985 lua_setmetatable(L, -2);
4986 lua_setfield(L, -2,
"current");
4990 lua_getglobal(L,
"wml");
4991 static luaL_Reg
const wml_callbacks[] {
4995 {
"get_variable", &dispatch<&game_lua_kernel::intf_get_variable>},
4996 {
"set_variable", &dispatch<&game_lua_kernel::intf_set_variable>},
4997 {
"get_all_vars", &dispatch<&game_lua_kernel::intf_get_all_vars>},
4998 {
nullptr,
nullptr }
5000 luaL_setfuncs(L, wml_callbacks, 0);
5005 static luaL_Reg
const map_callbacks[] {
5012 {
"get_owner", &dispatch<&game_lua_kernel::intf_get_village_owner>},
5013 {
"set_owner", &dispatch<&game_lua_kernel::intf_set_village_owner>},
5015 {
"add_label", &dispatch<&game_lua_kernel::intf_add_label>},
5016 {
"remove_label", &dispatch<&game_lua_kernel::intf_remove_label>},
5017 {
"get_label", &dispatch<&game_lua_kernel::intf_get_label>},
5019 {
"place_area", &dispatch<&game_lua_kernel::intf_add_time_area>},
5020 {
"remove_area", &dispatch<&game_lua_kernel::intf_remove_time_area>},
5021 {
"get_area", &dispatch<&game_lua_kernel::intf_get_time_area>},
5023 {
"find", &dispatch<&game_lua_kernel::intf_get_locations>},
5024 {
"matches", &dispatch<&game_lua_kernel::intf_match_location>},
5026 {
nullptr,
nullptr }
5028 luaL_setfuncs(L, map_callbacks, 0);
5032 cmd_log_ <<
"Adding units module...\n";
5033 static luaL_Reg
const unit_callbacks[] {
5036 {
"erase", &dispatch<&game_lua_kernel::intf_erase_unit>},
5037 {
"extract", &dispatch<&game_lua_kernel::intf_extract_unit>},
5038 {
"matches", &dispatch<&game_lua_kernel::intf_match_unit>},
5039 {
"select", &dispatch<&game_lua_kernel::intf_select_unit>},
5040 {
"to_map", &dispatch<&game_lua_kernel::intf_put_unit>},
5041 {
"to_recall", &dispatch<&game_lua_kernel::intf_put_recall_unit>},
5043 {
"teleport", &dispatch<&game_lua_kernel::intf_teleport>},
5045 {
"ability", &dispatch<&game_lua_kernel::intf_unit_ability>},
5056 {
"find_on_map", &dispatch<&game_lua_kernel::intf_get_units>},
5057 {
"find_on_recall", &dispatch<&game_lua_kernel::intf_get_recall_units>},
5058 {
"get", &dispatch<&game_lua_kernel::intf_get_unit>},
5059 {
"get_hovered", &dispatch<&game_lua_kernel::intf_get_displayed_unit>},
5060 {
"create_animator", &dispatch<&game_lua_kernel::intf_create_animator>},
5063 {
nullptr,
nullptr }
5065 lua_getglobal(L,
"wesnoth");
5067 luaL_setfuncs(L, unit_callbacks, 0);
5068 lua_setfield(L, -2,
"units");
5072 cmd_log_ <<
"Adding sides module...\n";
5073 static luaL_Reg
const side_callbacks[] {
5074 {
"is_enemy", &dispatch<&game_lua_kernel::intf_is_enemy> },
5075 {
"matches", &dispatch<&game_lua_kernel::intf_match_side> },
5076 {
"set_id", &dispatch<&game_lua_kernel::intf_set_side_id> },
5081 {
"find", &dispatch<&game_lua_kernel::intf_get_sides> },
5082 {
"get", &dispatch<&game_lua_kernel::intf_get_side> },
5083 {
"create", &dispatch<&game_lua_kernel::intf_create_side> },
5085 {
"place_shroud", &dispatch2<&game_lua_kernel::intf_toggle_shroud, true>},
5086 {
"remove_shroud", &dispatch2<&game_lua_kernel::intf_toggle_shroud, false>},
5087 {
"override_shroud", &dispatch<&game_lua_kernel::intf_override_shroud>},
5088 {
"is_shrouded", &dispatch2<&game_lua_kernel::intf_get_fog_or_shroud, false>},
5090 {
"place_fog", &dispatch2<&game_lua_kernel::intf_toggle_fog, false>},
5091 {
"remove_fog", &dispatch2<&game_lua_kernel::intf_toggle_fog, true>},
5092 {
"is_fogged", &dispatch2<&game_lua_kernel::intf_get_fog_or_shroud, true>},
5093 {
nullptr,
nullptr }
5095 std::vector<lua_cpp::Reg>
const cpp_side_callbacks {
5096 {
"add_ai_component", std::bind(
intf_modify_ai, std::placeholders::_1,
"add")},
5097 {
"delete_ai_component", std::bind(
intf_modify_ai, std::placeholders::_1,
"delete")},
5098 {
"change_ai_component", std::bind(
intf_modify_ai, std::placeholders::_1,
"change")},
5102 lua_getglobal(L,
"wesnoth");
5104 luaL_setfuncs(L, side_callbacks, 0);
5106 lua_setfield(L, -2,
"sides");
5110 cmd_log_ <<
"Adding interface module...\n";
5111 static luaL_Reg
const intf_callbacks[] {
5112 {
"add_hex_overlay", &dispatch<&game_lua_kernel::intf_add_tile_overlay>},
5113 {
"remove_hex_overlay", &dispatch<&game_lua_kernel::intf_remove_tile_overlay>},
5114 {
"get_color_adjust", &dispatch<&game_lua_kernel::intf_get_color_adjust>},
5115 {
"color_adjust", &dispatch<&game_lua_kernel::intf_color_adjust>},
5116 {
"screen_fade", &dispatch<&game_lua_kernel::intf_screen_fade>},
5117 {
"delay", &dispatch<&game_lua_kernel::intf_delay>},
5118 {
"deselect_hex", &dispatch<&game_lua_kernel::intf_deselect_hex>},
5119 {
"highlight_hex", &dispatch<&game_lua_kernel::intf_highlight_hex>},
5120 {
"float_label", &dispatch<&game_lua_kernel::intf_float_label>},
5121 {
"get_displayed_unit", &dispatch<&game_lua_kernel::intf_get_displayed_unit>},
5122 {
"get_hovered_hex", &dispatch<&game_lua_kernel::intf_get_mouseover_tile>},
5123 {
"get_selected_hex", &dispatch<&game_lua_kernel::intf_get_selected_tile>},
5124 {
"lock", &dispatch<&game_lua_kernel::intf_lock_view>},
5125 {
"is_locked", &dispatch<&game_lua_kernel::intf_view_locked>},
5126 {
"scroll", &dispatch<&game_lua_kernel::intf_scroll>},
5127 {
"scroll_to_hex", &dispatch<&game_lua_kernel::intf_scroll_to_tile>},
5128 {
"skip_messages", &dispatch<&game_lua_kernel::intf_skip_messages>},
5129 {
"is_skipping_messages", &dispatch<&game_lua_kernel::intf_is_skipping_messages>},
5130 {
"zoom", &dispatch<&game_lua_kernel::intf_zoom>},
5131 {
"clear_menu_item", &dispatch<&game_lua_kernel::intf_clear_menu_item>},
5132 {
"set_menu_item", &dispatch<&game_lua_kernel::intf_set_menu_item>},
5133 {
"allow_end_turn", &dispatch<&game_lua_kernel::intf_allow_end_turn>},
5134 {
"clear_chat_messages", &dispatch<&game_lua_kernel::intf_clear_messages>},
5135 {
"end_turn", &dispatch<&game_lua_kernel::intf_end_turn>},
5137 {
"add_chat_message", &dispatch<&game_lua_kernel::intf_message>},
5138 {
"add_overlay_text", &dispatch2<&game_lua_kernel::intf_set_floating_label, true>},
5140 {
nullptr,
nullptr }
5142 lua_getglobal(L,
"wesnoth");
5144 luaL_setfuncs(L, intf_callbacks, 0);
5145 lua_setfield(L, -2,
"interface");
5149 cmd_log_ <<
"Adding achievements module...\n";
5150 static luaL_Reg
const achievement_callbacks[] {
5151 {
"set", &dispatch<&game_lua_kernel::intf_set_achievement> },
5152 {
"has", &dispatch<&game_lua_kernel::intf_has_achievement> },
5153 {
"get", &dispatch<&game_lua_kernel::intf_get_achievement> },
5154 {
"progress", &dispatch<&game_lua_kernel::intf_progress_achievement> },
5155 {
"has_sub_achievement", &dispatch<&game_lua_kernel::intf_has_sub_achievement> },
5156 {
"set_sub_achievement", &dispatch<&game_lua_kernel::intf_set_sub_achievement> },
5157 {
nullptr,
nullptr }
5159 lua_getglobal(L,
"wesnoth");
5161 luaL_setfuncs(L, achievement_callbacks, 0);
5162 lua_setfield(L, -2,
"achievements");
5166 cmd_log_ <<
"Adding audio module...\n";
5167 static luaL_Reg
const audio_callbacks[] {
5168 {
"play", &dispatch<&game_lua_kernel::intf_play_sound > },
5169 {
nullptr,
nullptr }
5171 lua_getglobal(L,
"wesnoth");
5173 luaL_setfuncs(L, audio_callbacks, 0);
5174 lua_setfield(L, -2,
"audio");
5178 cmd_log_ <<
"Adding paths module...\n";
5179 static luaL_Reg
const path_callbacks[] {
5180 {
"find_cost_map", &dispatch<&game_lua_kernel::intf_find_cost_map > },
5181 {
"find_path", &dispatch<&game_lua_kernel::intf_find_path > },
5182 {
"find_reach", &dispatch<&game_lua_kernel::intf_find_reach > },
5183 {
"find_vacant_hex", &dispatch<&game_lua_kernel::intf_find_vacant_tile > },
5184 {
"find_vision_range", &dispatch<&game_lua_kernel::intf_find_vision_range > },
5185 {
nullptr,
nullptr }
5187 lua_getglobal(L,
"wesnoth");
5189 luaL_setfuncs(L, path_callbacks, 0);
5190 lua_setfield(L, -2,
"paths");
5194 cmd_log_ <<
"Adding sync module...\n";
5195 static luaL_Reg
const sync_callbacks[] {
5200 {
nullptr,
nullptr }
5202 lua_getglobal(L,
"wesnoth");
5204 luaL_setfuncs(L, sync_callbacks, 0);
5205 lua_setfield(L, -2,
"sync");
5209 cmd_log_ <<
"Adding schedule module...\n";
5210 static luaL_Reg
const schedule_callbacks[] {
5211 {
"get_time_of_day", &dispatch<&game_lua_kernel::intf_get_time_of_day<false>>},
5212 {
"get_illumination", &dispatch<&game_lua_kernel::intf_get_time_of_day<true>>},
5213 {
"replace", &dispatch<&game_lua_kernel::intf_replace_schedule>},
5214 {
nullptr,
nullptr }
5216 lua_getglobal(L,
"wesnoth");
5218 luaL_setfuncs(L, schedule_callbacks, 0);
5219 lua_createtable(L, 0, 2);
5220 lua_setmetatable(L, -2);
5221 lua_setfield(L, -2,
"schedule");
5228 cmd_log_ <<
"Adding wml_actions table...\n";
5230 lua_getglobal(L,
"wesnoth");
5232 lua_setfield(L, -2,
"wml_actions");
5236 cmd_log_ <<
"Adding wml_conditionals table...\n";
5238 lua_getglobal(L,
"wesnoth");
5240 lua_setfield(L, -2,
"wml_conditionals");
5247 cmd_log_ <<
"Adding effects table...\n";
5249 lua_getglobal(L,
"wesnoth");
5251 lua_setfield(L, -2,
"effects");
5255 cmd_log_ <<
"Adding custom_synced_commands table...\n";
5257 lua_getglobal(L,
"wesnoth");
5259 lua_setfield(L, -2,
"custom_synced_commands");
5263 cmd_log_ <<
"Adding game_events module...\n";
5264 static luaL_Reg
const event_callbacks[] {
5265 {
"add", &dispatch<&game_lua_kernel::intf_add_event> },
5266 {
"add_repeating", &dispatch<&game_lua_kernel::intf_add_event_simple<false>> },
5267 {
"add_menu", &dispatch<&game_lua_kernel::intf_add_event_simple<true>> },
5268 {
"add_wml", &dispatch<&game_lua_kernel::intf_add_event_wml> },
5269 {
"remove", &dispatch<&game_lua_kernel::intf_remove_event> },
5270 {
"fire", &dispatch2<&game_lua_kernel::intf_fire_event, false> },
5271 {
"fire_by_id", &dispatch2<&game_lua_kernel::intf_fire_event, true> },
5272 {
nullptr,
nullptr }
5274 lua_getglobal(L,
"wesnoth");
5276 luaL_setfuncs(L, event_callbacks, 0);
5277 lua_setfield(L, -2,
"game_events");
5281 cmd_log_ <<
"Adding game_display table...\n";
5285 lua_createtable(L, 0, 2);
5286 lua_pushcfunction(L, &dispatch<&game_lua_kernel::impl_theme_items_get>);
5287 lua_setfield(L, -2,
"__index");
5288 lua_pushcfunction(L, &dispatch<&game_lua_kernel::impl_theme_items_set>);
5289 lua_setfield(L, -2,
"__newindex");
5290 lua_setmetatable(L, -2);
5291 lua_setfield(L, -2,
"game_display");
5295 cmd_log_ <<
"Adding scenario table...\n";
5299 lua_createtable(L, 0, 2);
5300 lua_pushcfunction(L, &dispatch<&game_lua_kernel::impl_scenario_get>);
5301 lua_setfield(L, -2,
"__index");
5302 lua_pushcfunction(L, &dispatch<&game_lua_kernel::impl_scenario_set>);
5303 lua_setfield(L, -2,
"__newindex");
5304 lua_setmetatable(L, -2);
5305 lua_setfield(L, -2,
"scenario");
5316 lua_pushstring(L, effect.c_str());
5334 cmd_log_ <<
"Adding races table...\n";
5337 lua_getglobal(L,
"wesnoth");
5339 lua_setfield(L, -2,
"races");
5343 cmd_log_ <<
"Running preload scripts...\n";
5366 using namespace std::literals::string_view_literals;
5367 static const std::array handled_file_tags {
5380 "modify_unit_type"sv,
5386 "terrain_graphics"sv,
5394 return std::binary_search(handled_file_tags.begin(), handled_file_tags.end(),
s);
5413 lua_createtable(L, 2, 0);
5414 lua_pushstring(L, v.key.c_str());
5415 lua_rawseti(L, -2, 1);
5417 lua_rawseti(L, -2, 2);
5418 lua_rawseti(L, -2, k++);
5453 const std::string m =
"Tag is already used: [" +
i->key +
"]";
5474 lua_pushstring(L, ev.
name.c_str());
5483 if (!
luaW_getglobal(L,
"wesnoth",
"custom_synced_commands", name)) {
5499 std::string which_effect = lua_tostring(L, lua_upvalueindex(1));
5514 lua_pushstring(L, description.c_str());
5527 int str_i = lua_gettop(L);
5530 lua_pushstring(L,
"__call");
5531 lua_pushvalue(L, str_i);
5532 lua_pushboolean(L,
true);
5533 lua_pushcclosure(L, &dispatch<&game_lua_kernel::cfun_builtin_effect>, 2);
5535 lua_pushstring(L,
"__descr");
5536 lua_pushvalue(L, str_i);
5537 lua_pushboolean(L,
false);
5538 lua_pushcclosure(L, &dispatch<&game_lua_kernel::cfun_builtin_effect>, 2);
5540 lua_setmetatable(L, -2);
5550 (lua_touserdata(L, lua_upvalueindex(1)));