117 #include <functional> 129 #include <SDL2/SDL_timer.h> 137 #define LOG_LUA LOG_STREAM(info, log_scripting_lua) 138 #define WRN_LUA LOG_STREAM(warn, log_scripting_lua) 139 #define ERR_LUA LOG_STREAM(err, log_scripting_lua) 142 #define ERR_WML LOG_STREAM(err, log_wml) 150 template <member_callback method>
152 return ((lua_kernel_base::get_lua_kernel<game_lua_kernel>(L)).*method)(L);
158 template <member_callback2 method,
bool b>
160 return ((lua_kernel_base::get_lua_kernel<game_lua_kernel>(L)).*method)(L,
b);
179 game_lua_kernel::preload_scripts.clear();
181 game_lua_kernel::preload_scripts.push_back(cfg);
183 game_lua_kernel::preload_config = game_config.
child(
"game_config");
189 lua_chat(context, msg);
195 game_display_->get_chat_manager().add_chat_message(std::time(
nullptr), caption, 0, msg,
210 if(!sides.
empty()) {
WRN_LUA <<
"ignoring duplicate side filter information (inline side=)"; }
223 struct queued_event_context
226 std::stack<qe const *> & stack_;
228 queued_event_context(qe
const *new_qe, std::stack<qe const*> & stack)
234 ~queued_event_context()
249 lua_pushinteger(L, disp->viewing_side());
250 lua_pushboolean(L, disp->show_everything());
268 anim.~unit_animator();
277 std::string which = luaL_checkstring(L, 3);
279 std::string hits_str = luaL_checkstring(L, 4);
289 if(lua_istable(L, 5)) {
290 lua_getfield(L, 5,
"target");
293 return luaL_argerror(L, 5,
"target location must be different from animated unit's location");
295 return luaL_argerror(L, 5,
"target location must be adjacent to the animated unit");
300 if(!lua_isnoneornil(L, -1)) {
306 lua_getfield(L, 5,
"value");
307 if(lua_isnumber(L, -1)) {
308 v1 = lua_tointeger(L, -1);
309 }
else if(lua_istable(L, -1)) {
310 lua_rawgeti(L, -1, 1);
311 v1 = lua_tointeger(L, -1);
313 lua_rawgeti(L, -1, 2);
314 v2 = lua_tointeger(L, -1);
316 }
else if(!lua_isnoneornil(L, -1)) {
317 return luaW_type_error(L, 5,
"value",
"number or array of two numbers");
321 lua_getfield(L, 5,
"with_bars");
322 if(lua_isboolean(L, -1)) {
324 }
else if(!lua_isnoneornil(L, -1)) {
325 return luaW_type_error(L, 5,
"with_bars", lua_typename(L, LUA_TBOOLEAN));
329 lua_getfield(L, 5,
"text");
330 if(lua_isstring(L, -1)) {
331 text = lua_tostring(L, -1);
334 }
else if(!lua_isnoneornil(L, -1)) {
339 lua_getfield(L, 5,
"color");
340 if(lua_istable(L, -1) && lua_rawlen(L, -1) == 3) {
341 int idx = lua_absindex(L, -1);
342 lua_rawgeti(L, idx, 1);
343 lua_rawgeti(L, idx, 2);
344 lua_rawgeti(L, idx, 3);
345 color =
color_t(lua_tointeger(L, -3), lua_tointeger(L, -2), lua_tointeger(L, -1));
347 }
else if(!lua_isnoneornil(L, -1)) {
352 lua_getfield(L, 5,
"primary");
354 if(!primary && !lua_isnoneornil(L, -1)) {
359 lua_getfield(L, 5,
"secondary");
361 if(!secondary && !lua_isnoneornil(L, -1)) {
365 }
else if(!lua_isnoneornil(L, 5)) {
369 anim.
add_animation(up, which, u.
get_location(), dest, v1, bars, text, color, hits, primary, secondary, v2);
380 play_controller_.play_slice(
false);
397 const char* m = lua_tostring(L, 2);
405 luaL_Reg metafuncs[] {
409 {
"run", &dispatch<&game_lua_kernel::impl_run_animation>},
413 luaL_setfuncs(L, metafuncs, 0);
414 lua_pushstring(L,
"__metatable");
417 lua_setmetatable(L, -2);
440 if(lua_isstring(L, 1) && !lua_isnumber(L, 1)) {
441 std::string
id = luaL_checkstring(L, 1);
442 for(
const unit& u : units()) {
451 return luaL_argerror(L, 1,
"expected string or location");
455 if (!ui.
valid())
return 0;
468 if (!game_display_) {
473 game_display_->displayed_unit_hex(),
474 teams()[game_display_->viewing_team()],
475 game_display_->show_everything());
476 if (!ui.
valid())
return 0;
494 std::vector<const unit*> units;
498 return luaL_argerror(L, 2,
"unit not found");
501 }
else if(!lua_isnoneornil(L, 2)) {
505 return luaL_argerror(L, 2,
"invalid location");
518 for (
const unit * ui : units) {
520 lua_rawseti(L, 1, i);
540 lua_pushboolean(L,
true);
546 WRN_LUA <<
"wesnoth.units.matches called with a secondary unit (3rd argument), ";
547 WRN_LUA <<
"but unit to match was on recall list. ";
548 WRN_LUA <<
"Thus the 3rd argument is ignored.";
549 team &
t = board().get_team(side);
555 return luaL_argerror(L, 3,
"unit not found");
557 lua_pushboolean(L,
unit_filter(filter).matches(*u, *u_adj));
561 team &
t = board().get_team(side);
563 lua_pushboolean(L,
unit_filter(filter).matches(*u, loc));
568 lua_pushboolean(L,
unit_filter(filter).matches(*u, loc));
589 for (
team &
t : teams())
593 if (!filter.
null()) {
595 t.save_id_or_number(),
t.recall_list().find_index(u->id()));
600 lua_rawseti(L, 1, i);
619 char const *m = luaL_checkstring(L, 1);
648 b = std::get<0>(play_controller_.pump().fire(
"", m, l1, l2, data));
651 b = std::get<0>(play_controller_.pump().fire(m, l1, l2, data));
653 lua_pushboolean(L, b);
671 char const *m = luaL_checkstring(L, 1);
675 bool b = game_state_.get_wml_menu_items().fire_item(m, l1,
gamedata(), game_state_, units());
676 lua_pushboolean(L, b);
688 char const *m = luaL_checkstring(L, 1);
700 const std::string m = luaL_checkstring(L, 1);
701 if(m.empty())
return luaL_argerror(L, 1,
"empty variable name");
702 if (lua_isnoneornil(L, 2)) {
715 cfg[
"side"] = teams().size() + 1;
716 game_state_.add_side_wml(cfg);
717 lua_pushinteger(L, teams().
size());
724 game_state_.get_wml_menu_items().set_item(luaL_checkstring(L, 1),
luaW_checkvconfig(L,2));
730 std::string ids(luaL_checkstring(L, 1));
733 WRN_LUA <<
"[clear_menu_item] has been given an empty id=, ignoring";
736 game_state_.get_wml_menu_items().erase(
id);
750 if(lua_istable(L, 2)) {
762 return luaL_argerror(L, 2,
"expected list of locations");
765 game_display_->labels().recalculate_shroud();
766 game_display_->recalculate_minimap();
767 game_display_->invalidate_all();
781 if(lua_istable(L, 2)) {
791 game_display_->labels().recalculate_shroud();
792 game_display_->recalculate_minimap();
793 game_display_->invalidate_all();
804 if (!game_display_) {
809 if(!map().on_board(loc))
return luaL_argerror(L, 1,
"not on board");
810 game_display_->highlight_hex(loc);
811 game_display_->display_unit_hex(loc);
823 unsigned side_1, side_2;
827 side_1 = luaL_checkinteger(L, 1);
832 side_2 = luaL_checkinteger(L, 2);
834 if (side_1 > teams().
size() || side_2 > teams().
size())
return 0;
835 lua_pushboolean(L, board().get_team(side_1).is_enemy(side_2));
845 if (!game_display_) {
849 lua_pushboolean(L, game_display_->view_locked());
861 game_display_->set_view_locked(lock);
869 lua_pushstring(L, tod.
id.c_str());
870 lua_setfield(L, -2,
"id");
872 lua_setfield(L, -2,
"lawful_bonus");
874 lua_setfield(L, -2,
"bonus_modified");
875 lua_pushstring(L, tod.
image.c_str());
876 lua_setfield(L, -2,
"image");
878 lua_setfield(L, -2,
"name");
879 lua_pushstring(L, tod.
sounds.c_str());
880 lua_setfield(L, -2,
"sound");
882 lua_setfield(L, -2,
"mask");
884 lua_pushinteger(L, tod.
color.
r);
885 lua_setfield(L, -2,
"red");
886 lua_pushinteger(L, tod.
color.
g);
887 lua_setfield(L, -2,
"green");
888 lua_pushinteger(L, tod.
color.
b);
889 lua_setfield(L, -2,
"blue");
896 lua_newuserdatauv(L, 0, 1);
897 lua_pushinteger(L, area_index);
898 lua_setiuservalue(L, -2, 1);
899 if(luaL_newmetatable(L,
"schedule")) {
900 static luaL_Reg
const schedule_meta[] {
901 {
"__index", &dispatch<&game_lua_kernel::impl_schedule_get>},
902 {
"__newindex", &dispatch<&game_lua_kernel::impl_schedule_set>},
903 {
"__len", &dispatch<&game_lua_kernel::impl_schedule_len>},
906 luaL_setfuncs(L, schedule_meta, 0);
908 lua_setmetatable(L, -2);
913 int save_top = lua_gettop(L);
914 luaL_checkudata(L, idx,
"schedule");
915 lua_getiuservalue(L, idx, 1);
916 int i = luaL_checkinteger(L, -1);
917 lua_settop(L, save_top);
924 if(lua_isnumber(L, 2)) {
925 const auto& times = area_index < 0 ? tod_man().times() : tod_man().times(area_index);
926 int i = lua_tointeger(L, 2) - 1;
927 if(i < 0 || i >= static_cast<int>(times.size())) {
928 return luaL_argerror(L, 2,
"invalid time of day index");
933 const char* m = luaL_checkstring(L, 2);
934 if(area_index >= 0) {
937 if(strcmp(m,
"hexes") == 0) {
938 const auto& hexes = tod_man().get_area_by_index(area_index);
957 const auto& times = area_index < 0 ? tod_man().times() : tod_man().times(area_index);
958 lua_pushinteger(L, times.size());
965 if(lua_isnumber(L, 2)) {
966 std::vector<time_of_day> times = area_index < 0 ? tod_man().times() : tod_man().times(area_index);
967 int i = lua_tointeger(L, 2) - 1;
968 if(i < 0 || i >= static_cast<int>(times.size())) {
969 return luaL_argerror(L, 2,
"invalid time of day index");
974 tod_man().replace_schedule(times);
976 tod_man().replace_local_schedule(times, area_index);
979 const char* m = luaL_checkstring(L, 2);
980 if(strcmp(m,
"time_of_day") == 0) {
981 std::string value = luaL_checkstring(L, 3);
982 const auto& times = area_index < 0 ? tod_man().times() : tod_man().times(area_index);
983 auto iter = std::find_if(times.begin(), times.end(), [&value](
const time_of_day& tod) {
984 return tod.id == value;
986 if(iter == times.end()) {
987 std::ostringstream
err;
988 err <<
"invalid time of day ID for ";
990 err <<
"global schedule";
992 const std::string&
id = tod_man().get_area_id(area_index);
994 const auto& hexes = tod_man().get_area_by_index(area_index);
996 err <<
"anonymous empty time area";
998 err <<
"anonymous time area at (" << hexes.begin()->wml_x() <<
',' << hexes.begin()->wml_y() <<
")";
1001 err <<
"time area with id=" <<
id;
1005 return lua_error(L);
1007 int n = std::distance(times.begin(), iter);
1008 if(area_index < 0) {
1009 tod_man().set_current_time(n);
1011 tod_man().set_current_time(n, area_index);
1014 if(area_index >= 0) {
1016 if(strcmp(m,
"hexes") == 0) {
1018 tod_man().replace_area_locations(area_index, hexes);
1023 if(lua_isnil(L, 3) && strcmp(m,
"liminal_bonus") == 0) {
1024 tod_man().reset_max_liminal_bonus();
1040 char const *m = luaL_checkstring(L, 2);
1046 lua_pushstring(L, info.
id().c_str());
1047 lua_setfield(L, -2,
"id");
1049 lua_setfield(L, -2,
"name");
1051 lua_setfield(L, -2,
"editor_name");
1053 lua_setfield(L, -2,
"description");
1055 lua_setfield(L, -2,
"icon");
1057 lua_setfield(L, -2,
"editor_image");
1059 lua_setfield(L, -2,
"light");
1061 lua_setfield(L, -2,
"village");
1063 lua_setfield(L, -2,
"castle");
1064 lua_pushboolean(L, info.
is_keep());
1065 lua_setfield(L, -2,
"keep");
1067 lua_setfield(L, -2,
"healing");
1078 template<
bool cons
ider_illuminates>
1081 int for_turn = tod_man().turn();
1085 if(!board().map().on_board_with_border(loc)) {
1086 return luaL_argerror(L, 1,
"coordinates are not on board");
1088 }
else if(lua_isstring(L, 1)) {
1089 auto area = tod_man().get_area_by_id(lua_tostring(L, 1));
1091 return luaL_error(L,
"invalid or empty time_area ID");
1094 loc = *area.begin();
1095 }
else if(!lua_isnil(L, 1)) {
1098 return luaL_error(L,
"empty time_area");
1101 loc = *area.begin();
1104 if(lua_isnumber(L, 2)) {
1105 for_turn = luaL_checkinteger(L, 2);
1106 int number_of_turns = tod_man().number_of_turns();
1107 if(for_turn < 1 || (number_of_turns != -1 && for_turn > number_of_turns)) {
1108 return luaL_argerror(L, 2,
"turn number out of range");
1113 tod_man().get_illuminated_time_of_day(board().units(), board().map(), loc, for_turn) :
1114 tod_man().get_time_of_day(loc, for_turn);
1129 if (!board().map().is_village(loc))
1132 int side = board().village_owner(loc);
1133 if (!side)
return 0;
1134 lua_pushinteger(L, side);
1146 if(!board().map().is_village(loc)) {
1150 const int old_side_num = board().village_owner(loc);
1151 const int new_side_num = lua_isnoneornil(L, 2) ? 0 : luaL_checkinteger(L, 2);
1153 team* old_side =
nullptr;
1154 team* new_side =
nullptr;
1156 if(old_side_num == new_side_num) {
1161 old_side = &board().get_team(old_side_num);
1162 }
catch(
const std::out_of_range&) {
1168 new_side = &board().get_team(new_side_num);
1169 }
catch(
const std::out_of_range&) {
1175 if(new_side && board().team_is_defeated(*new_side)) {
1203 if (!game_display_) {
1207 const map_location &loc = game_display_->mouseover_hex();
1208 if (!board().map().on_board(loc))
return 0;
1209 lua_pushinteger(L, loc.
wml_x());
1210 lua_pushinteger(L, loc.
wml_y());
1221 if (!game_display_) {
1225 const map_location &loc = game_display_->selected_hex();
1226 if (!board().map().on_board(loc))
return 0;
1227 lua_pushinteger(L, loc.
wml_x());
1228 lua_pushinteger(L, loc.
wml_y());
1241 std::string m = luaL_checkstring(L, 1);
1247 return luaL_argerror(L, 1, (
"Cannot find ressource with id '" + m +
"'").c_str());
1259 char const *m = luaL_checkstring(L, 1);
1272 LOG_LUA <<
"impl_game_config_get";
1273 char const *m = luaL_checkstring(L, 2);
1286 utils::split(play_controller_.get_loaded_resources()));
1288 if(strcmp(m,
"global_traits") == 0) {
1291 const std::string&
id = trait[
"id"];
1295 lua_pushstring(L,
id.c_str());
1306 if(classification.
type==campaign_type::type::multiplayer) {
1324 LOG_LUA <<
"impl_game_config_set";
1325 char const *m = luaL_checkstring(L, 2);
1341 game_display_->set_theme(value);
1349 static config find_addon(
const std::string&
type,
const std::string&
id)
1358 const char* m = luaL_checkstring(L, 2);
1376 struct end_level_committer {
1378 ~end_level_committer() {
1379 pc_.set_end_level_data(data_);
1390 const char* m = luaL_checkstring(L, 2);
1391 end_level_committer commit(data, play_controller_);
1406 data->~end_level_data();
1412 void*
p = lua_touserdata(L, lua_upvalueindex(1));
1414 if(lua_type(L, 2) == LUA_TNUMBER) {
1417 size_t i = luaL_checkinteger(L, 2);
1419 lua_createtable(L, 2, 0);
1420 lua_pushstring(L,
"options");
1427 if(i < settings.
addons.size()) {
1428 auto iter = settings.
addons.begin();
1429 std::advance(iter, i);
1431 iter->second.write(cfg);
1432 cfg[
"id"] = iter->first;
1434 lua_createtable(L, 2, 0);
1435 lua_pushstring(L,
"addon");
1443 char const *m = luaL_checkstring(L, 2);
1471 if(strcmp(m,
"savegame") == 0) {
1473 if(
savegame == saved_game_mode::type::no) {
1474 lua_pushboolean(L,
false);
1480 if(strcmp(m,
"side_players") == 0) {
1484 if(strcmp(m,
"addons") == 0) {
1485 for(
const auto& [
id, addon] : settings.
addons) {
1486 lua_createtable(L, 0, 4);
1488 lua_setfield(L, -2,
"id");
1490 lua_setfield(L, -2,
"name");
1491 lua_pushboolean(L, addon.required);
1492 lua_setfield(L, -2,
"required");
1493 if(addon.min_version) {
1495 lua_push(L, addon.min_version->str());
1497 lua_setfield(L, -2,
"min_version");
1503 lua_setfield(L, -2,
"version");
1505 lua_createtable(L, addon.content.size(), 0);
1506 for(
const auto& content : addon.content) {
1507 lua_createtable(L, 0, 3);
1509 lua_setfield(L, -2,
"id");
1511 lua_setfield(L, -2,
"name");
1513 lua_setfield(L, -2,
"type");
1514 lua_seti(L, -2, lua_rawlen(L, -2) + 1);
1516 lua_setfield(L, -2,
"content");
1532 void*
p = lua_touserdata(L, lua_upvalueindex(1));
1534 lua_pushinteger(L, settings.
addons.size() + 1);
1546 LOG_LUA <<
"impl_scenario_get";
1547 char const *m = luaL_checkstring(L, 2);
1556 if(strcmp(m,
"resources") == 0) {
1558 for(
const std::string& rsrc :
utils::split(play_controller_.get_loaded_resources())) {
1559 resources.push_back(find_addon(
"resource", rsrc));
1571 if(!classification.
campaign.empty()) {
1574 if(strcmp(m,
"modifications") == 0) {
1575 std::vector<config> mods;
1576 for(
const std::string& mod : classification.
active_mods) {
1577 mods.push_back(find_addon(
"modification", mod));
1582 if(strcmp(m,
"end_level_data") == 0) {
1583 if (!play_controller_.is_regular_game_end()) {
1586 auto data = play_controller_.get_end_level_data();
1588 if(luaL_newmetatable(L,
"end level data")) {
1589 static luaL_Reg
const callbacks[] {
1591 {
"__newindex", &dispatch<&game_lua_kernel::impl_end_level_data_set>},
1593 {
nullptr,
nullptr }
1595 luaL_setfuncs(L, callbacks, 0);
1597 lua_setmetatable(L, -2);
1603 if(strcmp(m,
"mp_settings") == 0) {
1604 lua_newuserdatauv(L, 0, 0);
1605 if(luaL_newmetatable(L,
"mp settings")) {
1606 lua_pushlightuserdata(L, &play_controller_);
1608 lua_setfield(L, -2,
"__index");
1609 lua_pushlightuserdata(L, &play_controller_);
1611 lua_setfield(L, -2,
"__len");
1612 lua_pushstring(L,
"mp settings");
1613 lua_setfield(L, -2,
"__metatable");
1615 lua_setmetatable(L, -2);
1631 LOG_LUA <<
"impl_scenario_set";
1632 char const *m = luaL_checkstring(L, 2);
1644 if(strcmp(m,
"end_level_data") == 0) {
1651 data.
replay_save = cfg[
"replay_save"].to_bool(
true);
1654 data.
is_victory = cfg[
"result"] == level_result::victory;
1655 data.
test_result = cfg[
"test_result"].str(level_result::result_not_set);
1656 play_controller_.set_end_level_data(data);
1676 return "local_choice";
1695 char const *m = luaL_checkstring(L, 2);
1703 if(strcmp(m,
"map") == 0) {
1706 if(strcmp(m,
"schedule") == 0) {
1707 luaW_push_schedule(L, -1);
1711 if (strcmp(m,
"event_context") == 0)
1715 cfg[
"name"] = ev.
name;
1727 cfg[
"damage_inflicted"] = di;
1757 if (lua_isnone(L, 2)) {
1763 LOG_LUA <<
"Script says: \"" << m <<
"\"";
1769 if(!game_display_) {
1772 double factor = luaL_checknumber(L, 1);
1775 factor *= game_display_->get_zoom_factor();
1780 lua_pushnumber(L, game_display_->get_zoom_factor());
1789 if (game_display_) {
1790 game_display_->get_chat_manager().clear_chat_messages();
1799 if (!lua_isnoneornil(L, 1)) {
1800 int max = 2 * teams().size();
1801 int npn = luaL_checkinteger(L, 1);
1802 if (npn <= 0 || npn > max) {
1803 return luaL_argerror(L, 1,
"side number out of range");
1807 play_controller_.force_end_turn();
1820 lua_pushboolean(L, b);
1838 const unit* u =
nullptr;
1839 int viewing_side = 0;
1841 if (lua_isuserdata(L, arg))
1845 viewing_side = u->
side();
1854 viewing_side = u->
side();
1861 if (!board().map().on_board(src))
1862 return luaL_argerror(L, 1,
"invalid location");
1863 if (!board().map().on_board(dst))
1864 return luaL_argerror(L, arg,
"invalid location");
1867 const gamemap &map = board().map();
1868 bool ignore_units =
false, see_all =
false, ignore_teleport =
false;
1869 double stop_at = 10000;
1870 std::unique_ptr<pathfind::cost_calculator> calc;
1872 if (lua_istable(L, arg))
1874 ignore_units = luaW_table_get_def<bool>(L, arg,
"ignore_units",
false);
1875 see_all = luaW_table_get_def<bool>(L, arg,
"ignore_visibility",
false);
1876 ignore_teleport = luaW_table_get_def<bool>(L, arg,
"ignore_teleport",
false);
1878 stop_at = luaW_table_get_def<double>(L, arg,
"max_cost", stop_at);
1881 lua_pushstring(L,
"viewing_side");
1883 if (!lua_isnil(L, -1)) {
1884 int i = luaL_checkinteger(L, -1);
1885 if (i >= 1 && i <= static_cast<int>(teams().
size())) viewing_side =
i;
1889 if(u) see_all =
true;
1890 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.");
1895 lua_pushstring(L,
"calculate");
1897 if(lua_isfunction(L, -1)) {
1902 else if (lua_isfunction(L, arg))
1910 if(!ignore_teleport) {
1911 if(viewing_side == 0) {
1912 lua_warning(L,
"wesnoth.paths.find_path: ignore_teleport=false requires a valid viewing_side; continuing with ignore_teleport=true",
false);
1913 ignore_teleport =
true;
1921 return luaL_argerror(L, 1,
"unit not found OR custom cost function not provided");
1925 teams(), map, ignore_units,
false, see_all));
1929 &teleport_locations);
1931 int nb = res.
steps.size();
1932 lua_createtable(L, nb, 0);
1933 for (
int i = 0;
i < nb; ++
i)
1936 lua_rawseti(L, -2,
i + 1);
1952 const unit* u =
nullptr;
1954 if (lua_isuserdata(L, arg))
1964 return luaL_argerror(L, 1,
"unit not found");
1969 int viewing_side = u->
side();
1970 bool ignore_units =
false, see_all =
false, ignore_teleport =
false;
1971 int additional_turns = 0;
1973 if (lua_istable(L, arg))
1975 ignore_units = luaW_table_get_def<bool>(L, arg,
"ignore_units",
false);
1976 see_all = luaW_table_get_def<bool>(L, arg,
"ignore_visibility",
false);
1977 ignore_teleport = luaW_table_get_def<bool>(L, arg,
"ignore_teleport",
false);
1978 additional_turns = luaW_table_get_def<int>(L, arg,
"max_cost", additional_turns);
1980 lua_pushstring(L,
"viewing_side");
1982 if (!lua_isnil(L, -1)) {
1983 int i = luaL_checkinteger(L, -1);
1984 if (i >= 1 && i <= static_cast<int>(teams().
size())) viewing_side =
i;
1988 if(u) see_all =
true;
1995 const team& viewing_team = board().get_team(viewing_side);
1998 viewing_team, additional_turns, see_all, ignore_units);
2001 lua_createtable(L, nb, 0);
2002 for (
int i = 0;
i < nb; ++
i)
2007 lua_rawseti(L, -2, 1);
2009 lua_rawseti(L, -2, 2);
2011 lua_rawseti(L, -2, 3);
2012 lua_rawseti(L, -2,
i + 1);
2027 const unit* u =
nullptr;
2029 if (lua_isuserdata(L, arg))
2039 return luaL_argerror(L, 1,
"unit not found");
2046 return luaL_error(L,
"wesnoth.find_vision_range: requires a valid unit");
2049 std::map<map_location, int> jamming_map;
2053 lua_createtable(L, res.destinations.size() + res.edges.size(), 0);
2054 for(
const auto&
d : res.destinations) {
2056 lua_pushinteger(L,
d.curr.wml_x());
2057 lua_rawseti(L, -2, 1);
2058 lua_pushinteger(L,
d.curr.wml_y());
2059 lua_rawseti(L, -2, 2);
2060 lua_pushinteger(L,
d.move_left);
2061 lua_rawseti(L, -2, 3);
2062 lua_rawseti(L, -2, lua_rawlen(L, -2) + 1);
2064 for(
const auto&
e : res.edges) {
2066 lua_pushinteger(L,
e.wml_x());
2067 lua_rawseti(L, -2, 1);
2068 lua_pushinteger(L,
e.wml_y());
2069 lua_rawseti(L, -2, 2);
2070 lua_pushinteger(L, -1);
2071 lua_rawseti(L, -2, 3);
2072 lua_rawseti(L, -2, lua_rawlen(L, -2) + 1);
2077 template<
typename T>
2080 for (
int i = 1, i_end = lua_rawlen(L, arg);
i <= i_end; ++
i)
2083 lua_rawgeti(L, arg,
i);
2084 int entry = lua_gettop(L);
2085 if (!lua_istable(L, entry)) {
2093 lua_rawgeti(L, entry, 3);
2094 if (!lua_isnumber(L, -1)) {
2095 lua_getfield(L, entry,
"side");
2096 if (!lua_isnumber(L, -1)) {
2100 int side = lua_tointeger(L, -1);
2102 lua_rawgeti(L, entry, 4);
2103 if (!lua_isstring(L, -1)) {
2104 lua_getfield(L, entry,
"type");
2105 if (!lua_isstring(L, -1)) {
2109 std::string
unit_type = lua_tostring(L, -1);
2111 fake_units.emplace_back(src, side, unit_type);
2113 lua_settop(L, entry - 1);
2117 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");
2135 std::vector<const ::unit*> real_units;
2136 typedef std::vector<std::tuple<map_location, int, std::string>> unit_type_vector;
2142 real_units.push_back(unit);
2144 else if (!filter.
null())
2147 if(match->get_location().valid()) {
2148 real_units.push_back(match);
2158 real_units.push_back(&(*ui));
2163 if (lua_istable(L, arg))
2169 if(real_units.empty() && fake_units.empty())
2171 return luaL_argerror(L, 1,
"unit(s) not found");
2174 int viewing_side = 0;
2175 bool ignore_units =
true, see_all =
true, ignore_teleport =
false,
debug =
false, use_max_moves =
false;
2177 if (lua_istable(L, arg))
2179 lua_pushstring(L,
"ignore_units");
2181 if (!lua_isnil(L, -1))
2187 lua_pushstring(L,
"ignore_teleport");
2189 if (!lua_isnil(L, -1))
2195 lua_pushstring(L,
"viewing_side");
2197 if (!lua_isnil(L, -1))
2199 int i = luaL_checkinteger(L, -1);
2200 if (i >= 1 && i <= static_cast<int>(teams().
size()))
2207 lua_pushstring(L,
"debug");
2209 if (!lua_isnil(L, -1))
2215 lua_pushstring(L,
"use_max_moves");
2217 if (!lua_isnil(L, -1))
2234 const terrain_filter t_filter(filter, &fc,
false);
2235 t_filter.get_locations(location_set,
true);
2239 const team& viewing_team = viewing_side
2240 ? board().get_team(viewing_side)
2241 : board().teams()[0];
2244 ignore_units, !ignore_teleport, viewing_team, see_all, ignore_units);
2246 for (const ::unit*
const u : real_units)
2248 cost_map.
add_unit(*u, use_max_moves);
2250 for (
const unit_type_vector::value_type& fu : fake_units)
2253 cost_map.
add_unit(std::get<0>(fu), ut, std::get<1>(fu));
2258 if (game_display_) {
2259 game_display_->labels().clear_all();
2262 std::stringstream
s;
2266 game_display_->labels().set_label(loc, s.str());
2272 lua_createtable(L, location_set.size(), 0);
2278 lua_pushinteger(L, loc.wml_x());
2279 lua_rawseti(L, -2, 1);
2281 lua_pushinteger(L, loc.wml_y());
2282 lua_rawseti(L, -2, 2);
2284 lua_pushinteger(L, cost_map.
get_pair_at(loc).first);
2285 lua_rawseti(L, -2, 3);
2287 lua_pushinteger(L, cost_map.
get_pair_at(loc).second);
2288 lua_rawseti(L, -2, 4);
2290 lua_rawseti(L, -2, counter);
2300 return reinterpret_cast<int*
>(luaL_checkudata(L, idx,
labelKey));
2305 const char* m = luaL_checkstring(L, 2);
2313 int fade = luaL_optinteger(L, 2, -1);
2351 double width_ratio = 0;
2353 int lifetime = 2
'000, fadeout = 100; 2354 font::ALIGN alignment = font::ALIGN::CENTER_ALIGN, vertical_alignment = font::ALIGN::CENTER_ALIGN; 2355 // This is actually a relative screen location in pixels, but map_location already supports 2356 // everything needed to read in a pair of coordinates. 2357 // Depending on the chosen alignment, it may be relative to centre, an edge centre, or a corner. 2358 map_location loc{0, 0, wml_loc()}; 2359 if(lua_istable(L, 2)) { 2360 if(luaW_tableget(L, 2, "size")) { 2361 size = luaL_checkinteger(L, -1); 2363 if(luaW_tableget(L, 2, "max_width")) { 2365 width = lua_tointegerx(L, -1, &found_number); 2367 auto value = luaW_tostring(L, -1); 2369 if(!value.empty() && value.back() == '%
') { 2370 value.remove_suffix(1); 2371 width_ratio = std::stoi(std::string(value)) / 100.0; 2372 } else throw std::invalid_argument(value.data()); 2373 } catch(std::invalid_argument&) { 2374 return luaL_argerror(L, -1, "max_width should be integer or percentage"); 2379 if(luaW_tableget(L, 2, "color")) { 2380 if(lua_isstring(L, -1)) { 2381 color = color_t::from_hex_string(lua_tostring(L, -1)); 2383 auto vec = lua_check<std::vector<int>>(L, -1); 2384 if(vec.size() != 3) { 2385 return luaL_error(L, "floating label text color should be a hex string or an array of 3 integers"); 2392 if(luaW_tableget(L, 2, "bgcolor")) { 2393 if(lua_isstring(L, -1)) { 2394 bgcolor = color_t::from_hex_string(lua_tostring(L, -1)); 2396 auto vec = lua_check<std::vector<int>>(L, -1); 2397 if(vec.size() != 3) { 2398 return luaL_error(L, "floating label background color should be a hex string or an array of 3 integers"); 2403 bgcolor.a = ALPHA_OPAQUE; 2405 if(luaW_tableget(L, 2, "bgalpha")) { 2406 bgcolor.a = luaL_checkinteger(L, -1); 2409 if(luaW_tableget(L, 2, "duration")) { 2411 lifetime = lua_tointegerx(L, -1, &found_number); 2413 auto value = luaW_tostring(L, -1); 2414 if(value == "unlimited") { 2417 return luaL_argerror(L, -1, "duration should be integer or 'unlimited
'"); 2421 if(luaW_tableget(L, 2, "fade_time")) { 2422 fadeout = lua_tointeger(L, -1); 2424 if(luaW_tableget(L, 2, "location")) { 2425 loc = luaW_checklocation(L, -1); 2427 if(luaW_tableget(L, 2, "halign")) { 2428 static const char* options[] = {"left", "center", "right"}; 2429 alignment = font::ALIGN(luaL_checkoption(L, -1, nullptr, options)); 2431 if(luaW_tableget(L, 2, "valign")) { 2432 static const char* options[] = {"top", "center", "bottom"}; 2433 vertical_alignment = font::ALIGN(luaL_checkoption(L, -1, nullptr, options)); 2437 int* handle = nullptr; 2439 // Creating a new label, allocate a new handle 2440 handle = new(L)int(); 2442 // First argument is the label handle 2443 handle = luaW_check_floating_label(L, 1); 2445 int handle_idx = lua_gettop(L); 2448 font::remove_floating_label(*handle); 2451 SDL_Rect rect = game_display_->map_outside_area(); 2452 if(width_ratio > 0) { 2453 width = static_cast<int>(std::round(rect.w * width_ratio)); 2457 case font::ALIGN::LEFT_ALIGN: 2458 x = rect.x + loc.wml_x(); 2460 rect.w = std::min(rect.w, width); 2463 case font::ALIGN::CENTER_ALIGN: 2464 x = rect.x + rect.w / 2 + loc.wml_x(); 2466 rect.w = std::min(rect.w, width); 2467 rect.x = x - rect.w / 2; 2470 case font::ALIGN::RIGHT_ALIGN: 2471 x = rect.x + rect.w - loc.wml_x(); 2473 rect.x = (rect.x + rect.w) - std::min(rect.w, width); 2474 rect.w = std::min(rect.w, width); 2478 switch(vertical_alignment) { 2479 case font::ALIGN::LEFT_ALIGN: // top 2480 y = rect.y + loc.wml_y(); 2482 case font::ALIGN::CENTER_ALIGN: 2483 y = rect.y + rect.h / 2 + loc.wml_y(); 2485 case font::ALIGN::RIGHT_ALIGN: // bottom 2486 // The size * 1.5 adjustment avoids the text being cut off if placed at y = 0 2487 // This is necessary because the text is positioned by the top edge but we want it to 2488 // seem like it's positioned by the bottom edge.
2490 y =
rect.y +
rect.h - loc.wml_y() -
static_cast<int>(size * 1.5);
2504 lua_settop(L, handle_idx);
2505 if(luaL_newmetatable(L,
labelKey)) {
2507 static const luaL_Reg methods[] = {
2508 {
"remove", &dispatch<&game_lua_kernel::intf_remove_floating_label>},
2509 {
"move", &dispatch<&game_lua_kernel::intf_move_floating_label>},
2510 {
"replace", &dispatch2<&game_lua_kernel::intf_set_floating_label, false>},
2512 {
nullptr,
nullptr }
2514 luaL_setfuncs(L, methods, 0);
2517 lua_setmetatable(L, handle_idx);
2518 lua_settop(L, handle_idx);
2525 game_display_->invalidate(loc);
2544 return luaL_error(L,
"Attempted to move a unit while the map is locked");
2549 if (!map().on_board(loc)) {
2550 return luaL_argerror(L, 2,
"invalid location");
2561 if (!map().on_board(loc))
2562 return luaL_argerror(L, 1,
"invalid location");
2565 put_unit_helper(loc);
2568 }
else if(!lua_isnoneornil(L, 1)) {
2569 const vconfig* vcfg =
nullptr;
2571 if (!map().on_board(loc)) {
2574 if (!map().on_board(loc))
2575 return luaL_argerror(L, 2,
"invalid location");
2579 put_unit_helper(loc);
2580 u->set_location(loc);
2587 play_controller_.pump().fire(
"unit_placed", loc);
2599 return luaL_error(L,
"Attempted to remove a unit while the map is locked");
2607 if (!map().on_board(loc)) {
2608 return luaL_argerror(L, 1,
"invalid location");
2611 team &
t = board().get_team(side);
2615 return luaL_argerror(L, 1,
"can't erase private units");
2618 if (!map().on_board(loc)) {
2619 return luaL_argerror(L, 1,
"invalid location");
2622 return luaL_argerror(L, 1,
"expected unit or location");
2638 return luaL_error(L,
"Attempted to move a unit while the map is locked");
2642 int side = lua_tointeger(L, 2);
2643 if (static_cast<unsigned>(side) > teams().
size()) side = 0;
2647 u = lu->get_shared();
2648 if(lu->on_recall_list() && lu->on_recall_list() == side) {
2649 return luaL_argerror(L, 1,
"unit already on recall list");
2652 const vconfig* vcfg =
nullptr;
2662 team &
t = board().get_team(side);
2664 std::size_t uid = u->underlying_id();
2669 units().erase(u->get_location());
2671 u->anim_comp().clear_haloes();
2673 lu->lua_unit::~lua_unit();
2687 return luaL_error(L,
"Attempted to remove a unit while the map is locked");
2693 u = units().extract(u->get_location());
2695 u->anim_comp().clear_haloes();
2696 }
else if (
int side = lu->on_recall_list()) {
2697 team &
t = board().get_team(side);
2705 lu->lua_unit::~lua_unit();
2721 if (!lua_isnoneornil(L, 2)) {
2725 const vconfig* vcfg =
nullptr;
2733 if (!res.
valid())
return 0;
2734 lua_pushinteger(L, res.
wml_x());
2735 lua_pushinteger(L, res.
wml_y());
2751 if (!lua_isnoneornil(L, 3)) {
2755 if (game_display_) {
2756 game_display_->float_label(loc, text, color);
2768 const vconfig* vcfg =
nullptr;
2798 char const *m = luaL_checkstring(L, 2);
2802 if(lua_isboolean(L, 3)) {
2804 if(!lua_isnoneornil(L, 4)) {
2807 }
else if(!lua_isnoneornil(L, 3)) {
2828 }
else if(lua_isstring(L, 2)) {
2829 char const *m = luaL_checkstring(L, 2);
2849 }
else if(lua_isstring(L, 2)) {
2850 char const *m = luaL_checkstring(L, 2);
2870 }
else if(lua_isstring(L, 2)) {
2871 char const *m = luaL_checkstring(L, 2);
2891 }
else if(lua_isstring(L, 2)) {
2892 char const *m = luaL_checkstring(L, 2);
2908 char const *m = luaL_checkstring(L, 2);
2922 char const *m = luaL_checkstring(L, 2);
2924 if (!utp)
return luaL_argerror(L, 2,
"unknown unit type");
2925 if(lua_isstring(L, 3)) {
2926 const std::string& m2 = lua_tostring(L, 3);
2927 if(!utp->
has_variation(m2))
return luaL_argerror(L, 2,
"unknown unit variation");
2941 lua_createtable(L, 0, 4);
2943 lua_setfield(L, -2,
"poisoned");
2944 lua_pushnumber(L, cmb.
slowed);
2945 lua_setfield(L, -2,
"slowed");
2947 lua_setfield(L, -2,
"untouched");
2949 lua_setfield(L, -2,
"average_hp");
2950 lua_createtable(L, n, 0);
2951 for (
int i = 0;
i <
n; ++
i) {
2953 lua_rawseti(L, -2,
i);
2955 lua_setfield(L, -2,
"hp_chance");
2964 lua_createtable(L, 0, 16);
2967 lua_setfield(L, -2,
"num_blows");
2968 lua_pushnumber(L, bcustats.
damage);
2969 lua_setfield(L, -2,
"damage");
2971 lua_setfield(L, -2,
"chance_to_hit");
2972 lua_pushboolean(L, bcustats.
poisons);
2973 lua_setfield(L, -2,
"poisons");
2974 lua_pushboolean(L, bcustats.
slows);
2975 lua_setfield(L, -2,
"slows");
2977 lua_setfield(L, -2,
"petrifies");
2978 lua_pushboolean(L, bcustats.
plagues);
2979 lua_setfield(L, -2,
"plagues");
2981 lua_setfield(L, -2,
"plague_type");
2982 lua_pushnumber(L, bcustats.
rounds);
2983 lua_setfield(L, -2,
"rounds");
2985 lua_setfield(L, -2,
"firststrike");
2986 lua_pushboolean(L, bcustats.
drains);
2987 lua_setfield(L, -2,
"drains");
2989 lua_setfield(L, -2,
"drain_constant");
2991 lua_setfield(L, -2,
"drain_percent");
2996 lua_setfield(L, -2,
"attack_num");
2998 lua_setfield(L, -2,
"number");
3000 if(bcustats.
weapon !=
nullptr)
3002 lua_pushstring(L, bcustats.
weapon->id().c_str());
3003 lua_setfield(L, -2,
"name");
3005 lua_setfield(L, -2,
"weapon");
3024 int arg_num = 1, att_w = -1, def_w = -1;
3028 if (lua_isnumber(L, arg_num)) {
3029 att_w = lua_tointeger(L, arg_num) - 1;
3030 if (att_w < 0 || att_w >= static_cast<int>(att->attacks().size()))
3031 return luaL_argerror(L, arg_num,
"weapon index out of bounds");
3037 if (lua_isnumber(L, arg_num)) {
3038 def_w = lua_tointeger(L, arg_num) - 1;
3039 if (def_w < 0 || def_w >= static_cast<int>(def->attacks().size()))
3040 return luaL_argerror(L, arg_num,
"weapon index out of bounds");
3045 def->get_location(), att_w, def_w, 0.0,
nullptr, att, def);
3061 if (play_controller_.is_skipping_replay())
return 0;
3062 char const *m = luaL_checkstring(L, 1);
3063 int repeats = luaL_optinteger(L, 2, 0);
3075 const char *content_for = luaL_checkstring(L, 1);
3076 const char *
id = luaL_checkstring(L, 2);
3079 if(group.content_for_ == content_for) {
3081 if(achieve.
id_ ==
id) {
3089 ERR_LUA <<
"Achievement " <<
id <<
" not found for achievement group " << content_for;
3095 ERR_LUA <<
"Achievement group " << content_for <<
" not found";
3107 const char *content_for = luaL_checkstring(L, 1);
3108 const char *
id = luaL_checkstring(L, 2);
3111 ERR_LUA <<
"Returning false for whether a player has completed an achievement due to being networked multiplayer.";
3112 lua_pushboolean(L,
false);
3128 const char *content_for = luaL_checkstring(L, 1);
3129 const char *
id = luaL_checkstring(L, 2);
3133 if(group.content_for_ == content_for) {
3134 for(
const auto& achieve : group.achievements_) {
3135 if(achieve.id_ ==
id) {
3137 cfg[
"id"] = achieve.id_;
3138 cfg[
"name"] = achieve.name_;
3139 cfg[
"name_completed"] = achieve.name_completed_;
3140 cfg[
"description"] = achieve.description_;
3141 cfg[
"description_completed"] = achieve.description_completed_;
3142 cfg[
"icon"] = achieve.icon_;
3143 cfg[
"icon_completed"] = achieve.icon_completed_;
3144 cfg[
"hidden"] = achieve.hidden_;
3145 cfg[
"hidden_name"] = achieve.hidden_name_;
3146 cfg[
"hidden_hint"] = achieve.hidden_hint_;
3147 cfg[
"achieved"] = achieve.achieved_;
3153 ERR_LUA <<
"Achievement " <<
id <<
" not found for achievement group " << content_for;
3159 ERR_LUA <<
"Achievement group " << content_for <<
" not found";
3183 if (game_display_) {
3184 game_display_->scroll_to_tile(loc, scroll, check_fogged);
3197 if(lua_isnoneornil(L, 1)) {
3202 if(!map().on_board(loc))
return luaL_argerror(L, 1,
"not on board");
3203 bool highlight =
true;
3204 if(!lua_isnoneornil(L, 2))
3207 play_controller_.get_mouse_handler_base().select_hex(
3208 loc,
false, highlight, fire_event);
3230 bool skipping = play_controller_.is_skipping_replay() || play_controller_.is_skipping_story();
3232 skipping = game_state_.events_manager_->pump().context_skip_messages();
3234 lua_pushboolean(L, skipping);
3245 if (!lua_isnone(L, 1)) {
3248 game_state_.events_manager_->pump().context_skip_messages(skip);
3257 int user_choice_index;
3258 int random_choice_index;
3259 int ai_choice_index;
3261 lua_synchronize(lua_State *l,
const std::string& descr,
int user_index,
int random_index = 0,
int ai_index = 0)
3263 , user_choice_index(user_index)
3264 , random_choice_index(random_index)
3265 , ai_choice_index(ai_index != 0 ? ai_index : user_index)
3269 virtual config query_user(
int side)
const override 3271 bool is_local_ai = lua_kernel_base::get_lua_kernel<game_lua_kernel>(L).board().get_team(side).is_local_ai();
3273 query_lua(side, is_local_ai ? ai_choice_index : user_choice_index, cfg);
3277 virtual config random_choice(
int side)
const override 3280 if(random_choice_index != 0 && lua_isfunction(L, random_choice_index)) {
3281 query_lua(side, random_choice_index, cfg);
3286 virtual std::string description()
const override 3291 void query_lua(
int side,
int function_index,
config& cfg)
const 3293 lua_pushvalue(L, function_index);
3294 lua_pushnumber(L, side);
3297 static const char*
msg =
"function returned to wesnoth.sync.[multi_]evaluate a table which was partially invalid";
3298 lua_kernel_base::get_lua_kernel<game_lua_kernel>(L).log_error(msg);
3299 lua_warning(L, msg,
false);
3306 virtual bool is_visible()
const override {
return false; }
3320 std::string tagname =
"input";
3327 if(!lua_isfunction(L, nextarg) &&
luaW_totstring(L, nextarg, desc) ) {
3330 if(lua_isfunction(L, nextarg)) {
3331 human_func = nextarg++;
3334 return luaL_argerror(L, nextarg,
"expected a function");
3336 if(lua_isfunction(L, nextarg)) {
3337 ai_func = nextarg++;
3339 side_for = lua_tointeger(L, nextarg);
3355 std::string tagname =
"input";
3359 std::vector<int> sides_for;
3362 if(!lua_isfunction(L, nextarg) &&
luaW_totstring(L, nextarg, desc) ) {
3365 if(lua_isfunction(L, nextarg)) {
3366 human_func = nextarg++;
3369 return luaL_argerror(L, nextarg,
"expected a function");
3371 if(lua_isfunction(L, nextarg)) {
3372 null_func = nextarg++;
3374 sides_for = lua_check<std::vector<int>>(L, nextarg++);
3389 lua_pushvalue(L, 1);
3404 std::set<map_location> res;
3406 const terrain_filter t_filter(filter, &fc,
false);
3408 t_filter.get_locations(res, *
luaW_tounit(L, 2),
true);
3410 t_filter.get_locations(res,
true);
3429 if (filter.
null()) {
3430 lua_pushboolean(L,
true);
3435 const terrain_filter t_filter(filter, &fc,
false);
3437 lua_pushboolean(L, t_filter.match(loc, *
luaW_tounit(L, 3)));
3439 lua_pushboolean(L, t_filter.match(loc));
3456 if (filter.
null()) {
3457 lua_pushboolean(L,
true);
3465 lua_pushboolean(L, s_filter.
match(*
t));
3467 unsigned side = luaL_checkinteger(L, 1) - 1;
3468 if (side >= teams().
size())
return 0;
3469 lua_pushboolean(L, s_filter.
match(side + 1));
3480 team_i = luaL_checkinteger(L, 1);
3482 std::string
flag = luaL_optlstring(L, 2,
"",
nullptr);
3483 std::string color = luaL_optlstring(L, 3,
"",
nullptr);
3485 if(flag.empty() && color.empty()) {
3488 if(team_i < 1 || static_cast<std::size_t>(team_i) > teams().size()) {
3489 return luaL_error(L,
"set_side_id: side number %d out of range", team_i);
3491 team& side = board().get_team(team_i);
3493 if(!color.empty()) {
3500 game_display_->reinit_flags_for_team(side);
3508 side_num =
t->side();
3510 side_num = luaL_checkinteger(L, 1);
3512 std::string
path = luaL_checkstring(L, 2);
3517 if(strcmp(action,
"delete") == 0) {
3522 std::size_t len = std::string::npos, open_brak = path.find_last_of(
'[');
3523 std::size_t dot = path.find_last_of(
'.');
3524 if(open_brak != len) {
3525 len = open_brak - dot - 1;
3527 cfg.add_child(path.substr(dot + 1, len), component);
3536 side_num =
t->side();
3538 side_num = luaL_checkinteger(L, 1);
3540 if(lua_isstring(L, 2)) {
3541 std::string file = luaL_checkstring(L, 2);
3543 std::string
err =
formatter() <<
"Could not load AI for side " << side_num <<
" from file " << file;
3544 lua_pushlstring(L, err.c_str(), err.length());
3545 return lua_error(L);
3557 side_num =
t->side();
3559 side_num = luaL_checkinteger(L, 1);
3563 cfg =
config {
"ai", cfg};
3565 bool added_dummy_stage =
false;
3567 added_dummy_stage =
true;
3571 if(added_dummy_stage) {
3573 if(iter->key ==
"stage" && iter->cfg[
"name"] ==
"empty") {
3574 iter = cfg.
erase(iter);
3584 unsigned i = luaL_checkinteger(L, 1);
3585 if(i < 1 || i > teams().
size())
return 0;
3597 LOG_LUA <<
"intf_get_sides called: this = " << std::hex <<
this << std::dec <<
" myname = " << my_name();
3598 std::vector<int> sides;
3612 lua_createtable(L, sides.size(), 0);
3614 for(
int side : sides) {
3616 lua_rawseti(L, -2, index);
3633 char const *m = luaL_checkstring(L, 2);
3635 if (sm ==
"advance") {
3639 if (sm !=
"advancement" && sm !=
"object" && sm !=
"trait") {
3640 return luaL_argerror(L, 2,
"unknown modification type");
3642 bool write_to_mods =
true;
3643 if (!lua_isnone(L, 4)) {
3647 write_to_mods =
false;
3665 std::string tag = luaL_optstring(L, 3,
"object");
3671 if(obj.matches(filter)) {
3672 obj[
"duration"] =
"now";
3691 if(lua_isboolean(L, 2)) {
3694 if(lua_isboolean(L, 3)) {
3708 char const *ty = luaL_checkstring(L, 1);
3711 std::stringstream ss;
3712 ss <<
"unknown unit type: '" << ty <<
"'";
3713 return luaL_argerror(L, 1, ss.str().c_str());
3730 std::string team_name;
3733 std::vector<std::string> team_names;
3734 std::transform(teams.begin(), teams.end(), std::back_inserter(team_names),
3735 [&](
int team) {
return game_state_.get_disp_context().get_team(
team).team_name(); });
3738 team_name = cfg[
"team_name"].str();
3741 if (game_display_) {
3742 game_display_->add_overlay(loc, cfg[
"image"], cfg[
"halo"],
3743 team_name, cfg[
"name"], cfg[
"visible_in_fog"].to_bool(
true),
3744 cfg[
"submerge"].to_double(0), cfg[
"z_order"].to_double(0));
3757 char const *m = lua_tostring(L, 2);
3760 if (game_display_) {
3761 game_display_->remove_single_overlay(loc, m);
3764 if (game_display_) {
3765 game_display_->remove_overlay(loc);
3774 const int nargs = lua_gettop(L);
3775 if(nargs < 2 || nargs > 3) {
3776 return luaL_error(L,
"Wrong number of arguments to ai.log_replay() - should be 2 or 3 arguments.");
3778 const std::string key = nargs == 2 ? luaL_checkstring(L, 1) : luaL_checkstring(L, 2);
3783 recorder.
add_log_data(luaL_checkstring(L, 1), key, cfg);
3784 }
else if(!lua_isstring(L, 3)) {
3785 return luaL_argerror(L, 3,
"accepts only string or config");
3787 recorder.
add_log_data(luaL_checkstring(L, 1), key, luaL_checkstring(L, 3));
3801 return lk.run_wml_event(ref_, args_, event_info, &result) && result;
3805 lk.clear_wml_event(ref_);
3808 cfg.
add_child(
"filter_lua")[
"code"] =
"<function>";
3818 if(lua_isstring(L, idx)) {
3819 return lua_tostring(L, idx);
3838 using namespace std::literals;
3843 }
else if(is_menu_item) {
3845 return luaL_argerror(L, 1,
"non-empty id is required for a menu item");
3847 name =
"menu item " +
id;
3849 if(
id.empty() && name.empty()) {
3850 return luaL_argerror(L, 1,
"either a name or id is required");
3853 if(new_handler.valid()) {
3854 bool has_lua_filter =
false;
3858 int filterIdx = lua_gettop(L);
3861 if(lua_isfunction(L, filterIdx)) {
3862 int fcnIdx = lua_absindex(L, -1);
3863 new_handler->add_filter(std::make_unique<lua_event_filter>(*
this, fcnIdx,
luaW_table_get_def(L, 1,
"filter_args",
config())));
3864 has_lua_filter =
true;
3866 #define READ_ONE_FILTER(key) \ 3868 if(luaW_tableget(L, filterIdx, key)) { \ 3869 if(lua_isstring(L, -1)) { \ 3870 filters.add_child("insert_tag", config{ \ 3871 "name", "filter_" key, \ 3872 "variable", luaL_checkstring(L, -1) \ 3875 filters.add_child("filter_" key, luaW_checkconfig(L, -1)); \ 3885 #undef READ_ONE_FILTER 3887 filters[
"filter_formula"] = luaL_checkstring(L, -1);
3891 new_handler->read_filters(filters);
3895 new_handler->set_event_ref(save_wml_event(-1), has_preloaded_);
3897 if(has_lua_filter) {
3900 new_handler->set_event_ref(LUA_NOREF, has_preloaded_);
3902 new_handler->register_wml_event(*
this);
3912 template<
bool is_menu_item>
3919 return luaL_argerror(L, 1,
"must not be empty");
3923 name =
"menu item " + name;
3926 if(new_handler.valid()) {
3929 new_handler->set_event_ref(save_wml_event(2), has_preloaded_);
3941 bool delayed_variable_substitution = cfg[
"delayed_variable_substitution"].to_bool(
true);
3942 if(delayed_variable_substitution) {
3952 game_state_.events_manager_->remove_event_handler(luaL_checkstring(L, 1));
3958 if (game_display_) {
3959 game_display_->adjust_color_overlay(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2), luaL_checkinteger(L, 3));
3960 game_display_->invalidate_all();
3968 auto color = game_display_->get_color_overlay();
3969 lua_pushinteger(L, color.r);
3970 lua_pushinteger(L, color.g);
3971 lua_pushinteger(L, color.b);
3980 auto vec = lua_check<std::vector<uint8_t>>(L, 1);
3981 if(vec.size() != 4) {
3984 color_t fade{vec[0], vec[1], vec[2], vec[3]};
3985 game_display_->fade_to(fade, luaL_checkinteger(L, 2));
4002 lua_Integer delay = luaL_checkinteger(L, 1);
4004 play_controller_.play_slice(
false);
4007 if(
luaW_toboolean(L, 2) && game_display_ && game_display_->turbo_speed() > 0) {
4008 delay /= game_display_->turbo_speed();
4010 const unsigned final = SDL_GetTicks() + delay;
4012 play_controller_.play_slice(
false);
4014 }
while (static_cast<int>(
final - SDL_GetTicks()) > 0);
4021 if (game_display_) {
4036 if (game_display_) {
4038 std::string team_name;
4041 if(lua_gettop(L) == 1 && lua_istable(L, 1)) {
4042 using namespace std::literals;
4045 team_name = luaL_optstring(L, 2,
"");
4048 game_display_->labels().set_label(loc,
"", -1, team_name);
4059 switch(lua_type(L, 2)) {
4061 case LUA_TNONE:
case LUA_TNIL:
4066 if(
size_t n = luaL_checkinteger(L, 2);
n > 0 &&
n <= teams().size()) {
4091 if (game_display_) {
4106 for (
const int side : filter.
get_teams()){
4128 int side = cfg[
"side"];
4138 lua_getfield(L, -1,
"ca_ptr");
4152 lua_getfield(L, -1,
"stg_ptr");
4161 lua_createtable(L, 0, 0);
4163 lua_pushstring(L,
"name");
4164 lua_pushstring(L, c->
get_name().c_str());
4167 lua_pushstring(L,
"engine");
4171 lua_pushstring(L,
"id");
4172 lua_pushstring(L, c->
get_id().c_str());
4175 if (ct ==
"candidate_action") {
4176 lua_pushstring(L,
"ca_ptr");
4177 lua_pushlightuserdata(L, c);
4180 lua_pushstring(L,
"exec");
4185 if (ct ==
"stage") {
4186 lua_pushstring(L,
"stg_ptr");
4187 lua_pushlightuserdata(L, c);
4190 lua_pushstring(L,
"exec");
4198 for (std::vector<std::string>::const_iterator
t = c_types.begin();
t != c_types.end(); ++
t)
4201 std::string
type = *
t;
4202 if (type ==
"aspect" || type ==
"goal" || type ==
"engine")
4207 lua_pushstring(L, type.c_str());
4208 lua_createtable(L, 0, 0);
4210 for (std::vector<ai::component*>::const_iterator
i = children.begin();
i != children.end(); ++
i)
4212 lua_pushstring(L, (*i)->get_name().c_str());
4243 side = luaL_checkinteger(L, 1);
4250 std::vector<ai::component*> engines = c->
get_children(
"engine");
4252 for (std::vector<ai::component*>::const_iterator
i = engines.begin();
i != engines.end(); ++
i)
4254 if ((*i)->get_name() ==
"lua")
4264 if (lua_engine ==
nullptr)
4277 LOG_LUA <<
"Created new dummy lua-engine for debug_ai().";
4287 lua_pushstring(L,
"components");
4306 gamedata().set_allow_end_turn(allow, reason);
4313 if(lua_isboolean(L, 1)) {
4314 play_controller_.pump().set_undo_disabled(!
luaW_toboolean(L, 1));
4317 play_controller_.pump().set_undo_disabled(
false);
4324 play_controller_.pump().set_action_canceled();
4338 std::set<map_location> locs;
4341 if(lua_gettop(L) == 1) {
4344 id = cfg[
"id"].str();
4345 const terrain_filter filter(cfg, &game_state_,
false);
4346 filter.get_locations(locs,
true);
4349 id = luaL_checkstring(L, 1);
4350 if(!lua_isnoneornil(L, 3))
4355 const terrain_filter filter(cfg, &game_state_,
false);
4356 filter.get_locations(locs,
true);
4363 tod_man().add_time_area(
id, locs, times);
4364 LOG_LUA <<
"Lua inserted time_area '" <<
id <<
"'";
4373 const char *
id = luaL_checkstring(L, 1);
4374 tod_man().remove_time_area(
id);
4375 LOG_LUA <<
"Lua removed time_area '" <<
id <<
"'";
4384 int area_index = tod_man().get_area_on_hex(loc).first;
4385 if(area_index < 0) {
4389 luaW_push_schedule(L, area_index);
4392 std::string area_id = luaL_checkstring(L, 1);
4393 const auto& area_ids = tod_man().get_area_ids();
4394 if(
auto iter = std::find(area_ids.begin(), area_ids.end(), area_id); iter == area_ids.end()) {
4398 luaW_push_schedule(L, std::distance(area_ids.begin(), iter));
4408 if(luaL_testudata(L, 1,
"schedule")) {
4413 if(area >= 0) tod_man().replace_schedule(tod_man().times(area));
4418 ERR_LUA <<
"attempted to to replace ToD schedule with empty schedule";
4421 if (game_display_) {
4422 game_display_->new_turn();
4424 LOG_LUA <<
"replaced ToD schedule";
4432 int x = luaL_checkinteger(L, 1), y = luaL_checkinteger(L, 2);
4434 if (game_display_) {
4435 game_display_->scroll(x, y,
true);
4446 lua_report_generator(lua_State *L,
const std::string &
n)
4447 : mState(L), name(n) {}
4453 lua_State *L = mState;
4455 if (!
luaW_getglobal(L,
"wesnoth",
"interface",
"game_display", name))
4469 reports::context temp_context =
reports::context(board(), *game_display_, tod_man(), play_controller_.get_whiteboard(), play_controller_.get_mouse_handler_base());
4470 luaW_pushconfig(L, reports_.generate_report(m.c_str(), temp_context ,
true));
4479 char const *m = luaL_checkstring(L, 2);
4481 lua_pushvalue(L, 2);
4482 lua_pushvalue(L, -2);
4484 reports_.register_generator(m,
new lua_report_generator(L, m));
4493 char const *m = luaL_checkstring(L, 2);
4494 lua_pushvalue(L, 2);
4495 lua_pushvalue(L, 3);
4497 reports_.register_generator(m,
new lua_report_generator(L, m));
4527 if (dst == u->get_location() || !map().on_board(dst)) {
4531 if (!map().on_board(vacant_dst)) {
4536 if ( clear_shroud ) {
4542 std::vector<map_location> teleport_path;
4543 teleport_path.push_back(src_loc);
4544 teleport_path.push_back(vacant_dst);
4547 units().move(src_loc, vacant_dst);
4550 u = units().find(vacant_dst).get_shared_ptr();
4551 u->anim_comp().set_standing();
4553 if ( clear_shroud ) {
4558 if (map().is_village(vacant_dst)) {
4562 game_display_->invalidate_unit_after_move(src_loc, vacant_dst);
4577 const std::string& logger = lua_isstring(L, 2) ? luaL_checkstring(L, 1) :
"";
4578 const std::string&
msg = lua_isstring(L, 2) ? luaL_checkstring(L, 2) : luaL_checkstring(L, 1);
4580 if(logger ==
"wml" || logger ==
"WML") {
4585 game_state_.events_manager_->pump().put_wml_message(logger,msg,in_chat);
4612 bool affect_normal_fog =
false;
4613 if(lua_isboolean(L, -1)) {
4616 std::set<int> sides;
4618 sides.insert(
t->side());
4619 }
else if(lua_isnumber(L, 1)) {
4620 sides.insert(lua_tointeger(L, 1));
4621 }
else if(lua_istable(L, 1) && lua_istable(L, 2)) {
4622 const auto& v = lua_check<std::vector<int>>(L, 1);
4623 sides.insert(v.begin(), v.end());
4625 for(
const team&
t : teams()) {
4626 sides.insert(
t.side()+1);
4631 for(
const int &side_num : sides) {
4632 if(side_num < 1 || static_cast<std::size_t>(side_num) > teams().
size()) {
4635 team &
t = board().get_team(side_num);
4639 if(affect_normal_fog) {
4642 }
else if(!affect_normal_fog) {
4654 game_display_->recalculate_minimap();
4655 game_display_->invalidate_all();
4662 const std::string name = luaL_checkstring(L, 1);
4667 if(!
luaW_getglobal(L,
"wesnoth",
"custom_synced_commands", name)) {
4668 return luaL_argerror(L, 1,
"Unknown synced command");
4671 cmd_tag[
"name"] = name;
4672 if(!lua_isnoneornil(L, 2)) {
4689 return game_state_.board_;
4693 return game_state_.board_.units();
4697 return game_state_.board_.teams();
4701 return game_state_.board_.map();
4705 return game_state_.gamedata_;
4709 return game_state_.tod_manager_;
4713 return *queued_events_.top();
4719 , game_display_(nullptr)
4721 , play_controller_(pc)
4722 , reports_(reports_object)
4724 , EVENT_TABLE(LUA_NOREF)
4733 cmd_log_ <<
"Registering game-specific wesnoth lib functions...\n";
4736 static luaL_Reg
const callbacks[] {
4741 {
"allow_undo", &dispatch<&game_lua_kernel::intf_allow_undo > },
4742 {
"cancel_action", &dispatch<&game_lua_kernel::intf_cancel_action > },
4743 {
"log_replay", &dispatch<&game_lua_kernel::intf_log_replay > },
4744 {
"log", &dispatch<&game_lua_kernel::intf_log > },
4745 {
"redraw", &dispatch<&game_lua_kernel::intf_redraw > },
4746 {
"simulate_combat", &dispatch<&game_lua_kernel::intf_simulate_combat > },
4747 {
nullptr,
nullptr }
4748 };lua_getglobal(L,
"wesnoth");
4749 if (!lua_istable(L,-1)) {
4752 luaL_setfuncs(L, callbacks, 0);
4754 lua_setglobal(L,
"wesnoth");
4756 lua_getglobal(L,
"gui");
4757 lua_pushcfunction(L, &dispatch<&game_lua_kernel::intf_gamestate_inspector>);
4758 lua_setfield(L, -2,
"show_inspector");
4764 static luaL_Reg
const test_callbacks[] {
4765 {
"fire_wml_menu_item", &dispatch<&game_lua_kernel::intf_fire_wml_menu_item> },
4766 {
nullptr,
nullptr }
4768 luaL_setfuncs(L, test_callbacks, 0);
4769 lua_setglobal(L,
"unit_test");
4795 cmd_log_ <<
"Adding terrain_types table...\n";
4796 lua_getglobal(L,
"wesnoth");
4797 lua_newuserdatauv(L, 0, 0);
4798 lua_createtable(L, 0, 2);
4799 lua_pushcfunction(L, &dispatch<&game_lua_kernel::impl_get_terrain_info>);
4800 lua_setfield(L, -2,
"__index");
4801 lua_pushstring(L,
"terrain types");
4802 lua_setfield(L, -2,
"__metatable");
4803 lua_setmetatable(L, -2);
4804 lua_setfield(L, -2,
"terrain_types");
4808 cmd_log_ <<
"Adding ai elements table...\n";
4813 cmd_log_ <<
"Adding wesnoth current table...\n";
4815 lua_getglobal(L,
"wesnoth");
4816 lua_newuserdatauv(L, 0, 0);
4817 lua_createtable(L, 0, 2);
4818 lua_pushcfunction(L, &dispatch<&game_lua_kernel::impl_current_get>);
4819 lua_setfield(L, -2,
"__index");
4820 lua_pushstring(L,
"current config");
4821 lua_setfield(L, -2,
"__metatable");
4822 lua_setmetatable(L, -2);
4823 lua_setfield(L, -2,
"current");
4827 lua_getglobal(L,
"wml");
4828 static luaL_Reg
const wml_callbacks[] {
4832 {
"get_variable", &dispatch<&game_lua_kernel::intf_get_variable>},
4833 {
"set_variable", &dispatch<&game_lua_kernel::intf_set_variable>},
4834 {
"get_all_vars", &dispatch<&game_lua_kernel::intf_get_all_vars>},
4835 {
nullptr,
nullptr }
4837 luaL_setfuncs(L, wml_callbacks, 0);
4842 static luaL_Reg
const map_callbacks[] {
4849 {
"get_owner", &dispatch<&game_lua_kernel::intf_get_village_owner>},
4850 {
"set_owner", &dispatch<&game_lua_kernel::intf_set_village_owner>},
4852 {
"add_label", &dispatch<&game_lua_kernel::intf_add_label>},
4853 {
"remove_label", &dispatch<&game_lua_kernel::intf_remove_label>},
4854 {
"get_label", &dispatch<&game_lua_kernel::intf_get_label>},
4856 {
"place_area", &dispatch<&game_lua_kernel::intf_add_time_area>},
4857 {
"remove_area", &dispatch<&game_lua_kernel::intf_remove_time_area>},
4858 {
"get_area", &dispatch<&game_lua_kernel::intf_get_time_area>},
4860 {
"find", &dispatch<&game_lua_kernel::intf_get_locations>},
4861 {
"matches", &dispatch<&game_lua_kernel::intf_match_location>},
4863 {
nullptr,
nullptr }
4865 luaL_setfuncs(L, map_callbacks, 0);
4869 cmd_log_ <<
"Adding units module...\n";
4870 static luaL_Reg
const unit_callbacks[] {
4873 {
"erase", &dispatch<&game_lua_kernel::intf_erase_unit>},
4874 {
"extract", &dispatch<&game_lua_kernel::intf_extract_unit>},
4875 {
"matches", &dispatch<&game_lua_kernel::intf_match_unit>},
4876 {
"select", &dispatch<&game_lua_kernel::intf_select_unit>},
4877 {
"to_map", &dispatch<&game_lua_kernel::intf_put_unit>},
4878 {
"to_recall", &dispatch<&game_lua_kernel::intf_put_recall_unit>},
4880 {
"teleport", &dispatch<&game_lua_kernel::intf_teleport>},
4882 {
"ability", &dispatch<&game_lua_kernel::intf_unit_ability>},
4893 {
"find_on_map", &dispatch<&game_lua_kernel::intf_get_units>},
4894 {
"find_on_recall", &dispatch<&game_lua_kernel::intf_get_recall_units>},
4895 {
"get", &dispatch<&game_lua_kernel::intf_get_unit>},
4896 {
"get_hovered", &dispatch<&game_lua_kernel::intf_get_displayed_unit>},
4897 {
"create_animator", &dispatch<&game_lua_kernel::intf_create_animator>},
4900 {
nullptr,
nullptr }
4902 lua_getglobal(L,
"wesnoth");
4904 luaL_setfuncs(L, unit_callbacks, 0);
4905 lua_setfield(L, -2,
"units");
4909 cmd_log_ <<
"Adding sides module...\n";
4910 static luaL_Reg
const side_callbacks[] {
4911 {
"is_enemy", &dispatch<&game_lua_kernel::intf_is_enemy> },
4912 {
"matches", &dispatch<&game_lua_kernel::intf_match_side> },
4913 {
"set_id", &dispatch<&game_lua_kernel::intf_set_side_id> },
4918 {
"find", &dispatch<&game_lua_kernel::intf_get_sides> },
4919 {
"get", &dispatch<&game_lua_kernel::intf_get_side> },
4920 {
"create", &dispatch<&game_lua_kernel::intf_create_side> },
4922 {
"place_shroud", &dispatch2<&game_lua_kernel::intf_toggle_shroud, true>},
4923 {
"remove_shroud", &dispatch2<&game_lua_kernel::intf_toggle_shroud, false>},
4924 {
"override_shroud", &dispatch<&game_lua_kernel::intf_override_shroud>},
4925 {
"is_shrouded", &dispatch2<&game_lua_kernel::intf_get_fog_or_shroud, false>},
4927 {
"place_fog", &dispatch2<&game_lua_kernel::intf_toggle_fog, false>},
4928 {
"remove_fog", &dispatch2<&game_lua_kernel::intf_toggle_fog, true>},
4929 {
"is_fogged", &dispatch2<&game_lua_kernel::intf_get_fog_or_shroud, true>},
4930 {
nullptr,
nullptr }
4932 std::vector<lua_cpp::Reg>
const cpp_side_callbacks {
4933 {
"add_ai_component", std::bind(
intf_modify_ai, std::placeholders::_1,
"add")},
4934 {
"delete_ai_component", std::bind(
intf_modify_ai, std::placeholders::_1,
"delete")},
4935 {
"change_ai_component", std::bind(
intf_modify_ai, std::placeholders::_1,
"change")},
4939 lua_getglobal(L,
"wesnoth");
4941 luaL_setfuncs(L, side_callbacks, 0);
4943 lua_setfield(L, -2,
"sides");
4947 cmd_log_ <<
"Adding interface module...\n";
4948 static luaL_Reg
const intf_callbacks[] {
4949 {
"add_hex_overlay", &dispatch<&game_lua_kernel::intf_add_tile_overlay>},
4950 {
"remove_hex_overlay", &dispatch<&game_lua_kernel::intf_remove_tile_overlay>},
4951 {
"get_color_adjust", &dispatch<&game_lua_kernel::intf_get_color_adjust>},
4952 {
"color_adjust", &dispatch<&game_lua_kernel::intf_color_adjust>},
4953 {
"screen_fade", &dispatch<&game_lua_kernel::intf_screen_fade>},
4954 {
"delay", &dispatch<&game_lua_kernel::intf_delay>},
4955 {
"deselect_hex", &dispatch<&game_lua_kernel::intf_deselect_hex>},
4956 {
"highlight_hex", &dispatch<&game_lua_kernel::intf_highlight_hex>},
4957 {
"float_label", &dispatch<&game_lua_kernel::intf_float_label>},
4958 {
"get_displayed_unit", &dispatch<&game_lua_kernel::intf_get_displayed_unit>},
4959 {
"get_hovered_hex", &dispatch<&game_lua_kernel::intf_get_mouseover_tile>},
4960 {
"get_selected_hex", &dispatch<&game_lua_kernel::intf_get_selected_tile>},
4961 {
"lock", &dispatch<&game_lua_kernel::intf_lock_view>},
4962 {
"is_locked", &dispatch<&game_lua_kernel::intf_view_locked>},
4963 {
"scroll", &dispatch<&game_lua_kernel::intf_scroll>},
4964 {
"scroll_to_hex", &dispatch<&game_lua_kernel::intf_scroll_to_tile>},
4965 {
"skip_messages", &dispatch<&game_lua_kernel::intf_skip_messages>},
4966 {
"is_skipping_messages", &dispatch<&game_lua_kernel::intf_is_skipping_messages>},
4967 {
"zoom", &dispatch<&game_lua_kernel::intf_zoom>},
4968 {
"clear_menu_item", &dispatch<&game_lua_kernel::intf_clear_menu_item>},
4969 {
"set_menu_item", &dispatch<&game_lua_kernel::intf_set_menu_item>},
4970 {
"allow_end_turn", &dispatch<&game_lua_kernel::intf_allow_end_turn>},
4971 {
"clear_chat_messages", &dispatch<&game_lua_kernel::intf_clear_messages>},
4972 {
"end_turn", &dispatch<&game_lua_kernel::intf_end_turn>},
4974 {
"add_chat_message", &dispatch<&game_lua_kernel::intf_message>},
4975 {
"add_overlay_text", &dispatch2<&game_lua_kernel::intf_set_floating_label, true>},
4977 {
nullptr,
nullptr }
4979 lua_getglobal(L,
"wesnoth");
4981 luaL_setfuncs(L, intf_callbacks, 0);
4982 lua_setfield(L, -2,
"interface");
4986 cmd_log_ <<
"Adding achievements module...\n";
4987 static luaL_Reg
const achievement_callbacks[] {
4988 {
"set", &dispatch<&game_lua_kernel::intf_set_achievement> },
4989 {
"has", &dispatch<&game_lua_kernel::intf_has_achievement> },
4990 {
"get", &dispatch<&game_lua_kernel::intf_get_achievement> },
4991 {
nullptr,
nullptr }
4993 lua_getglobal(L,
"wesnoth");
4995 luaL_setfuncs(L, achievement_callbacks, 0);
4996 lua_setfield(L, -2,
"achievements");
5000 cmd_log_ <<
"Adding audio module...\n";
5001 static luaL_Reg
const audio_callbacks[] {
5002 {
"play", &dispatch<&game_lua_kernel::intf_play_sound > },
5003 {
nullptr,
nullptr }
5005 lua_getglobal(L,
"wesnoth");
5007 luaL_setfuncs(L, audio_callbacks, 0);
5008 lua_setfield(L, -2,
"audio");
5012 cmd_log_ <<
"Adding paths module...\n";
5013 static luaL_Reg
const path_callbacks[] {
5014 {
"find_cost_map", &dispatch<&game_lua_kernel::intf_find_cost_map > },
5015 {
"find_path", &dispatch<&game_lua_kernel::intf_find_path > },
5016 {
"find_reach", &dispatch<&game_lua_kernel::intf_find_reach > },
5017 {
"find_vacant_hex", &dispatch<&game_lua_kernel::intf_find_vacant_tile > },
5018 {
"find_vision_range", &dispatch<&game_lua_kernel::intf_find_vision_range > },
5019 {
nullptr,
nullptr }
5021 lua_getglobal(L,
"wesnoth");
5023 luaL_setfuncs(L, path_callbacks, 0);
5024 lua_setfield(L, -2,
"paths");
5028 cmd_log_ <<
"Adding sync module...\n";
5029 static luaL_Reg
const sync_callbacks[] {
5034 {
nullptr,
nullptr }
5036 lua_getglobal(L,
"wesnoth");
5038 luaL_setfuncs(L, sync_callbacks, 0);
5039 lua_setfield(L, -2,
"sync");
5043 cmd_log_ <<
"Adding schedule module...\n";
5044 static luaL_Reg
const schedule_callbacks[] {
5045 {
"get_time_of_day", &dispatch<&game_lua_kernel::intf_get_time_of_day<false>>},
5046 {
"get_illumination", &dispatch<&game_lua_kernel::intf_get_time_of_day<true>>},
5047 {
"replace", &dispatch<&game_lua_kernel::intf_replace_schedule>},
5048 {
nullptr,
nullptr }
5050 lua_getglobal(L,
"wesnoth");
5052 luaL_setfuncs(L, schedule_callbacks, 0);
5053 lua_createtable(L, 0, 2);
5054 lua_setmetatable(L, -2);
5055 lua_setfield(L, -2,
"schedule");
5062 cmd_log_ <<
"Adding wml_actions table...\n";
5064 lua_getglobal(L,
"wesnoth");
5066 lua_setfield(L, -2,
"wml_actions");
5070 cmd_log_ <<
"Adding wml_conditionals table...\n";
5072 lua_getglobal(L,
"wesnoth");
5074 lua_setfield(L, -2,
"wml_conditionals");
5081 cmd_log_ <<
"Adding effects table...\n";
5083 lua_getglobal(L,
"wesnoth");
5085 lua_setfield(L, -2,
"effects");
5089 cmd_log_ <<
"Adding custom_synced_commands table...\n";
5091 lua_getglobal(L,
"wesnoth");
5093 lua_setfield(L, -2,
"custom_synced_commands");
5097 cmd_log_ <<
"Adding game_events module...\n";
5098 static luaL_Reg
const event_callbacks[] {
5099 {
"add", &dispatch<&game_lua_kernel::intf_add_event> },
5100 {
"add_repeating", &dispatch<&game_lua_kernel::intf_add_event_simple<false>> },
5101 {
"add_menu", &dispatch<&game_lua_kernel::intf_add_event_simple<true>> },
5102 {
"add_wml", &dispatch<&game_lua_kernel::intf_add_event_wml> },
5103 {
"remove", &dispatch<&game_lua_kernel::intf_remove_event> },
5104 {
"fire", &dispatch2<&game_lua_kernel::intf_fire_event, false> },
5105 {
"fire_by_id", &dispatch2<&game_lua_kernel::intf_fire_event, true> },
5106 {
nullptr,
nullptr }
5108 lua_getglobal(L,
"wesnoth");
5110 luaL_setfuncs(L, event_callbacks, 0);
5111 lua_setfield(L, -2,
"game_events");
5115 cmd_log_ <<
"Adding game_display table...\n";
5119 lua_createtable(L, 0, 2);
5120 lua_pushcfunction(L, &dispatch<&game_lua_kernel::impl_theme_items_get>);
5121 lua_setfield(L, -2,
"__index");
5122 lua_pushcfunction(L, &dispatch<&game_lua_kernel::impl_theme_items_set>);
5123 lua_setfield(L, -2,
"__newindex");
5124 lua_setmetatable(L, -2);
5125 lua_setfield(L, -2,
"game_display");
5129 cmd_log_ <<
"Adding scenario table...\n";
5133 lua_createtable(L, 0, 2);
5134 lua_pushcfunction(L, &dispatch<&game_lua_kernel::impl_scenario_get>);
5135 lua_setfield(L, -2,
"__index");
5136 lua_pushcfunction(L, &dispatch<&game_lua_kernel::impl_scenario_set>);
5137 lua_setfield(L, -2,
"__newindex");
5138 lua_setmetatable(L, -2);
5139 lua_setfield(L, -2,
"scenario");
5150 lua_pushstring(L, effect.c_str());
5168 cmd_log_ <<
"Adding races table...\n";
5171 lua_getglobal(L,
"wesnoth");
5173 lua_setfield(L, -2,
"races");
5177 cmd_log_ <<
"Running preload scripts...\n";
5180 for (
const config &cfg : game_lua_kernel::preload_scripts) {
5200 using namespace std::literals::string_view_literals;
5201 static const std::array handled_file_tags {
5214 "modify_unit_type"sv,
5220 "terrain_graphics"sv,
5228 return std::binary_search(handled_file_tags.begin(), handled_file_tags.end(),
s);
5247 lua_createtable(L, 2, 0);
5248 lua_pushstring(L, v.key.c_str());
5249 lua_rawseti(L, -2, 1);
5251 lua_rawseti(L, -2, 2);
5252 lua_rawseti(L, -2, k++);
5287 const std::string m =
"Tag is already used: [" + i->key +
"]";
5308 lua_pushstring(L, ev.
name.c_str());
5317 if (!
luaW_getglobal(L,
"wesnoth",
"custom_synced_commands", name)) {
5333 std::string which_effect = lua_tostring(L, lua_upvalueindex(1));
5348 lua_pushstring(L, description.c_str());
5361 int str_i = lua_gettop(L);
5364 lua_pushstring(L,
"__call");
5365 lua_pushvalue(L, str_i);
5366 lua_pushboolean(L,
true);
5367 lua_pushcclosure(L, &dispatch<&game_lua_kernel::cfun_builtin_effect>, 2);
5369 lua_pushstring(L,
"__descr");
5370 lua_pushvalue(L, str_i);
5371 lua_pushboolean(L,
false);
5372 lua_pushcclosure(L, &dispatch<&game_lua_kernel::cfun_builtin_effect>, 2);
5374 lua_setmetatable(L, -2);
5384 (lua_touserdata(L, lua_upvalueindex(1)));
5398 lua_getglobal(L,
"wesnoth");
5399 lua_pushstring(L,
"wml_actions");
5401 lua_pushstring(L, cmd.c_str());
5402 lua_pushlightuserdata(L, reinterpret_cast<void *>(h));
5403 lua_pushcclosure(L, &dispatch<&game_lua_kernel::cfun_wml_action>, 1);
5416 (lua_touserdata(L, lua_upvalueindex(1)));
5419 lua_pushboolean(L,
h(vcfg));
5430 lua_getglobal(L,
"wesnoth");
5431 lua_pushstring(L,
"wml_conditionals");
5433 lua_pushstring(L, cmd.c_str());
5434 lua_pushlightuserdata(L, reinterpret_cast<void *>(h));
5476 ERR_WML <<
"unknown conditional wml: [" << cmd <<
"]";
5495 int argIdx = lua_gettop(L);
5497 return luaL_error(L,
"wesnoth.wml_actions.command is missing");
5499 lua_pushvalue(L, argIdx);
5508 int evtIdx = lua_gettop(L);
5513 return luaL_ref(L, evtIdx);
5520 int evtIdx = lua_gettop(L);
5524 std::ostringstream lua_name;
5525 lua_name <<
"event ";
5527 lua_name <<
"<anon>";
5532 lua_name <<
"[id=" <<
id <<
"]";
5535 ERR_LUA <<
"Failed to register WML event: " << lua_name.str();
5538 return luaL_ref(L, evtIdx);
5544 idx = lua_absindex(L, idx);
5546 int evtIdx = lua_gettop(L);
5550 lua_pushvalue(L, idx);
5551 return luaL_ref(L, evtIdx);
5558 luaL_unref(L, -1, ref);
5569 lua_geti(L, -1, ref);
5570 if(lua_isnil(L, -1))
return false;
5616 lua_pushvalue(L, -1);
5636 std::string message = std::string() +
"function " + name +
" not found";
5637 log_error(message.c_str(),
"Lua SUF Error");
5641 lua_insert(L, -nArgs - 1);
5653 int top = lua_gettop(L);
5664 if(lua_istable(L, -1)) {
5667 lua_pushvalue(L, -1);
5669 lua_pushvalue(L, top + 1);
5671 lua_pushvalue(L, top + 2);
5676 if(luaL_getmetafield(L, -1,
"__descr")) {
5678 if(lua_isstring(L, -1)) {
5680 descr = lua_tostring(L, -1);
5682 lua_pushvalue(L, -2);
5684 lua_pushvalue(L, top + 1);
5686 lua_pushvalue(L, top + 2);
5689 if(lua_isstring(L, -1) && !lua_isnumber(L, -1)) {
5690 descr = lua_tostring(L, -1);
5692 ERR_LUA <<
"Effect __descr metafunction should have returned a string, but instead returned ";
5693 if(lua_isnone(L, -1)) {
5696 ERR_LUA << lua_typename(L, lua_type(L, -1));
5701 }
else if(need_apply) {
5703 lua_pushvalue(L, top + 1);
5705 lua_pushvalue(L, top + 2);
5730 if (!
luaW_getglobal(L,
"wesnoth",
"game_events",
"on_mouse_move")) {
5743 if (!
luaW_getglobal(L,
"wesnoth",
"game_events",
"on_mouse_action")) {
const_attack_ptr weapon
The weapon used by the unit to attack the opponent, or nullptr if there is none.
std::decay_t< T > lua_check(lua_State *L, int n)
bool luaW_checkvariable(lua_State *L, variable_access_create &v, int n)
int dispatch(lua_State *L)
#define modify_bool_attrib(name, accessor)
bool luaW_tableget(lua_State *L, int index, const char *key)
play_controller * controller
static int intf_transform_unit(lua_State *L)
Changes a unit to the given unit type.
void wait_for_end() const
unsigned int end_text_duration
for how long the end-of-campaign text is shown
bool empty() const
Tests for an attribute that either was never set or was set to "".
static int impl_end_level_data_get(lua_State *L)
config get_user_choice(const std::string &name, const user_choice &uch, int side=0)
std::stack< game_events::queued_event const *> queued_events_
void luaW_push_schedule(lua_State *L, int area_index)
static synced_state get_synced_state()
static int intf_advance_unit(lua_State *L)
Advances a unit if the unit has enough xp.
static int intf_get_era(lua_State *L)
Gets a table for an era tag.
virtual std::string get_id() const =0
std::decay_t< T > luaW_table_get_def(lua_State *L, int index, std::string_view k, const T &def)
returns t[k] where k is the table at index index and k is k or def if it is not convertible to the co...
int map_locked_
A value != 0 means that the shouldn't remove any units from the map, usually because we are currently...
std::string apply_effect(const std::string &name, unit &u, const config &cfg, bool need_apply)
config & child(config_key_type key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
lua_unit * luaW_pushunit(lua_State *L, Args... args)
bool end_credits
whether to show the standard credits at the end
double untouched
Resulting chance we were not hit by this opponent (important if it poisons)
std::vector< double > hp_dist
Resulting probability distribution (might be not as large as max_hp)
std::string join_map(const T &v, const std::string &major=",", const std::string &minor=":")
static display * get_singleton()
Returns the display object if a display object exists.
int intf_allow_end_turn(lua_State *)
Allow undo sets the flag saying whether the event has mutated the game to false.
const_all_children_itors all_children_range() const
In-order iteration over all children.
const unit_type * find(const std::string &key, unit_type::BUILD_STATUS status=unit_type::FULL) const
Finds a unit_type by its id() and makes sure it is built to the specified level.
void remove_floating_label(int handle, int fadeout)
removes the floating label given by 'handle' from the screen
int intf_find_path(lua_State *L)
Finds a path between two locations.
const terrain_code NONE_TERRAIN
bool luaW_tovconfig(lua_State *L, int index, vconfig &vcfg)
Gets an optional vconfig from either a table or a userdata.
std::string register_metatables(lua_State *L)
vconfig child(const std::string &key) const
Returns a child of *this whose key is key.
std::string image_mask
The image that is to be laid over all images while this time of day lasts.
#define return_tstring_attrib(name, accessor)
const t_string & description() const
game_display * game_display_
void luaW_pushconfig(lua_State *L, const config &cfg)
Converts a config object to a Lua table pushed at the top of the stack.
int intf_fire_event(lua_State *L, const bool by_id)
Fires an event.
team & luaW_checkteam(lua_State *L, int idx)
Test if the top stack element is a team, and if not, error.
int intf_get_unit(lua_State *)
Gets the unit at the given location or with the given id.
bool luaW_pcall(lua_State *L, int nArgs, int nRets, bool allow_wml_error)
Calls a Lua function stored below its nArgs arguments at the top of the stack.
void luaW_pushteam(lua_State *L, team &tm)
Create a full userdata containing a pointer to the team.
std::string plague_type
The plague type used by the attack, if any.
This class represents a single unit of a specific type.
static lg::log_domain log_wml("wml")
game_classification * classification
int intf_get_variable(lua_State *L)
Gets a WML variable.
tod_color color
The color modifications that should be made to the game board to reflect the time of day...
int movement_cost(const t_translation::terrain_code &terrain) const
Get the unit's movement cost on a particular terrain.
int intf_match_location(lua_State *L)
Matches a location against the given filter.