118 #include <functional>
137 #define DBG_LUA LOG_STREAM(debug, log_scripting_lua)
138 #define LOG_LUA LOG_STREAM(info, log_scripting_lua)
139 #define WRN_LUA LOG_STREAM(warn, log_scripting_lua)
140 #define ERR_LUA LOG_STREAM(err, log_scripting_lua)
143 #define ERR_WML LOG_STREAM(err, log_wml)
151 template <member_callback method>
153 return ((lua_kernel_base::get_lua_kernel<game_lua_kernel>(L)).*method)(L);
159 template <member_callback2 method,
bool b>
161 return ((lua_kernel_base::get_lua_kernel<game_lua_kernel>(L)).*method)(L,
b);
211 if(!sides.
empty()) {
WRN_LUA <<
"ignoring duplicate side filter information (inline side=)"; }
213 return filter.get_teams();
217 return filter.get_teams();
224 struct queued_event_context
227 std::stack<qe const *> & stack_;
229 queued_event_context(qe
const *new_qe, std::stack<qe const*> & stack)
235 ~queued_event_context()
250 lua_pushinteger(L, disp->viewing_team().side());
251 lua_pushboolean(L, disp->show_everything());
269 anim.~unit_animator();
278 std::string which = luaL_checkstring(L, 3);
280 std::string hits_str = luaL_checkstring(L, 4);
290 if(lua_istable(L, 5)) {
291 lua_getfield(L, 5,
"target");
294 return luaL_argerror(L, 5,
"target location must be different from animated unit's location");
296 return luaL_argerror(L, 5,
"target location must be adjacent to the animated unit");
301 if(!lua_isnoneornil(L, -1)) {
307 lua_getfield(L, 5,
"value");
308 if(lua_isnumber(L, -1)) {
309 v1 = lua_tointeger(L, -1);
310 }
else if(lua_istable(L, -1)) {
311 lua_rawgeti(L, -1, 1);
312 v1 = lua_tointeger(L, -1);
314 lua_rawgeti(L, -1, 2);
315 v2 = lua_tointeger(L, -1);
317 }
else if(!lua_isnoneornil(L, -1)) {
318 return luaW_type_error(L, 5,
"value",
"number or array of two numbers");
322 lua_getfield(L, 5,
"with_bars");
323 if(lua_isboolean(L, -1)) {
325 }
else if(!lua_isnoneornil(L, -1)) {
326 return luaW_type_error(L, 5,
"with_bars", lua_typename(L, LUA_TBOOLEAN));
330 lua_getfield(L, 5,
"text");
331 if(lua_isstring(L, -1)) {
332 text = lua_tostring(L, -1);
335 }
else if(!lua_isnoneornil(L, -1)) {
340 lua_getfield(L, 5,
"color");
341 if(lua_istable(L, -1) && lua_rawlen(L, -1) == 3) {
342 int idx = lua_absindex(L, -1);
343 lua_rawgeti(L, idx, 1);
344 lua_rawgeti(L, idx, 2);
345 lua_rawgeti(L, idx, 3);
346 color =
color_t(lua_tointeger(L, -3), lua_tointeger(L, -2), lua_tointeger(L, -1));
348 }
else if(!lua_isnoneornil(L, -1)) {
353 lua_getfield(L, 5,
"primary");
355 if(!primary && !lua_isnoneornil(L, -1)) {
360 lua_getfield(L, 5,
"secondary");
362 if(!secondary && !lua_isnoneornil(L, -1)) {
366 }
else if(!lua_isnoneornil(L, 5)) {
370 anim.
add_animation(up, which, u.
get_location(), dest, v1, bars, text, color, hits, primary, secondary, v2);
398 const char* m = lua_tostring(L, 2);
406 luaL_Reg metafuncs[] {
410 {
"run", &dispatch<&game_lua_kernel::impl_run_animation>},
414 luaL_setfuncs(L, metafuncs, 0);
415 lua_pushstring(L,
"__metatable");
418 lua_setmetatable(L, -2);
428 name = cfg[
"name"].str();
431 name = luaL_optstring(L, 1,
"");
449 if(lua_isstring(L, 1) && !lua_isnumber(L, 1)) {
450 std::string
id = luaL_checkstring(L, 1);
460 return luaL_argerror(L, 1,
"expected string or location");
464 if (!ui.
valid())
return 0;
485 if (!ui.
valid())
return 0;
503 std::vector<const unit*>
units;
507 return luaL_argerror(L, 2,
"unit not found");
510 }
else if(!lua_isnoneornil(L, 2)) {
514 return luaL_argerror(L, 2,
"invalid location");
529 lua_rawseti(L, 1,
i);
549 lua_pushboolean(L,
true);
555 WRN_LUA <<
"wesnoth.units.matches called with a secondary unit (3rd argument), ";
556 WRN_LUA <<
"but unit to match was on recall list. ";
557 WRN_LUA <<
"Thus the 3rd argument is ignored.";
564 return luaL_argerror(L, 3,
"unit not found");
604 t.save_id_or_number(),
t.recall_list().find_index(u->id()));
609 lua_rawseti(L, 1,
i);
628 char const *m = luaL_checkstring(L, 1);
645 if(
data.has_child(
"primary_attack")) {
646 data.add_child(
"first",
data.mandatory_child(
"primary_attack"));
647 data.remove_children(
"primary_attack");
649 if(
data.has_child(
"secondary_attack")) {
650 data.add_child(
"second",
data.mandatory_child(
"secondary_attack"));
651 data.remove_children(
"secondary_attack");
662 lua_pushboolean(L,
b);
680 char const *m = luaL_checkstring(L, 1);
685 lua_pushboolean(L,
b);
697 char const *m = luaL_checkstring(L, 1);
709 const std::string m = luaL_checkstring(L, 1);
710 if(m.empty())
return luaL_argerror(L, 1,
"empty variable name");
711 if (lua_isnoneornil(L, 2)) {
724 cfg[
"side"] =
teams().size() + 1;
739 std::string ids(luaL_checkstring(L, 1));
742 WRN_LUA <<
"[clear_menu_item] has been given an empty id=, ignoring";
759 if(lua_istable(L, 2)) {
771 return luaL_argerror(L, 2,
"expected list of locations");
790 if(lua_istable(L, 2)) {
818 if(!
map().on_board(
loc))
return luaL_argerror(L, 1,
"not on board");
832 unsigned side_1, side_2;
836 side_1 = luaL_checkinteger(L, 1);
841 side_2 = luaL_checkinteger(L, 2);
844 lua_pushboolean(L,
board().get_team(side_1).is_enemy(side_2));
878 lua_pushstring(L, tod.
id.c_str());
879 lua_setfield(L, -2,
"id");
881 lua_setfield(L, -2,
"lawful_bonus");
883 lua_setfield(L, -2,
"bonus_modified");
884 lua_pushstring(L, tod.
image.c_str());
885 lua_setfield(L, -2,
"image");
887 lua_setfield(L, -2,
"name");
888 lua_pushstring(L, tod.
sounds.c_str());
889 lua_setfield(L, -2,
"sound");
891 lua_setfield(L, -2,
"mask");
893 lua_pushinteger(L, tod.
color.
r);
894 lua_setfield(L, -2,
"red");
895 lua_pushinteger(L, tod.
color.
g);
896 lua_setfield(L, -2,
"green");
897 lua_pushinteger(L, tod.
color.
b);
898 lua_setfield(L, -2,
"blue");
905 lua_newuserdatauv(L, 0, 1);
906 lua_pushinteger(L, area_index);
907 lua_setiuservalue(L, -2, 1);
908 if(luaL_newmetatable(L,
"schedule")) {
909 static luaL_Reg
const schedule_meta[] {
910 {
"__index", &dispatch<&game_lua_kernel::impl_schedule_get>},
911 {
"__newindex", &dispatch<&game_lua_kernel::impl_schedule_set>},
912 {
"__dir", &dispatch<&game_lua_kernel::impl_schedule_dir>},
913 {
"__len", &dispatch<&game_lua_kernel::impl_schedule_len>},
916 luaL_setfuncs(L, schedule_meta, 0);
918 lua_setmetatable(L, -2);
923 int save_top = lua_gettop(L);
924 luaL_checkudata(L, idx,
"schedule");
925 lua_getiuservalue(L, idx, 1);
926 int i = luaL_checkinteger(L, -1);
927 lua_settop(L, save_top);
937 #define SCHEDULE_GETTER(name, type) LATTR_GETTER(name, type, schedule_tag, sched)
938 #define SCHEDULE_SETTER(name, type) LATTR_SETTER(name, type, schedule_tag, sched)
939 #define SCHEDULE_VALID(name) LATTR_VALID(name, schedule_tag, sched)
943 inline static auto metatable =
"schedule";
945 schedule_tag sched{lua_kernel_base::get_lua_kernel<game_lua_kernel>(L)};
954 if(lua_isnumber(L, 2)) {
956 int i = lua_tointeger(L, 2) - 1;
957 if(i < 0 || i >=
static_cast<int>(times.size())) {
958 return luaL_argerror(L, 2,
"invalid time of day index");
970 lua_pushinteger(L, times.size());
977 if(lua_isnumber(L, 2)) {
979 int i = lua_tointeger(L, 2) - 1;
980 if(i < 0 || i >=
static_cast<int>(times.size())) {
981 return luaL_argerror(L, 2,
"invalid time of day index");
1000 if(sched.area_index >= 0) {
1001 return sched.tod_man().get_area_time_of_day(sched.area_index).id;
1003 return sched.tod_man().get_time_of_day().id;
1007 const auto& times = sched.area_index < 0 ? sched.tod_man().times() : sched.tod_man().times(sched.area_index);
1008 auto iter = std::find_if(times.begin(), times.end(), [&value](
const time_of_day& tod) {
1009 return tod.id == value;
1011 if(iter == times.end()) {
1012 std::ostringstream
err;
1013 err <<
"invalid time of day ID for ";
1014 if(sched.area_index < 0) {
1015 err <<
"global schedule";
1017 const std::string&
id = sched.tod_man().get_area_id(sched.area_index);
1019 const auto& hexes = sched.tod_man().get_area_by_index(sched.area_index);
1021 err <<
"anonymous empty time area";
1023 err <<
"anonymous time area at (" << hexes.begin()->wml_x() <<
',' << hexes.begin()->wml_y() <<
")";
1026 err <<
"time area with id=" <<
id;
1032 int n = std::distance(times.begin(), iter);
1033 if(sched.area_index < 0) {
1034 sched.tod_man().set_current_time(
n);
1036 sched.tod_man().set_current_time(
n, sched.area_index);
1041 return sched.area_index < 0;
1045 if(sched.area_index >= 0)
return utils::nullopt;
1046 return sched.tod_man().get_max_liminal_bonus();
1050 if(sched.area_index >= 0) {
1051 throw luaL_error(L,
"liminal_bonus can only be set on the global schedule");
1054 sched.tod_man().set_max_liminal_bonus(*value);
1056 sched.tod_man().reset_max_liminal_bonus();
1061 return sched.area_index >= 0;
1065 if(sched.area_index < 0)
return utils::nullopt;
1066 return sched.tod_man().get_area_id(sched.area_index);
1070 if(sched.area_index < 0) {
1071 throw luaL_error(L,
"can't set id of global schedule");
1073 sched.tod_man().set_area_id(sched.area_index, value);
1077 return sched.area_index >= 0;
1081 if(sched.area_index < 0)
return utils::nullopt;
1082 return sched.tod_man().get_area_by_index(sched.area_index);
1086 if(sched.area_index < 0) {
1087 throw luaL_error(L,
"can't set hexes of global schedule");
1089 sched.tod_man().replace_area_locations(sched.area_index, value);
1100 char const *m = luaL_checkstring(L, 2);
1106 lua_pushstring(L,
info.id().c_str());
1107 lua_setfield(L, -2,
"id");
1109 lua_setfield(L, -2,
"name");
1111 lua_setfield(L, -2,
"editor_name");
1113 lua_setfield(L, -2,
"description");
1115 lua_setfield(L, -2,
"icon");
1117 lua_setfield(L, -2,
"editor_image");
1118 lua_pushinteger(L,
info.light_bonus(0));
1119 lua_setfield(L, -2,
"light");
1120 lua_pushboolean(L,
info.is_village());
1121 lua_setfield(L, -2,
"village");
1122 lua_pushboolean(L,
info.is_castle());
1123 lua_setfield(L, -2,
"castle");
1124 lua_pushboolean(L,
info.is_keep());
1125 lua_setfield(L, -2,
"keep");
1126 lua_pushinteger(L,
info.gives_healing());
1127 lua_setfield(L, -2,
"healing");
1139 std::vector<std::string> terrains;
1140 terrains.reserve(codes.size());
1141 for(
auto code : codes) {
1154 template<
bool cons
ider_illuminates>
1162 return luaL_argerror(L, 1,
"coordinates are not on board");
1164 }
else if(lua_isstring(L, 1)) {
1167 return luaL_error(L,
"invalid or empty time_area ID");
1170 loc = *area.begin();
1171 }
else if(!lua_isnil(L, 1)) {
1174 return luaL_error(L,
"empty time_area");
1177 loc = *area.begin();
1180 if(lua_isnumber(L, 2)) {
1181 for_turn = luaL_checkinteger(L, 2);
1183 if(for_turn < 1 || (number_of_turns != -1 && for_turn > number_of_turns)) {
1184 return luaL_argerror(L, 2,
"turn number out of range");
1209 if (!side)
return 0;
1210 lua_pushinteger(L, side);
1227 const int new_side_num = lua_isnoneornil(L, 2) ? 0 : luaL_checkinteger(L, 2);
1229 team* old_side =
nullptr;
1230 team* new_side =
nullptr;
1232 if(old_side_num == new_side_num) {
1238 }
catch(
const std::out_of_range&) {
1245 }
catch(
const std::out_of_range&) {
1251 if(new_side &&
board().team_is_defeated(*new_side)) {
1317 std::string m = luaL_checkstring(L, 1);
1323 return luaL_argerror(L, 1, (
"Cannot find resource with id '" + m +
"'").c_str());
1335 std::string m = luaL_checkstring(L, 1);
1341 return luaL_argerror(L, 1, (
"Cannot find era with id '" + m +
"'").c_str());
1356 #define GAME_CONFIG_SIMPLE_SETTER(name) \
1357 GAME_CONFIG_SETTER(#name, decltype(game_config::name), game_lua_kernel) { \
1359 game_config::name = value; \
1365 return k2.pc().gamestate().do_healing_;
1370 k2.pc().gamestate().do_healing_ = value;}
1380 k2.disp()->set_theme(value);
1383 using traits_map = std::map<std::string, config>;
1386 std::map<std::string, config> result;
1391 result.emplace(trait[
"id"], trait);
1407 static config find_addon(
const std::string&
type,
const std::string&
id)
1416 const char* m = luaL_checkstring(L, 2);
1434 struct end_level_committer {
1436 ~end_level_committer() {
1437 pc_.set_end_level_data(data_);
1448 const char* m = luaL_checkstring(L, 2);
1464 data->~end_level_data();
1470 void*
p = lua_touserdata(L, lua_upvalueindex(1));
1472 if(lua_type(L, 2) == LUA_TNUMBER) {
1475 size_t i = luaL_checkinteger(L, 2);
1477 lua_createtable(L, 2, 0);
1478 lua_pushstring(L,
"options");
1486 auto iter =
settings.addons.begin();
1487 std::advance(iter,
i);
1489 iter->second.write(cfg);
1490 cfg[
"id"] = iter->first;
1492 lua_createtable(L, 2, 0);
1493 lua_pushstring(L,
"addon");
1501 char const *m = luaL_checkstring(L, 2);
1529 if(strcmp(m,
"savegame") == 0) {
1531 if(
savegame == saved_game_mode::type::no) {
1532 lua_pushboolean(L,
false);
1538 if(strcmp(m,
"side_players") == 0) {
1542 if(strcmp(m,
"addons") == 0) {
1543 for(
const auto& [
id, addon] :
settings.addons) {
1544 lua_createtable(L, 0, 4);
1546 lua_setfield(L, -2,
"id");
1548 lua_setfield(L, -2,
"name");
1549 lua_pushboolean(L, addon.required);
1550 lua_setfield(L, -2,
"required");
1551 if(addon.min_version) {
1553 lua_push(L, addon.min_version->str());
1555 lua_setfield(L, -2,
"min_version");
1561 lua_setfield(L, -2,
"version");
1563 lua_createtable(L, addon.content.size(), 0);
1564 for(
const auto& content : addon.content) {
1565 lua_createtable(L, 0, 3);
1567 lua_setfield(L, -2,
"id");
1569 lua_setfield(L, -2,
"name");
1571 lua_setfield(L, -2,
"type");
1572 lua_seti(L, -2, lua_rawlen(L, -2) + 1);
1574 lua_setfield(L, -2,
"content");
1590 void*
p = lua_touserdata(L, lua_upvalueindex(1));
1592 lua_pushinteger(L,
settings.addons.size() + 1);
1603 auto end_level_set()
const {
return &dispatch<&game_lua_kernel::impl_end_level_data_set>; }
1605 #define SCENARIO_GETTER(name, type) LATTR_GETTER(name, type, scenario_tag, k)
1606 #define SCENARIO_SETTER(name, type) LATTR_SETTER(name, type, scenario_tag, k)
1607 #define SCENARIO_VALID(name) LATTR_VALID(name, scenario_tag, k)
1611 inline static auto metatable =
"scenario";
1613 return lua_kernel_base::get_lua_kernel<game_lua_kernel>(L);
1619 return k.tod_man().number_of_turns();
1623 k.tod_man().set_number_of_turns_by_wml(value);
1627 return k.gamedata().next_scenario();
1631 k.gamedata().set_next_scenario(value);
1635 return k.gamedata().get_id();
1639 return k.pc().get_scenario_name();
1643 return k.gamedata().get_defeat_music();
1647 k.gamedata().set_defeat_music(value);
1651 return k.gamedata().get_victory_music();
1655 k.gamedata().set_victory_music(value);
1660 for(
const std::string& rsrc :
utils::split(k.pc().get_loaded_resources())) {
1661 resources.push_back(find_addon(
"resource", rsrc));
1671 return k.cls().difficulty;
1675 return k.cls().end_credits;
1679 k.cls().end_credits = value;
1683 return k.cls().end_text;
1687 k.cls().end_text = value;
1691 return k.cls().end_text_duration.count();
1695 k.cls().end_text_duration = std::chrono::milliseconds{value};
1699 return !k.cls().campaign.empty();
1703 if(k.cls().campaign.empty())
return utils::nullopt;
1704 return find_addon(
"campaign", k.cls().campaign);
1708 std::vector<config> mods;
1709 for(
const std::string& mod : k.cls().active_mods) {
1710 mods.push_back(find_addon(
"modification", mod));
1716 if (!k.pc().is_regular_game_end()) {
1720 auto data = k.pc().get_end_level_data();
1722 if(luaL_newmetatable(L,
"end level data")) {
1723 static luaL_Reg
const callbacks[] {
1725 {
"__newindex", k.end_level_set()},
1727 {
nullptr,
nullptr }
1729 luaL_setfuncs(L, callbacks, 0);
1731 lua_setmetatable(L, -2);
1738 data.proceed_to_next_level = value[
"proceed_to_next_level"].to_bool(
true);
1739 data.transient.carryover_report = value[
"carryover_report"].to_bool(
true);
1740 data.prescenario_save = value[
"save"].to_bool(
true);
1741 data.replay_save = value[
"replay_save"].to_bool(
true);
1742 data.transient.linger_mode = value[
"linger_mode"].to_bool(
true) && !k.ref.teams().empty();
1743 data.transient.reveal_map = value[
"reveal_map"].to_bool(k.pc().reveal_map_default());
1744 data.is_victory = value[
"result"] == level_result::victory;
1745 data.test_result = value[
"test_result"].str();
1746 k.pc().set_end_level_data(
data);
1750 return k.cls().is_multiplayer();
1754 if(!k.cls().is_multiplayer()) {
1758 lua_newuserdatauv(L, 0, 0);
1759 if(luaL_newmetatable(L,
"mp settings")) {
1760 lua_pushlightuserdata(L, &k.pc());
1762 lua_setfield(L, -2,
"__index");
1763 lua_pushlightuserdata(L, &k.pc());
1765 lua_setfield(L, -2,
"__len");
1766 lua_pushstring(L,
"mp settings");
1767 lua_setfield(L, -2,
"__metatable");
1769 lua_setmetatable(L, -2);
1774 return k.cls().is_multiplayer();
1778 if(!k.cls().is_multiplayer())
return utils::nullopt;
1779 return find_addon(
"era", k.cls().era_id);
1791 DBG_LUA <<
"impl_scenario_get";
1803 DBG_LUA <<
"impl_scenario_set";
1812 DBG_LUA <<
"impl_scenario_dir";
1829 return "local_choice";
1848 #define CURRENT_GETTER(name, type) LATTR_GETTER(name, type, current_tag, k)
1852 inline static auto metatable =
"current";
1854 return lua_kernel_base::get_lua_kernel<game_lua_kernel>(L);
1860 return k.pc().current_side();
1864 return k.pc().turn();
1887 return k.pc().is_replay();
1893 cfg[
"name"] = ev.
name;
1900 cfg.
add_child(
"second_weapon", *weapon);
1905 cfg[
"damage_inflicted"] = di;
1951 if (lua_isnone(L, 2)) {
1957 LOG_LUA <<
"Script says: \"" << m <<
"\"";
1966 double factor = luaL_checknumber(L, 1);
1993 if (!lua_isnoneornil(L, 1)) {
1994 int max = 2 *
teams().size();
1995 int npn = luaL_checkinteger(L, 1);
1996 if (npn <= 0 || npn > max) {
1997 return luaL_argerror(L, 1,
"side number out of range");
2014 lua_pushboolean(L,
b);
2032 const unit* u =
nullptr;
2033 int viewing_side = 0;
2035 if (lua_isuserdata(L, arg))
2039 viewing_side = u->
side();
2048 viewing_side = u->
side();
2056 return luaL_argerror(L, 1,
"invalid location");
2058 return luaL_argerror(L, arg,
"invalid location");
2062 bool ignore_units =
false, see_all =
false, ignore_teleport =
false;
2063 double stop_at = 10000;
2064 std::unique_ptr<pathfind::cost_calculator> calc;
2066 if (lua_istable(L, arg))
2068 ignore_units = luaW_table_get_def<bool>(L, arg,
"ignore_units",
false);
2069 see_all = luaW_table_get_def<bool>(L, arg,
"ignore_visibility",
false);
2070 ignore_teleport = luaW_table_get_def<bool>(L, arg,
"ignore_teleport",
false);
2072 stop_at = luaW_table_get_def<double>(L, arg,
"max_cost", stop_at);
2075 lua_pushstring(L,
"viewing_side");
2077 if (!lua_isnil(L, -1)) {
2078 int i = luaL_checkinteger(L, -1);
2079 if (
i >= 1 &&
i <=
static_cast<int>(
teams().
size())) viewing_side =
i;
2083 if(u) see_all =
true;
2084 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.");
2089 lua_pushstring(L,
"calculate");
2091 if(lua_isfunction(L, -1)) {
2096 else if (lua_isfunction(L, arg))
2104 if(!ignore_teleport) {
2105 if(viewing_side == 0) {
2106 lua_warning(L,
"wesnoth.paths.find_path: ignore_teleport=false requires a valid viewing_side; continuing with ignore_teleport=true",
false);
2107 ignore_teleport =
true;
2115 return luaL_argerror(L, 1,
"unit not found OR custom cost function not provided");
2119 teams(),
map, ignore_units,
false, see_all));
2123 &teleport_locations);
2125 int nb = res.
steps.size();
2126 lua_createtable(L, nb, 0);
2127 for (
int i = 0;
i < nb; ++
i)
2130 lua_rawseti(L, -2,
i + 1);
2146 const unit* u =
nullptr;
2148 if (lua_isuserdata(L, arg))
2158 return luaL_argerror(L, 1,
"unit not found");
2163 int viewing_side = u->
side();
2164 bool ignore_units =
false, see_all =
false, ignore_teleport =
false;
2165 int additional_turns = 0;
2167 if (lua_istable(L, arg))
2169 ignore_units = luaW_table_get_def<bool>(L, arg,
"ignore_units",
false);
2170 see_all = luaW_table_get_def<bool>(L, arg,
"ignore_visibility",
false);
2171 ignore_teleport = luaW_table_get_def<bool>(L, arg,
"ignore_teleport",
false);
2172 additional_turns = luaW_table_get_def<int>(L, arg,
"max_cost", additional_turns);
2174 lua_pushstring(L,
"viewing_side");
2176 if (!lua_isnil(L, -1)) {
2177 int i = luaL_checkinteger(L, -1);
2178 if (
i >= 1 &&
i <=
static_cast<int>(
teams().
size())) viewing_side =
i;
2182 if(u) see_all =
true;
2192 viewing_team, additional_turns, see_all, ignore_units);
2195 lua_createtable(L, nb, 0);
2196 for (
int i = 0;
i < nb; ++
i)
2200 lua_pushinteger(L,
s.curr.wml_x());
2201 lua_rawseti(L, -2, 1);
2202 lua_pushinteger(L,
s.curr.wml_y());
2203 lua_rawseti(L, -2, 2);
2204 lua_pushinteger(L,
s.move_left);
2205 lua_rawseti(L, -2, 3);
2206 lua_rawseti(L, -2,
i + 1);
2221 const unit* u =
nullptr;
2223 if (lua_isuserdata(L, arg))
2233 return luaL_argerror(L, 1,
"unit not found");
2240 return luaL_error(L,
"wesnoth.find_vision_range: requires a valid unit");
2243 std::map<map_location, int> jamming_map;
2250 lua_pushinteger(L,
d.curr.wml_x());
2251 lua_rawseti(L, -2, 1);
2252 lua_pushinteger(L,
d.curr.wml_y());
2253 lua_rawseti(L, -2, 2);
2254 lua_pushinteger(L,
d.move_left);
2255 lua_rawseti(L, -2, 3);
2256 lua_rawseti(L, -2, lua_rawlen(L, -2) + 1);
2258 for(
const auto&
e : res.
edges) {
2260 lua_pushinteger(L,
e.wml_x());
2261 lua_rawseti(L, -2, 1);
2262 lua_pushinteger(L,
e.wml_y());
2263 lua_rawseti(L, -2, 2);
2264 lua_pushinteger(L, -1);
2265 lua_rawseti(L, -2, 3);
2266 lua_rawseti(L, -2, lua_rawlen(L, -2) + 1);
2271 template<
typename T>
2274 for (
int i = 1, i_end = lua_rawlen(L, arg);
i <= i_end; ++
i)
2277 lua_rawgeti(L, arg,
i);
2278 int entry = lua_gettop(L);
2279 if (!lua_istable(L, entry)) {
2287 lua_rawgeti(L, entry, 3);
2288 if (!lua_isnumber(L, -1)) {
2289 lua_getfield(L, entry,
"side");
2290 if (!lua_isnumber(L, -1)) {
2294 int side = lua_tointeger(L, -1);
2296 lua_rawgeti(L, entry, 4);
2297 if (!lua_isstring(L, -1)) {
2298 lua_getfield(L, entry,
"type");
2299 if (!lua_isstring(L, -1)) {
2303 std::string
unit_type = lua_tostring(L, -1);
2307 lua_settop(L, entry - 1);
2311 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");
2329 std::vector<const ::unit*> real_units;
2330 typedef std::vector<std::tuple<map_location, int, std::string>> unit_type_vector;
2336 real_units.push_back(
unit);
2341 if(match->get_location().valid()) {
2342 real_units.push_back(match);
2352 real_units.push_back(&(*ui));
2357 if (lua_istable(L, arg))
2365 return luaL_argerror(L, 1,
"unit(s) not found");
2368 int viewing_side = 0;
2369 bool ignore_units =
true, see_all =
true, ignore_teleport =
false,
debug =
false, use_max_moves =
false;
2371 if (lua_istable(L, arg))
2373 lua_pushstring(L,
"ignore_units");
2375 if (!lua_isnil(L, -1))
2381 lua_pushstring(L,
"ignore_teleport");
2383 if (!lua_isnil(L, -1))
2389 lua_pushstring(L,
"viewing_side");
2391 if (!lua_isnil(L, -1))
2393 int i = luaL_checkinteger(L, -1);
2394 if (
i >= 1 &&
i <=
static_cast<int>(
teams().
size()))
2401 lua_pushstring(L,
"debug");
2403 if (!lua_isnil(L, -1))
2409 lua_pushstring(L,
"use_max_moves");
2411 if (!lua_isnil(L, -1))
2433 const team& viewing_team = viewing_side
2438 ignore_units, !ignore_teleport, viewing_team, see_all, ignore_units);
2440 for (const ::unit*
const u : real_units)
2442 cost_map.
add_unit(*u, use_max_moves);
2444 for (
const unit_type_vector::value_type& fu :
fake_units)
2447 cost_map.
add_unit(std::get<0>(fu), ut, std::get<1>(fu));
2456 std::stringstream
s;
2473 lua_rawseti(L, -2, 1);
2476 lua_rawseti(L, -2, 2);
2479 lua_rawseti(L, -2, 3);
2482 lua_rawseti(L, -2, 4);
2484 lua_rawseti(L, -2, counter);
2494 return reinterpret_cast<int*
>(luaL_checkudata(L, idx,
labelKey));
2499 const char* m = luaL_checkstring(L, 2);
2507 std::chrono::milliseconds fade{luaL_optinteger(L, 2, -1)};
2545 double width_ratio = 0;
2547 int lifetime = 2'000, fadeout = 100;
2553 if(lua_istable(L, 2)) {
2555 size = luaL_checkinteger(L, -1);
2559 width = lua_tointegerx(L, -1, &found_number);
2563 if(!value.empty() && value.back() ==
'%') {
2564 value.remove_suffix(1);
2565 width_ratio =
std::stoi(std::string(value)) / 100.0;
2566 }
else throw std::invalid_argument(value.data());
2567 }
catch(std::invalid_argument&) {
2568 return luaL_argerror(L, -1,
"max_width should be integer or percentage");
2574 if(lua_isstring(L, -1)) {
2577 auto vec = lua_check<std::vector<int>>(L, -1);
2578 if(vec.size() != 3) {
2579 int idx = lua_absindex(L, -1);
2581 color.r = luaL_checkinteger(L, -3);
2582 color.g = luaL_checkinteger(L, -2);
2583 color.b = luaL_checkinteger(L, -1);
2585 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");
2595 if(lua_isstring(L, -1)) {
2598 auto vec = lua_check<std::vector<int>>(L, -1);
2599 if(vec.size() != 3) {
2600 int idx = lua_absindex(L, -1);
2602 bgcolor.r = luaL_checkinteger(L, -3);
2603 bgcolor.g = luaL_checkinteger(L, -2);
2604 bgcolor.b = luaL_checkinteger(L, -1);
2606 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");
2616 bgcolor.a = luaL_checkinteger(L, -1);
2621 lifetime = lua_tointegerx(L, -1, &found_number);
2624 if(value ==
"unlimited") {
2627 return luaL_argerror(L, -1,
"duration should be integer or 'unlimited'");
2632 fadeout = lua_tointeger(L, -1);
2638 static const char* options[] = {
"left",
"center",
"right"};
2639 alignment =
font::ALIGN(luaL_checkoption(L, -1,
nullptr, options));
2642 static const char* options[] = {
"top",
"center",
"bottom"};
2643 vertical_alignment =
font::ALIGN(luaL_checkoption(L, -1,
nullptr, options));
2655 int handle_idx = lua_gettop(L);
2662 if(width_ratio > 0) {
2663 width =
static_cast<int>(std::round(
rect.w * width_ratio));
2677 switch(vertical_alignment) {
2693 using std::chrono::milliseconds;
2700 flabel.
set_lifetime(milliseconds{lifetime}, milliseconds{fadeout});
2711 lua_settop(L, handle_idx);
2712 if(luaL_newmetatable(L,
labelKey)) {
2714 static const luaL_Reg methods[] = {
2715 {
"remove", &dispatch<&game_lua_kernel::intf_remove_floating_label>},
2716 {
"move", &dispatch<&game_lua_kernel::intf_move_floating_label>},
2717 {
"replace", &dispatch2<&game_lua_kernel::intf_set_floating_label, false>},
2719 {
nullptr,
nullptr }
2721 luaL_setfuncs(L, methods, 0);
2724 lua_setmetatable(L, handle_idx);
2725 lua_settop(L, handle_idx);
2750 return luaL_error(L,
"Attempted to move a unit while the map is locked");
2755 if (!
map().on_board(
loc)) {
2756 return luaL_argerror(L, 2,
"invalid location");
2767 if (!
map().on_board(
loc))
2768 return luaL_argerror(L, 1,
"invalid location");
2774 }
else if(!lua_isnoneornil(L, 1)) {
2775 const vconfig* vcfg =
nullptr;
2777 if (!
map().on_board(
loc)) {
2780 if (!
map().on_board(
loc))
2781 return luaL_argerror(L, 2,
"invalid location");
2787 u->set_location(
loc);
2806 return luaL_error(L,
"Attempted to remove a unit while the map is locked");
2814 if (!
map().on_board(
loc)) {
2815 return luaL_argerror(L, 1,
"invalid location");
2820 t.recall_list().erase_if_matches_id(u->
id());
2822 return luaL_argerror(L, 1,
"can't erase private units");
2825 if (!
map().on_board(
loc)) {
2826 return luaL_argerror(L, 1,
"invalid location");
2829 return luaL_argerror(L, 1,
"expected unit or location");
2845 return luaL_error(L,
"Attempted to move a unit while the map is locked");
2849 int side = lua_tointeger(L, 2);
2850 if (
static_cast<unsigned>(side) >
teams().
size()) side = 0;
2854 u = lu->get_shared();
2855 if(lu->on_recall_list() && lu->on_recall_list() == side) {
2856 return luaL_argerror(L, 1,
"unit already on recall list");
2859 const vconfig* vcfg =
nullptr;
2871 std::size_t uid = u->underlying_id();
2872 t.recall_list().erase_by_underlying_id(uid);
2873 t.recall_list().add(u);
2878 u->anim_comp().clear_haloes();
2880 lu->lua_unit::~lua_unit();
2894 return luaL_error(L,
"Attempted to remove a unit while the map is locked");
2902 u->anim_comp().clear_haloes();
2903 }
else if (
int side = lu->on_recall_list()) {
2906 t.recall_list().erase_if_matches_id(u->id());
2912 lu->lua_unit::~lua_unit();
2928 if (!lua_isnoneornil(L, 2)) {
2932 const vconfig* vcfg =
nullptr;
2940 if (!res.
valid())
return 0;
2941 lua_pushinteger(L, res.
wml_x());
2942 lua_pushinteger(L, res.
wml_y());
2958 if (!lua_isnoneornil(L, 3)) {
2975 const vconfig* vcfg =
nullptr;
3005 char const *m = luaL_checkstring(L, 2);
3009 if(lua_isboolean(L, 3)) {
3011 if(!lua_isnoneornil(L, 4)) {
3014 }
else if(!lua_isnoneornil(L, 3)) {
3035 }
else if(lua_isstring(L, 2)) {
3036 char const *m = luaL_checkstring(L, 2);
3056 }
else if(lua_isstring(L, 2)) {
3057 char const *m = luaL_checkstring(L, 2);
3077 }
else if(lua_isstring(L, 2)) {
3078 char const *m = luaL_checkstring(L, 2);
3098 }
else if(lua_isstring(L, 2)) {
3099 char const *m = luaL_checkstring(L, 2);
3115 char const *m = luaL_checkstring(L, 2);
3129 char const *m = luaL_checkstring(L, 2);
3131 if (!utp)
return luaL_argerror(L, 2,
"unknown unit type");
3132 if(lua_isstring(L, 3)) {
3133 const std::string& m2 = lua_tostring(L, 3);
3134 if(!utp->
has_variation(m2))
return luaL_argerror(L, 2,
"unknown unit variation");
3148 lua_createtable(L, 0, 4);
3150 lua_setfield(L, -2,
"poisoned");
3151 lua_pushnumber(L, cmb.
slowed);
3152 lua_setfield(L, -2,
"slowed");
3154 lua_setfield(L, -2,
"untouched");
3156 lua_setfield(L, -2,
"average_hp");
3157 lua_createtable(L,
n, 0);
3158 for (
int i = 0;
i <
n; ++
i) {
3160 lua_rawseti(L, -2,
i);
3162 lua_setfield(L, -2,
"hp_chance");
3171 lua_createtable(L, 0, 16);
3174 lua_setfield(L, -2,
"num_blows");
3175 lua_pushnumber(L, bcustats.
damage);
3176 lua_setfield(L, -2,
"damage");
3178 lua_setfield(L, -2,
"chance_to_hit");
3179 lua_pushboolean(L, bcustats.
poisons);
3180 lua_setfield(L, -2,
"poisons");
3181 lua_pushboolean(L, bcustats.
slows);
3182 lua_setfield(L, -2,
"slows");
3184 lua_setfield(L, -2,
"petrifies");
3185 lua_pushboolean(L, bcustats.
plagues);
3186 lua_setfield(L, -2,
"plagues");
3188 lua_setfield(L, -2,
"plague_type");
3189 lua_pushnumber(L, bcustats.
rounds);
3190 lua_setfield(L, -2,
"rounds");
3192 lua_setfield(L, -2,
"firststrike");
3193 lua_pushboolean(L, bcustats.
drains);
3194 lua_setfield(L, -2,
"drains");
3196 lua_setfield(L, -2,
"drain_constant");
3198 lua_setfield(L, -2,
"drain_percent");
3203 lua_setfield(L, -2,
"attack_num");
3205 lua_setfield(L, -2,
"number");
3207 if(bcustats.
weapon !=
nullptr)
3209 lua_pushstring(L, bcustats.
weapon->id().c_str());
3210 lua_setfield(L, -2,
"name");
3212 lua_setfield(L, -2,
"weapon");
3231 int arg_num = 1, att_w = -1, def_w = -1;
3235 if (lua_isnumber(L, arg_num)) {
3236 att_w = lua_tointeger(L, arg_num) - 1;
3237 if (att_w < 0 || att_w >=
static_cast<int>(att->attacks().size()))
3238 return luaL_argerror(L, arg_num,
"weapon index out of bounds");
3244 if (lua_isnumber(L, arg_num)) {
3245 def_w = lua_tointeger(L, arg_num) - 1;
3246 if (def_w < 0 || def_w >=
static_cast<int>(def->attacks().size()))
3247 return luaL_argerror(L, arg_num,
"weapon index out of bounds");
3252 def->get_location(), att_w, def_w, 0.0,
nullptr, att, def);
3269 char const *m = luaL_checkstring(L, 1);
3270 int repeats = luaL_optinteger(L, 2, 0);
3282 const char* content_for = luaL_checkstring(L, 1);
3283 const char*
id = luaL_checkstring(L, 2);
3286 if(group.content_for_ == content_for) {
3288 if(achieve.
id_ ==
id) {
3315 ERR_LUA <<
"Achievement " <<
id <<
" not found for achievement group " << content_for;
3321 ERR_LUA <<
"Achievement group " << content_for <<
" not found";
3333 const char* content_for = luaL_checkstring(L, 1);
3334 const char*
id = luaL_checkstring(L, 2);
3337 ERR_LUA <<
"Returning false for whether a player has completed an achievement due to being networked multiplayer.";
3338 lua_pushboolean(L,
false);
3354 const char* content_for = luaL_checkstring(L, 1);
3355 const char*
id = luaL_checkstring(L, 2);
3359 if(group.content_for_ == content_for) {
3360 for(
const auto& achieve : group.achievements_) {
3361 if(achieve.id_ ==
id) {
3363 cfg[
"id"] = achieve.id_;
3364 cfg[
"name"] = achieve.name_;
3365 cfg[
"name_completed"] = achieve.name_completed_;
3366 cfg[
"description"] = achieve.description_;
3367 cfg[
"description_completed"] = achieve.description_completed_;
3368 cfg[
"icon"] = achieve.icon_;
3369 cfg[
"icon_completed"] = achieve.icon_completed_;
3370 cfg[
"hidden"] = achieve.hidden_;
3371 cfg[
"achieved"] = achieve.achieved_;
3372 cfg[
"max_progress"] = achieve.max_progress_;
3373 cfg[
"current_progress"] = achieve.current_progress_;
3375 for(
const auto& sub_ach : achieve.sub_achievements_) {
3377 sub[
"id"] = sub_ach.id_;
3378 sub[
"description"] = sub_ach.description_;
3379 sub[
"icon"] = sub_ach.icon_;
3380 sub[
"achieved"] = sub_ach.achieved_;
3388 ERR_LUA <<
"Achievement " <<
id <<
" not found for achievement group " << content_for;
3394 ERR_LUA <<
"Achievement group " << content_for <<
" not found";
3410 const char* content_for = luaL_checkstring(L, 1);
3411 const char*
id = luaL_checkstring(L, 2);
3412 int amount = luaL_checkinteger(L, 3);
3413 int limit = luaL_optinteger(L, 4, 999999999);
3416 if(group.content_for_ == content_for) {
3418 if(achieve.
id_ ==
id) {
3421 ERR_LUA <<
"Attempted to progress achievement " <<
id <<
" for achievement group " << content_for <<
", is not a progressible achievement.";
3422 lua_pushinteger(L, -1);
3423 lua_pushinteger(L, -1);
3438 lua_pushinteger(L, progress);
3440 lua_pushinteger(L, -1);
3448 lua_push(L,
"Achievement " + std::string(
id) +
" not found for achievement group " + content_for);
3449 return lua_error(L);
3454 lua_push(L,
"Achievement group " + std::string(content_for) +
" not found");
3455 return lua_error(L);
3467 const char* content_for = luaL_checkstring(L, 1);
3468 const char*
id = luaL_checkstring(L, 2);
3469 const char* sub_id = luaL_checkstring(L, 3);
3472 ERR_LUA <<
"Returning false for whether a player has completed an achievement due to being networked multiplayer.";
3473 lua_pushboolean(L,
false);
3489 const char* content_for = luaL_checkstring(L, 1);
3490 const char*
id = luaL_checkstring(L, 2);
3491 const char* sub_id = luaL_checkstring(L, 3);
3494 if(group.content_for_ == content_for) {
3496 if(achieve.
id_ ==
id) {
3503 if(sub_ach.
id_ == sub_id) {
3521 lua_push(L,
"Sub-achievement " + std::string(
id) +
" not found for achievement" +
id +
" in achievement group " + content_for);
3522 return lua_error(L);
3526 lua_push(L,
"Achievement " + std::string(
id) +
" not found for achievement group " + content_for);
3527 return lua_error(L);
3532 lua_push(L,
"Achievement group " + std::string(content_for) +
" not found");
3533 return lua_error(L);
3569 if(lua_isnoneornil(L, 1)) {
3574 if(!
map().on_board(
loc))
return luaL_argerror(L, 1,
"not on board");
3575 bool highlight =
true;
3576 if(!lua_isnoneornil(L, 2))
3606 lua_pushboolean(L, skipping);
3617 if (!lua_isnone(L, 1)) {
3629 int user_choice_index;
3630 int random_choice_index;
3631 int ai_choice_index;
3633 lua_synchronize(lua_State *l,
const std::string& descr,
int user_index,
int random_index = 0,
int ai_index = 0)
3635 , user_choice_index(user_index)
3636 , random_choice_index(random_index)
3637 , ai_choice_index(ai_index != 0 ? ai_index : user_index)
3643 bool is_local_ai = lua_kernel_base::get_lua_kernel<game_lua_kernel>(L).board().get_team(side).is_local_ai();
3645 query_lua(side, is_local_ai ? ai_choice_index : user_choice_index, cfg);
3652 if(random_choice_index != 0 && lua_isfunction(L, random_choice_index)) {
3653 query_lua(side, random_choice_index, cfg);
3663 void query_lua(
int side,
int function_index,
config& cfg)
const
3665 lua_pushvalue(L, function_index);
3666 lua_pushnumber(L, side);
3669 static const char*
msg =
"function returned to wesnoth.sync.[multi_]evaluate a table which was partially invalid";
3670 lua_kernel_base::get_lua_kernel<game_lua_kernel>(L).log_error(
msg);
3671 lua_warning(L,
msg,
false);
3678 virtual bool is_visible()
const override {
return false; }
3692 std::string tagname =
"input";
3699 if(!lua_isfunction(L, nextarg) &&
luaW_totstring(L, nextarg, desc) ) {
3702 if(lua_isfunction(L, nextarg)) {
3703 human_func = nextarg++;
3706 return luaL_argerror(L, nextarg,
"expected a function");
3708 if(lua_isfunction(L, nextarg)) {
3709 ai_func = nextarg++;
3711 side_for = lua_tointeger(L, nextarg);
3727 std::string tagname =
"input";
3731 std::vector<int> sides_for;
3734 if(!lua_isfunction(L, nextarg) &&
luaW_totstring(L, nextarg, desc) ) {
3737 if(lua_isfunction(L, nextarg)) {
3738 human_func = nextarg++;
3741 return luaL_argerror(L, nextarg,
"expected a function");
3743 if(lua_isfunction(L, nextarg)) {
3744 null_func = nextarg++;
3746 sides_for = lua_check<std::vector<int>>(L, nextarg++);
3761 lua_pushvalue(L, 1);
3776 std::set<map_location> res;
3802 lua_pushboolean(L,
true);
3811 lua_pushboolean(L, t_filter.
match(
loc));
3829 lua_pushboolean(L,
true);
3837 lua_pushboolean(L, s_filter.
match(*
t));
3839 unsigned side = luaL_checkinteger(L, 1) - 1;
3841 lua_pushboolean(L, s_filter.
match(side + 1));
3852 team_i = luaL_checkinteger(L, 1);
3854 std::string
flag = luaL_optlstring(L, 2,
"",
nullptr);
3855 std::string color = luaL_optlstring(L, 3,
"",
nullptr);
3857 if(
flag.empty() && color.empty()) {
3860 if(team_i < 1 ||
static_cast<std::size_t
>(team_i) >
teams().
size()) {
3861 return luaL_error(L,
"set_side_id: side number %d out of range", team_i);
3865 if(!color.empty()) {
3880 side_num =
t->side();
3882 side_num = luaL_checkinteger(L, 1);
3884 std::string
path = luaL_checkstring(L, 2);
3889 if(strcmp(action,
"delete") == 0) {
3894 std::size_t len = std::string::npos, open_brak =
path.find_last_of(
'[');
3895 std::size_t dot =
path.find_last_of(
'.');
3896 if(open_brak != len) {
3897 len = open_brak - dot - 1;
3908 side_num =
t->side();
3910 side_num = luaL_checkinteger(L, 1);
3912 if(lua_isstring(L, 2)) {
3913 std::string file = luaL_checkstring(L, 2);
3915 std::string
err =
formatter() <<
"Could not load AI for side " << side_num <<
" from file " << file;
3916 lua_pushlstring(L,
err.c_str(),
err.length());
3917 return lua_error(L);
3929 side_num =
t->side();
3931 side_num = luaL_checkinteger(L, 1);
3935 cfg =
config {
"ai", cfg};
3937 bool added_dummy_stage =
false;
3939 added_dummy_stage =
true;
3943 if(added_dummy_stage) {
3952 unsigned i = luaL_checkinteger(L, 1);
3953 if(i < 1 || i >
teams().
size())
return 0;
3965 LOG_LUA <<
"intf_get_sides called: this = " << std::hex <<
this << std::dec <<
" myname = " <<
my_name();
3966 std::vector<int> sides;
3970 sides.push_back(
t.side());
3976 sides =
filter.get_teams();
3980 lua_createtable(L, sides.size(), 0);
3982 for(
int side : sides) {
3984 lua_rawseti(L, -2,
index);
4001 char const *m = luaL_checkstring(L, 2);
4003 if (sm ==
"advance") {
4007 if (sm !=
"advancement" && sm !=
"object" && sm !=
"trait") {
4008 return luaL_argerror(L, 2,
"unknown modification type");
4010 bool write_to_mods =
true;
4011 if (!lua_isnone(L, 4)) {
4015 write_to_mods =
false;
4033 std::vector<std::string> tags;
4034 if(lua_isstring(L, 3)) {
4035 tags.push_back(lua_check<std::string>(L, 3));
4036 }
else if (lua_istable(L, 3)){
4037 tags = lua_check<std::vector<std::string>>(L, 3);
4039 tags.push_back(
"object");
4042 if(
filter.attribute_count() == 1 &&
filter.all_children_count() == 0 &&
filter.attribute_range().front().first ==
"duration") {
4045 for(
const std::string&
tag : tags) {
4047 if(obj.matches(
filter)) {
4048 obj[
"duration"] =
"now";
4068 if(lua_isboolean(L, 2)) {
4071 if(lua_isboolean(L, 3)) {
4085 char const *ty = luaL_checkstring(L, 1);
4088 std::stringstream ss;
4089 ss <<
"unknown unit type: '" << ty <<
"'";
4090 return luaL_argerror(L, 1, ss.str().c_str());
4107 std::string team_name;
4110 std::vector<std::string> team_names;
4112 [&](
int team) { return game_state_.get_disp_context().get_team(team).team_name(); });
4115 team_name = cfg[
"team_name"].str();
4124 cfg[
"visible_in_fog"].to_bool(
true),
4125 cfg[
"submerge"].to_double(0),
4126 cfg[
"z_order"].to_double(0)
4140 char const *m = lua_tostring(L, 2);
4157 const int nargs = lua_gettop(L);
4158 if(nargs < 2 || nargs > 3) {
4159 return luaL_error(L,
"Wrong number of arguments to ai.log_replay() - should be 2 or 3 arguments.");
4161 const std::string key = nargs == 2 ? luaL_checkstring(L, 1) : luaL_checkstring(L, 2);
4167 }
else if(!lua_isstring(L, 3)) {
4168 return luaL_argerror(L, 3,
"accepts only string or config");
4191 cfg.
add_child(
"filter_lua")[
"code"] =
"<function>";
4201 if(lua_isstring(L, idx)) {
4202 return lua_tostring(L, idx);
4242 using namespace std::literals;
4248 }
else if(is_menu_item) {
4250 return luaL_argerror(L, 1,
"non-empty id is required for a menu item");
4252 name =
"menu item " +
id;
4254 if(
id.empty() && name.empty()) {
4255 return luaL_argerror(L, 1,
"either a name or id is required");
4258 if(new_handler.valid()) {
4259 bool has_lua_filter =
false;
4263 int filterIdx = lua_gettop(L);
4266 if(lua_isfunction(L, filterIdx)) {
4267 int fcnIdx = lua_absindex(L, -1);
4268 new_handler->add_filter(std::make_unique<lua_event_filter>(*
this, fcnIdx,
luaW_table_get_def(L, 1,
"filter_args",
config())));
4269 has_lua_filter =
true;
4271 #define READ_ONE_FILTER(key, tag) \
4273 if(luaW_tableget(L, filterIdx, key)) { \
4274 if(lua_isstring(L, -1)) { \
4275 filters.add_child("insert_tag", config{ \
4277 "variable", luaL_checkstring(L, -1) \
4280 filters.add_child(tag, luaW_checkconfig(L, -1)); \
4290 #undef READ_ONE_FILTER
4292 filters[
"filter_formula"] = luaL_checkstring(L, -1);
4296 new_handler->read_filters(filters);
4302 if(has_lua_filter) {
4307 new_handler->register_wml_event(*
this);
4320 lua_pushvalue(L, lua_upvalueindex(1));
4337 template<
bool is_menu_item>
4343 double priority = luaL_optnumber(L, 3, 0.);
4345 return luaL_argerror(L, 1,
"must not be empty");
4349 name =
"menu item " + name;
4350 }
else if(lua_absindex(L, -1) > 2 && lua_isfunction(L, -1)) {
4353 lua_pushcclosure(L, &dispatch<&game_lua_kernel::cfun_undoable_event>, 2);
4356 if(new_handler.valid()) {
4373 bool delayed_variable_substitution = cfg[
"delayed_variable_substitution"].to_bool(
true);
4374 if(delayed_variable_substitution) {
4401 lua_pushinteger(L, color.r);
4402 lua_pushinteger(L, color.g);
4403 lua_pushinteger(L, color.b);
4412 auto vec = lua_check<std::vector<uint8_t>>(L, 1);
4413 if(vec.size() != 4) {
4416 color_t fade{vec[0], vec[1], vec[2], vec[3]};
4434 using namespace std::chrono_literals;
4435 std::chrono::milliseconds delay{luaL_checkinteger(L, 1)};
4443 const auto end_time = std::chrono::steady_clock::now() + delay;
4446 std::this_thread::sleep_for(10ms);
4447 }
while (std::chrono::steady_clock::now() < end_time);
4471 std::string team_name;
4474 if(lua_gettop(L) == 1 && lua_istable(L, 1)) {
4475 using namespace std::literals;
4478 team_name = luaL_optstring(L, 2,
"");
4492 switch(lua_type(L, 2)) {
4494 case LUA_TNONE:
case LUA_TNIL:
4499 if(
size_t n = luaL_checkinteger(L, 2);
n > 0 &&
n <=
teams().size()) {
4539 for (
const int side :
filter.get_teams()){
4561 int side = cfg[
"side"].to_int();
4571 lua_getfield(L, -1,
"ca_ptr");
4585 lua_getfield(L, -1,
"stg_ptr");
4594 lua_createtable(L, 0, 0);
4596 lua_pushstring(L,
"name");
4597 lua_pushstring(L,
c->get_name().c_str());
4600 lua_pushstring(L,
"engine");
4601 lua_pushstring(L,
c->get_engine().c_str());
4604 lua_pushstring(L,
"id");
4605 lua_pushstring(L,
c->get_id().c_str());
4608 if (ct ==
"candidate_action") {
4609 lua_pushstring(L,
"ca_ptr");
4610 lua_pushlightuserdata(L,
c);
4613 lua_pushstring(L,
"exec");
4618 if (ct ==
"stage") {
4619 lua_pushstring(L,
"stg_ptr");
4620 lua_pushlightuserdata(L,
c);
4623 lua_pushstring(L,
"exec");
4629 std::vector<std::string> c_types =
c->get_children_types();
4631 for (std::vector<std::string>::const_iterator
t = c_types.begin();
t != c_types.end(); ++
t)
4633 std::vector<ai::component*> children =
c->get_children(*
t);
4634 std::string
type = *
t;
4635 if (
type ==
"aspect" ||
type ==
"goal" ||
type ==
"engine")
4640 lua_pushstring(L,
type.c_str());
4641 lua_createtable(L, 0, 0);
4643 for (std::vector<ai::component*>::const_iterator
i = children.begin();
i != children.end(); ++
i)
4645 lua_pushstring(L, (*i)->get_name().c_str());
4676 side = luaL_checkinteger(L, 1);
4683 std::vector<ai::component*> engines =
c->get_children(
"engine");
4685 for (std::vector<ai::component*>::const_iterator
i = engines.begin();
i != engines.end(); ++
i)
4687 if ((*i)->get_name() ==
"lua")
4697 if (lua_engine ==
nullptr)
4710 LOG_LUA <<
"Created new dummy lua-engine for debug_ai().";
4720 lua_pushstring(L,
"components");
4746 if(lua_isboolean(L, 1)) {
4771 std::set<map_location> locs;
4774 if(lua_gettop(L) == 1) {
4777 id = cfg[
"id"].str();
4779 filter.get_locations(locs,
true);
4782 id = luaL_checkstring(L, 1);
4783 if(!lua_isnoneornil(L, 3))
4789 filter.get_locations(locs,
true);
4797 LOG_LUA <<
"Lua inserted time_area '" <<
id <<
"'";
4806 const char *
id = luaL_checkstring(L, 1);
4808 LOG_LUA <<
"Lua removed time_area '" <<
id <<
"'";
4818 if(area_index < 0) {
4825 std::string area_id = luaL_checkstring(L, 1);
4827 if(
auto iter =
std::find(area_ids.begin(), area_ids.end(), area_id); iter == area_ids.end()) {
4841 if(luaL_testudata(L, 1,
"schedule")) {
4851 ERR_LUA <<
"attempted to to replace ToD schedule with empty schedule";
4857 LOG_LUA <<
"replaced ToD schedule";
4866 point scroll_to(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2));
4883 lua_report_generator(lua_State *L,
const std::string &
n)
4892 if (!
luaW_getglobal(L,
"wesnoth",
"interface",
"game_display", name))
4916 char const *m = luaL_checkstring(L, 2);
4918 lua_pushvalue(L, 2);
4919 lua_pushvalue(L, -2);
4930 char const *m = luaL_checkstring(L, 2);
4931 lua_pushvalue(L, 2);
4932 lua_pushvalue(L, 3);
4973 if (
dst == u->get_location() || !
map().on_board(
dst)) {
4977 if (!
map().on_board(vacant_dst)) {
4988 std::vector<map_location> teleport_path;
4989 teleport_path.push_back(src_loc);
4990 teleport_path.push_back(vacant_dst);
4997 u->anim_comp().set_standing();
5004 if (
map().is_village(vacant_dst)) {
5023 const std::string& logger = lua_isstring(L, 2) ? luaL_checkstring(L, 1) :
"";
5024 const std::string&
msg = lua_isstring(L, 2) ? luaL_checkstring(L, 2) : luaL_checkstring(L, 1);
5026 if(logger ==
"wml" || logger ==
"WML") {
5040 lua_pushboolean(L, fog ?
t.fogged(
loc) :
t.shrouded(
loc));
5058 bool affect_normal_fog =
false;
5059 if(lua_isboolean(L, -1)) {
5062 std::set<int> sides;
5064 sides.insert(
t->side());
5065 }
else if(lua_isnumber(L, 1)) {
5066 sides.insert(lua_tointeger(L, 1));
5067 }
else if(lua_istable(L, 1) && lua_istable(L, 2)) {
5068 const auto& v = lua_check<std::vector<int>>(L, 1);
5069 sides.insert(v.begin(), v.end());
5072 sides.insert(
t.side()+1);
5077 for(
const int &side_num : sides) {
5078 if(side_num < 1 ||
static_cast<std::size_t
>(side_num) >
teams().
size()) {
5084 t.remove_fog_override(locs);
5085 if(affect_normal_fog) {
5088 }
else if(!affect_normal_fog) {
5090 t.add_fog_override(locs);
5108 const std::string name = luaL_checkstring(L, 1);
5113 if(!
luaW_getglobal(L,
"wesnoth",
"custom_synced_commands", name)) {
5114 return luaL_argerror(L, 1,
"Unknown synced command");
5117 cmd_tag[
"name"] = name;
5118 if(!lua_isnoneornil(L, 2)) {
5136 #define CALLBACK_GETTER(name, type) LATTR_GETTER(name, lua_index_raw, callbacks_tag, ) { lua_pushcfunction(L, &impl_null_callback<type>); return lua_index_raw(L); }
5139 template<
typename Ret>
5141 if constexpr(std::is_same_v<Ret, void>)
return 0;
5147 inline static auto metatable =
"game_events";
5149 return lua_kernel_base::get_lua_kernel<game_lua_kernel>(L);
5170 template<
typename Ret =
void>
5172 int top = lua_gettop(L);
5176 lua_getfield(L, -1, name.c_str());
5177 lua_pushcfunction(L, &impl_null_callback<Ret>);
5178 if(lua_rawequal(L, -1, -2)) {
5234 cmd_log_ <<
"Registering game-specific wesnoth lib functions...\n";
5237 static luaL_Reg
const callbacks[] {
5242 {
"cancel_action", &dispatch<&game_lua_kernel::intf_cancel_action > },
5243 {
"log_replay", &dispatch<&game_lua_kernel::intf_log_replay > },
5244 {
"log", &dispatch<&game_lua_kernel::intf_log > },
5245 {
"redraw", &dispatch<&game_lua_kernel::intf_redraw > },
5246 {
"simulate_combat", &dispatch<&game_lua_kernel::intf_simulate_combat > },
5247 {
nullptr,
nullptr }
5248 };lua_getglobal(L,
"wesnoth");
5249 if (!lua_istable(L,-1)) {
5252 luaL_setfuncs(L, callbacks, 0);
5254 lua_setglobal(L,
"wesnoth");
5256 lua_getglobal(L,
"gui");
5257 lua_pushcfunction(L, &dispatch<&game_lua_kernel::intf_gamestate_inspector>);
5258 lua_setfield(L, -2,
"show_inspector");
5260 lua_setfield(L, -2,
"show_recruit_dialog");
5262 lua_setfield(L, -2,
"show_recall_dialog");
5268 static luaL_Reg
const test_callbacks[] {
5269 {
"fire_wml_menu_item", &dispatch<&game_lua_kernel::intf_fire_wml_menu_item> },
5270 {
nullptr,
nullptr }
5272 luaL_setfuncs(L, test_callbacks, 0);
5273 lua_setglobal(L,
"unit_test");
5299 cmd_log_ <<
"Adding terrain_types table...\n";
5300 lua_getglobal(L,
"wesnoth");
5301 lua_newuserdatauv(L, 0, 0);
5302 lua_createtable(L, 0, 2);
5303 lua_pushcfunction(L, &dispatch<&game_lua_kernel::impl_get_terrain_info>);
5304 lua_setfield(L, -2,
"__index");
5305 lua_pushcfunction(L, &dispatch<&game_lua_kernel::impl_get_terrain_list>);
5306 lua_setfield(L, -2,
"__dir");
5307 lua_pushstring(L,
"terrain types");
5308 lua_setfield(L, -2,
"__metatable");
5309 lua_setmetatable(L, -2);
5310 lua_setfield(L, -2,
"terrain_types");
5314 cmd_log_ <<
"Adding ai elements table...\n";
5319 cmd_log_ <<
"Adding wesnoth current table...\n";
5321 lua_getglobal(L,
"wesnoth");
5322 lua_newuserdatauv(L, 0, 0);
5323 lua_createtable(L, 0, 2);
5324 lua_pushcfunction(L, &dispatch<&game_lua_kernel::impl_current_get>);
5325 lua_setfield(L, -2,
"__index");
5326 lua_pushcfunction(L, &dispatch<&game_lua_kernel::impl_current_dir>);
5327 lua_setfield(L, -2,
"__dir");
5328 lua_pushboolean(L,
true);
5329 lua_setfield(L, -2,
"__dir_tablelike");
5330 lua_pushstring(L,
"current config");
5331 lua_setfield(L, -2,
"__metatable");
5332 lua_setmetatable(L, -2);
5333 lua_setfield(L, -2,
"current");
5337 lua_getglobal(L,
"wml");
5338 static luaL_Reg
const wml_callbacks[] {
5342 {
"get_variable", &dispatch<&game_lua_kernel::intf_get_variable>},
5343 {
"set_variable", &dispatch<&game_lua_kernel::intf_set_variable>},
5344 {
"get_all_vars", &dispatch<&game_lua_kernel::intf_get_all_vars>},
5345 {
nullptr,
nullptr }
5347 luaL_setfuncs(L, wml_callbacks, 0);
5352 static luaL_Reg
const map_callbacks[] {
5359 {
"get_owner", &dispatch<&game_lua_kernel::intf_get_village_owner>},
5360 {
"set_owner", &dispatch<&game_lua_kernel::intf_set_village_owner>},
5362 {
"add_label", &dispatch<&game_lua_kernel::intf_add_label>},
5363 {
"remove_label", &dispatch<&game_lua_kernel::intf_remove_label>},
5364 {
"get_label", &dispatch<&game_lua_kernel::intf_get_label>},
5366 {
"place_area", &dispatch<&game_lua_kernel::intf_add_time_area>},
5367 {
"remove_area", &dispatch<&game_lua_kernel::intf_remove_time_area>},
5368 {
"get_area", &dispatch<&game_lua_kernel::intf_get_time_area>},
5370 {
"find", &dispatch<&game_lua_kernel::intf_get_locations>},
5371 {
"matches", &dispatch<&game_lua_kernel::intf_match_location>},
5373 {
nullptr,
nullptr }
5375 luaL_setfuncs(L, map_callbacks, 0);
5379 cmd_log_ <<
"Adding units module...\n";
5380 static luaL_Reg
const unit_callbacks[] {
5383 {
"erase", &dispatch<&game_lua_kernel::intf_erase_unit>},
5384 {
"extract", &dispatch<&game_lua_kernel::intf_extract_unit>},
5385 {
"matches", &dispatch<&game_lua_kernel::intf_match_unit>},
5386 {
"select", &dispatch<&game_lua_kernel::intf_select_unit>},
5387 {
"to_map", &dispatch<&game_lua_kernel::intf_put_unit>},
5388 {
"to_recall", &dispatch<&game_lua_kernel::intf_put_recall_unit>},
5390 {
"teleport", &dispatch<&game_lua_kernel::intf_teleport>},
5392 {
"ability", &dispatch<&game_lua_kernel::intf_unit_ability>},
5403 {
"find_on_map", &dispatch<&game_lua_kernel::intf_get_units>},
5404 {
"find_on_recall", &dispatch<&game_lua_kernel::intf_get_recall_units>},
5405 {
"get", &dispatch<&game_lua_kernel::intf_get_unit>},
5406 {
"get_hovered", &dispatch<&game_lua_kernel::intf_get_displayed_unit>},
5407 {
"create_animator", &dispatch<&game_lua_kernel::intf_create_animator>},
5410 {
nullptr,
nullptr }
5412 lua_getglobal(L,
"wesnoth");
5414 luaL_setfuncs(L, unit_callbacks, 0);
5415 lua_setfield(L, -2,
"units");
5419 cmd_log_ <<
"Adding sides module...\n";
5420 static luaL_Reg
const side_callbacks[] {
5421 {
"is_enemy", &dispatch<&game_lua_kernel::intf_is_enemy> },
5422 {
"matches", &dispatch<&game_lua_kernel::intf_match_side> },
5423 {
"set_id", &dispatch<&game_lua_kernel::intf_set_side_id> },
5428 {
"find", &dispatch<&game_lua_kernel::intf_get_sides> },
5429 {
"get", &dispatch<&game_lua_kernel::intf_get_side> },
5430 {
"create", &dispatch<&game_lua_kernel::intf_create_side> },
5432 {
"place_shroud", &dispatch2<&game_lua_kernel::intf_toggle_shroud, true>},
5433 {
"remove_shroud", &dispatch2<&game_lua_kernel::intf_toggle_shroud, false>},
5434 {
"override_shroud", &dispatch<&game_lua_kernel::intf_override_shroud>},
5435 {
"is_shrouded", &dispatch2<&game_lua_kernel::intf_get_fog_or_shroud, false>},
5437 {
"place_fog", &dispatch2<&game_lua_kernel::intf_toggle_fog, false>},
5438 {
"remove_fog", &dispatch2<&game_lua_kernel::intf_toggle_fog, true>},
5439 {
"is_fogged", &dispatch2<&game_lua_kernel::intf_get_fog_or_shroud, true>},
5440 {
nullptr,
nullptr }
5442 std::vector<lua_cpp::Reg>
const cpp_side_callbacks {
5443 {
"add_ai_component", std::bind(
intf_modify_ai, std::placeholders::_1,
"add")},
5444 {
"delete_ai_component", std::bind(
intf_modify_ai, std::placeholders::_1,
"delete")},
5445 {
"change_ai_component", std::bind(
intf_modify_ai, std::placeholders::_1,
"change")},
5449 lua_getglobal(L,
"wesnoth");
5451 luaL_setfuncs(L, side_callbacks, 0);
5453 lua_setfield(L, -2,
"sides");
5457 cmd_log_ <<
"Adding interface module...\n";
5458 static luaL_Reg
const intf_callbacks[] {
5459 {
"add_hex_overlay", &dispatch<&game_lua_kernel::intf_add_tile_overlay>},
5460 {
"remove_hex_overlay", &dispatch<&game_lua_kernel::intf_remove_tile_overlay>},
5461 {
"get_color_adjust", &dispatch<&game_lua_kernel::intf_get_color_adjust>},
5462 {
"color_adjust", &dispatch<&game_lua_kernel::intf_color_adjust>},
5463 {
"screen_fade", &dispatch<&game_lua_kernel::intf_screen_fade>},
5464 {
"delay", &dispatch<&game_lua_kernel::intf_delay>},
5465 {
"deselect_hex", &dispatch<&game_lua_kernel::intf_deselect_hex>},
5466 {
"highlight_hex", &dispatch<&game_lua_kernel::intf_highlight_hex>},
5467 {
"float_label", &dispatch<&game_lua_kernel::intf_float_label>},
5468 {
"get_displayed_unit", &dispatch<&game_lua_kernel::intf_get_displayed_unit>},
5469 {
"get_hovered_hex", &dispatch<&game_lua_kernel::intf_get_mouseover_tile>},
5470 {
"get_selected_hex", &dispatch<&game_lua_kernel::intf_get_selected_tile>},
5471 {
"lock", &dispatch<&game_lua_kernel::intf_lock_view>},
5472 {
"is_locked", &dispatch<&game_lua_kernel::intf_view_locked>},
5473 {
"scroll", &dispatch<&game_lua_kernel::intf_scroll>},
5474 {
"scroll_to_hex", &dispatch<&game_lua_kernel::intf_scroll_to_tile>},
5475 {
"skip_messages", &dispatch<&game_lua_kernel::intf_skip_messages>},
5476 {
"is_skipping_messages", &dispatch<&game_lua_kernel::intf_is_skipping_messages>},
5477 {
"zoom", &dispatch<&game_lua_kernel::intf_zoom>},
5478 {
"clear_menu_item", &dispatch<&game_lua_kernel::intf_clear_menu_item>},
5479 {
"set_menu_item", &dispatch<&game_lua_kernel::intf_set_menu_item>},
5480 {
"allow_end_turn", &dispatch<&game_lua_kernel::intf_allow_end_turn>},
5481 {
"clear_chat_messages", &dispatch<&game_lua_kernel::intf_clear_messages>},
5482 {
"end_turn", &dispatch<&game_lua_kernel::intf_end_turn>},
5484 {
"add_chat_message", &dispatch<&game_lua_k