From facf4eefda47b8eec733deb2cd8478204d2c0be1 Mon Sep 17 00:00:00 2001 From: Celtic Minstrel Date: Wed, 11 Sep 2024 22:34:04 -0400 Subject: [PATCH] Lua API: Add __dir metamethod to wesnoth.scenario --- src/scripting/game_lua_kernel.cpp | 286 +++++++++++++++++++----------- src/scripting/game_lua_kernel.hpp | 2 + src/scripting/push_check.hpp | 22 +++ 3 files changed, 208 insertions(+), 102 deletions(-) diff --git a/src/scripting/game_lua_kernel.cpp b/src/scripting/game_lua_kernel.cpp index 8d9d608c280..7fd72ae2e32 100644 --- a/src/scripting/game_lua_kernel.cpp +++ b/src/scripting/game_lua_kernel.cpp @@ -1547,6 +1547,178 @@ static int impl_mp_settings_len(lua_State* L) return 1; } +struct scenario_tag { + game_lua_kernel& ref; + scenario_tag(game_lua_kernel& k) : ref(k) {} + auto& tod_man() const { return ref.tod_man(); } + auto& gamedata() const { return ref.gamedata(); } + auto& pc() const { return ref.play_controller_; } + auto& cls() const { return ref.play_controller_.get_classification(); } + auto end_level_set() const { return &dispatch<&game_lua_kernel::impl_end_level_data_set>; } +}; +#define SCENARIO_GETTER(name, type) LATTR_GETTER(name, type, scenario_tag, k) +#define SCENARIO_SETTER(name, type) LATTR_SETTER(name, type, scenario_tag, k) +luaW_Registry scenarioReg{"scenario"}; + +template<> struct lua_object_traits { + inline static auto metatable = "scenario"; + inline static scenario_tag get(lua_State* L, int) { + return lua_kernel_base::get_lua_kernel(L); + } +}; + +SCENARIO_GETTER("turns", int) { + return k.tod_man().number_of_turns(); +} + +SCENARIO_SETTER("turns", int) { + k.tod_man().set_number_of_turns_by_wml(value); +} + +SCENARIO_GETTER("next", std::string) { + return k.gamedata().next_scenario(); +} + +SCENARIO_SETTER("next", std::string) { + k.gamedata().set_next_scenario(value); +} + +SCENARIO_GETTER("id", std::string) { + return k.gamedata().get_id(); +} + +SCENARIO_GETTER("name", t_string) { + return k.pc().get_scenario_name(); +} + +SCENARIO_GETTER("defeat_music", std::vector) { + return k.gamedata().get_defeat_music(); +} + +SCENARIO_SETTER("defeat_music", std::vector) { + k.gamedata().set_defeat_music(value); +} + +SCENARIO_GETTER("victory_music", std::vector) { + return k.gamedata().get_victory_music(); +} + +SCENARIO_SETTER("victory_music", std::vector) { + k.gamedata().set_victory_music(value); +} + +SCENARIO_GETTER("resources", std::vector) { + std::vector resources; + for(const std::string& rsrc : utils::split(k.pc().get_loaded_resources())) { + resources.push_back(find_addon("resource", rsrc)); + } + return resources; +} + +SCENARIO_GETTER("type", std::string) { + return campaign_type::get_string(k.cls().type); +} + +SCENARIO_GETTER("difficulty", std::string) { + return k.cls().difficulty; +} + +SCENARIO_GETTER("show_credits", bool) { + return k.cls().end_credits; +} + +SCENARIO_SETTER("show_credits", bool) { + k.cls().end_credits = value; +} + +SCENARIO_GETTER("end_text", t_string) { + return k.cls().end_text; +} + +SCENARIO_SETTER("end_text", t_string) { + k.cls().end_text = value; +} + +SCENARIO_GETTER("end_text_duration", int) { + return k.cls().end_text_duration; +} + +SCENARIO_SETTER("end_text_duration", int) { + k.cls().end_text_duration = value; +} + +SCENARIO_GETTER("campaign", utils::optional) { + if(k.cls().campaign.empty()) return utils::nullopt; + return find_addon("campaign", k.cls().campaign); +} + +SCENARIO_GETTER("modifications", std::vector) { + std::vector mods; + for(const std::string& mod : k.cls().active_mods) { + mods.push_back(find_addon("modification", mod)); + } + return mods; +} + +SCENARIO_GETTER("end_level_data", lua_index_raw) { + if (!k.pc().is_regular_game_end()) { + lua_pushnil(L); + return lua_index_raw(L); + } + auto data = k.pc().get_end_level_data(); + new(L) end_level_data(data); + if(luaL_newmetatable(L, "end level data")) { + static luaL_Reg const callbacks[] { + { "__index", &impl_end_level_data_get}, + { "__newindex", k.end_level_set()}, + { "__gc", &impl_end_level_data_collect}, + { nullptr, nullptr } + }; + luaL_setfuncs(L, callbacks, 0); + } + lua_setmetatable(L, -2); + return lua_index_raw(L); +} + +SCENARIO_SETTER("end_level_data", vconfig) { + end_level_data data; + + data.proceed_to_next_level = value["proceed_to_next_level"].to_bool(true); + data.transient.carryover_report = value["carryover_report"].to_bool(true); + data.prescenario_save = value["save"].to_bool(true); + data.replay_save = value["replay_save"].to_bool(true); + data.transient.linger_mode = value["linger_mode"].to_bool(true) && !k.ref.teams().empty(); + data.transient.reveal_map = value["reveal_map"].to_bool(k.pc().reveal_map_default()); + data.is_victory = value["result"] == level_result::victory; + data.test_result = value["test_result"].str(); + k.pc().set_end_level_data(data); +} + +SCENARIO_GETTER("mp_settings", lua_index_raw) { + if(!k.cls().is_multiplayer()) { + lua_pushnil(L); + return lua_index_raw(L); + } + lua_newuserdatauv(L, 0, 0); + if(luaL_newmetatable(L, "mp settings")) { + lua_pushlightuserdata(L, &k.pc()); + lua_pushcclosure(L, impl_mp_settings_get, 1); + lua_setfield(L, -2, "__index"); + lua_pushlightuserdata(L, &k.pc()); + lua_pushcclosure(L, impl_mp_settings_len, 1); + lua_setfield(L, -2, "__len"); + lua_pushstring(L, "mp settings"); + lua_setfield(L, -2, "__metatable"); + } + lua_setmetatable(L, -2); + return lua_index_raw(L); +} + +SCENARIO_GETTER("era", utils::optional) { + if(!k.cls().is_multiplayer()) return utils::nullopt; + return find_addon("era", k.cls().era_id); +} + /** * Gets some scenario data (__index metamethod). * - Arg 1: userdata (ignored). @@ -1556,80 +1728,7 @@ static int impl_mp_settings_len(lua_State* L) int game_lua_kernel::impl_scenario_get(lua_State *L) { DBG_LUA << "impl_scenario_get"; - char const *m = luaL_checkstring(L, 2); - - // Find the corresponding attribute. - return_int_attrib("turns", tod_man().number_of_turns()); - return_string_attrib("next", gamedata().next_scenario()); - return_string_attrib("id", gamedata().get_id()); - return_tstring_attrib("name", play_controller_.get_scenario_name()); - return_vector_string_attrib("defeat_music", gamedata().get_defeat_music()); - return_vector_string_attrib("victory_music", gamedata().get_victory_music()); - if(strcmp(m, "resources") == 0) { - std::vector resources; - for(const std::string& rsrc : utils::split(play_controller_.get_loaded_resources())) { - resources.push_back(find_addon("resource", rsrc)); - } - lua_push(L, resources); - return 1; - } - - const game_classification& classification = play_controller_.get_classification(); - return_string_attrib("type", campaign_type::get_string(classification.type)); - return_string_attrib("difficulty", classification.difficulty); - return_bool_attrib("show_credits", classification.end_credits); - return_tstring_attrib("end_text", classification.end_text); - return_int_attrib("end_text_duration", classification.end_text_duration); - if(!classification.campaign.empty()) { - return_cfgref_attrib("campaign", find_addon("campaign", classification.campaign)); - } - if(strcmp(m, "modifications") == 0) { - std::vector mods; - for(const std::string& mod : classification.active_mods) { - mods.push_back(find_addon("modification", mod)); - } - lua_push(L, mods); - return 1; - } - if(strcmp(m, "end_level_data") == 0) { - if (!play_controller_.is_regular_game_end()) { - return 0; - } - auto data = play_controller_.get_end_level_data(); - new(L) end_level_data(data); - if(luaL_newmetatable(L, "end level data")) { - static luaL_Reg const callbacks[] { - { "__index", &impl_end_level_data_get}, - { "__newindex", &dispatch<&game_lua_kernel::impl_end_level_data_set>}, - { "__gc", &impl_end_level_data_collect}, - { nullptr, nullptr } - }; - luaL_setfuncs(L, callbacks, 0); - } - lua_setmetatable(L, -2); - - return 1; - } - - if(classification.is_multiplayer()) { - if(strcmp(m, "mp_settings") == 0) { - lua_newuserdatauv(L, 0, 0); - if(luaL_newmetatable(L, "mp settings")) { - lua_pushlightuserdata(L, &play_controller_); - lua_pushcclosure(L, impl_mp_settings_get, 1); - lua_setfield(L, -2, "__index"); - lua_pushlightuserdata(L, &play_controller_); - lua_pushcclosure(L, impl_mp_settings_len, 1); - lua_setfield(L, -2, "__len"); - lua_pushstring(L, "mp settings"); - lua_setfield(L, -2, "__metatable"); - } - lua_setmetatable(L, -2); - return 1; - } - return_cfgref_attrib("era", find_addon("era", classification.era_id)); - } - return 0; + return scenarioReg.get(L); } /** @@ -1641,35 +1740,16 @@ int game_lua_kernel::impl_scenario_get(lua_State *L) int game_lua_kernel::impl_scenario_set(lua_State *L) { DBG_LUA << "impl_scenario_set"; - char const *m = luaL_checkstring(L, 2); + return scenarioReg.set(L); +} - // Find the corresponding attribute. - modify_int_attrib("turns", tod_man().set_number_of_turns_by_wml(value)); - modify_string_attrib("next", gamedata().set_next_scenario(value)); - modify_vector_string_attrib("defeat_music", gamedata().set_defeat_music(std::move(value))); - modify_vector_string_attrib("victory_music", gamedata().set_victory_music(std::move(value))); - - game_classification& classification = play_controller_.get_classification(); - modify_bool_attrib("show_credits", classification.end_credits = value); - modify_tstring_attrib("end_text", classification.end_text = value); - modify_int_attrib("end_text_duration", classification.end_text_duration = value); - if(strcmp(m, "end_level_data") == 0) { - vconfig cfg(luaW_checkvconfig(L, 3)); - end_level_data data; - - data.proceed_to_next_level = cfg["proceed_to_next_level"].to_bool(true); - data.transient.carryover_report = cfg["carryover_report"].to_bool(true); - data.prescenario_save = cfg["save"].to_bool(true); - data.replay_save = cfg["replay_save"].to_bool(true); - data.transient.linger_mode = cfg["linger_mode"].to_bool(true) && !teams().empty(); - data.transient.reveal_map = cfg["reveal_map"].to_bool(play_controller_.reveal_map_default()); - data.is_victory = cfg["result"] == level_result::victory; - data.test_result = cfg["test_result"].str(); - play_controller_.set_end_level_data(data); - - return 1; - } - return 0; +/** + * Get a list of scenario data (__dir metamethod). + */ +int game_lua_kernel::impl_scenario_dir(lua_State *L) +{ + DBG_LUA << "impl_scenario_dir"; + return scenarioReg.dir(L); } /** @@ -5444,6 +5524,8 @@ game_lua_kernel::game_lua_kernel(game_state & gs, play_controller & pc, reports lua_setfield(L, -2, "__index"); lua_pushcfunction(L, &dispatch<&game_lua_kernel::impl_scenario_set>); lua_setfield(L, -2, "__newindex"); + lua_pushcfunction(L, &dispatch<&game_lua_kernel::impl_scenario_dir>); + lua_setfield(L, -2, "__dir"); lua_setmetatable(L, -2); lua_setfield(L, -2, "scenario"); lua_pop(L, 1); diff --git a/src/scripting/game_lua_kernel.hpp b/src/scripting/game_lua_kernel.hpp index b9a9baf0df7..ab55b50b06c 100644 --- a/src/scripting/game_lua_kernel.hpp +++ b/src/scripting/game_lua_kernel.hpp @@ -69,6 +69,7 @@ class game_lua_kernel : public lua_kernel_base friend class game_config_manager; // to allow it to call extract_preload_scripts friend struct current_tag; + friend struct scenario_tag; // Private lua callbacks int intf_allow_end_turn(lua_State *); @@ -107,6 +108,7 @@ class game_lua_kernel : public lua_kernel_base int impl_game_config_set(lua_State *L) override; int impl_scenario_get(lua_State *L); int impl_scenario_set(lua_State *L); + int impl_scenario_dir(lua_State *L); int impl_current_get(lua_State *L); int impl_current_dir(lua_State *L); int intf_clear_messages(lua_State*); diff --git a/src/scripting/push_check.hpp b/src/scripting/push_check.hpp index 32ea0c5deb4..125c652297e 100644 --- a/src/scripting/push_check.hpp +++ b/src/scripting/push_check.hpp @@ -20,6 +20,7 @@ #include "lua/wrapper_lauxlib.h" #include "tstring.hpp" #include "map/location.hpp" +#include "variable.hpp" #include #include @@ -148,6 +149,27 @@ namespace lua_check_impl luaW_pushconfig(L, val); } + //vconfig + template + std::enable_if_t, vconfig> + lua_check(lua_State *L, int n) + { + return luaW_checkvconfig(L, n); + } + template + std::enable_if_t, vconfig> + lua_to_or_default(lua_State *L, int n, const T& def) + { + vconfig cfg = vconfig::unconstructed_vconfig(); + return luaW_tovconfig(L, n, cfg) ? cfg : def; + } + template + std::enable_if_t, void> + lua_push(lua_State *L, const vconfig& val) + { + luaW_pushvconfig(L, val); + } + //location template std::enable_if_t, map_location>