From 6a10e12a6a359e8a5a9ef2a3dfd0d6d66ba65806 Mon Sep 17 00:00:00 2001 From: gfgtdf Date: Sun, 12 Aug 2018 03:31:12 +0200 Subject: [PATCH] add wesnoth.terrain_mask lua function `[terrain_mask]` had multiple unexpected behviours, see for example #3364 in parituclar `wesnoth.wml_actions.terrain_mask { x =2, y=2, mask="Ww"}` will change the tile at (1,2) instead of (2,2), so instead of reusing the old terrain mask code i wrote a new function that behaves as one would expect. `wesnoth.terrain_mask` does not have a `border=` parameter but a `is_odd` parameter that specifies that a map is in the odd format __ __ /00\__/20\__ \__/10\__/30\ /01\__/21\__/ \__/11\__/31\ /02\__/22\__/ \__/ \__/ instead of the even map format __ __ __/10\__/30\ /00\__/20\__/ \__/11\__/31\ /01\__/21\__/ \__/12\__/32\ \__/ \__/ (Monospaced font required to see ascii images.) The lua function also has a lua interfacte, meaning it does not take wml tables but normal lua tables making it easier to use from lua code. (cherry-picked from commit a3367ee8489608a6fff42a69fb026933571848b7) --- src/game_board.cpp | 4 +- src/game_board.hpp | 2 +- src/map/map.cpp | 107 ++++++++++++++++++++++------- src/map/map.hpp | 26 ++++++- src/scripting/game_lua_kernel.cpp | 110 ++++++++++++++++++++++++++++++ src/scripting/game_lua_kernel.hpp | 1 + 6 files changed, 222 insertions(+), 28 deletions(-) diff --git a/src/game_board.cpp b/src/game_board.cpp index 5852a4596b0a..a0bfc60102f9 100644 --- a/src/game_board.cpp +++ b/src/game_board.cpp @@ -283,10 +283,8 @@ boost::optional game_board::replace_map(const gamemap & newmap) { return ret; } - - void game_board::overlay_map(const gamemap & mask_map, const config & cfg, map_location loc) { - map_->overlay(mask_map, cfg, loc); + map_->overlay_old(mask_map, cfg, loc); } bool game_board::change_terrain(const map_location &loc, const std::string &t_str, diff --git a/src/game_board.hpp b/src/game_board.hpp index ea2870dc4862..cee209f67346 100644 --- a/src/game_board.hpp +++ b/src/game_board.hpp @@ -156,7 +156,7 @@ class game_board : public display_context bool try_add_unit_to_recall_list(const map_location& loc, const unit_ptr u); boost::optional replace_map (const gamemap & r); - void overlay_map (const gamemap & o, const config & cfg, map_location loc); + void overlay_map(const gamemap & o, const config & cfg, map_location loc); bool change_terrain(const map_location &loc, const std::string &t, const std::string & mode, bool replace_if_failed); //used only by lua diff --git a/src/map/map.cpp b/src/map/map.cpp index d9b7786b5eb3..98a1869da0b1 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -206,30 +206,8 @@ std::string gamemap::write() const { return t_translation::write_game_map(tiles_, starting_positions_, t_translation::coordinate{ border_size(), border_size() }) + "\n"; } -namespace -{ - struct overlay_rule - { - t_translation::ter_list old_; - t_translation::ter_list new_; - terrain_type_data::merge_mode mode_; - boost::optional terrain_; - bool use_old_; - bool replace_if_failed_; - - overlay_rule() - : old_() - , new_() - , mode_(terrain_type_data::BOTH) - , terrain_() - , use_old_(false) - , replace_if_failed_(false) - { - } - }; -} -void gamemap::overlay(const gamemap& m, const config& rules_cfg, map_location loc) +void gamemap::overlay_old(const gamemap& m, const config& rules_cfg, map_location loc) { int xpos = loc.x; int ypos = loc.y; @@ -297,6 +275,89 @@ void gamemap::overlay(const gamemap& m, const config& rules_cfg, map_location lo } } +void gamemap::overlay(const gamemap& m, map_location loc, const std::vector& rules, bool m_is_odd, bool ignore_special_locations) +{ + int xpos = loc.wml_x(); + int ypos = loc.wml_y(); + + const int xstart = std::max(0, -xpos); + const int xend = std::min(m.total_width(), total_width() -xpos); + const int xoffset = xpos; + + const int ystart_even = std::max(0, -ypos); + const int yend_even = std::min(m.total_height(), total_height() - ypos); + const int yoffset_even = ypos; + + const int ystart_odd = std::max(0, -ypos +(xpos & 1) -(m_is_odd ? 1 : 0)); + const int yend_odd = std::min(m.total_height(), total_height() - ypos +(xpos & 1) -(m_is_odd ? 1 : 0)); + const int yoffset_odd = ypos -(xpos & 1) + (m_is_odd ? 1 : 0); + + for(int x1 = xstart; x1 != xend; ++x1) { + int ystart, yend, yoffset; + if(x1 & 1) { + ystart = ystart_odd , yend = yend_odd , yoffset = yoffset_odd; + } + else { + ystart = ystart_even, yend = yend_even, yoffset = yoffset_even; + } + for(int y1 = ystart; y1 != yend; ++y1) { + const int x2 = x1 + xoffset; + const int y2 = y1 + yoffset;//ypos + ((xpos & 1) && (x1 & 1) ? 1 : 0); + + const t_translation::terrain_code t = m.tiles_.get(x1,y1); + const t_translation::terrain_code current = tiles_.get(x2, y2); + + if(t == t_translation::FOGGED || t == t_translation::VOID_TERRAIN) { + continue; + } + + // See if there is a matching rule + const overlay_rule* rule = nullptr; + for(const overlay_rule& current_rule : rules) + { + if(!current_rule.old_.empty() && !t_translation::terrain_matches(current, current_rule.old_)) { + continue; + } + if(!current_rule.new_.empty() && !t_translation::terrain_matches(t, current_rule.new_)) { + continue; + } + rule = ¤t_rule; + break; + } + + if (!rule) { + set_terrain(map_location(x2, y2, wml_loc()), t); + } + else if(!rule->use_old_) { + set_terrain(map_location(x2, y2, wml_loc()), rule->terrain_ ? *rule->terrain_ : t , rule->mode_, rule->replace_if_failed_); + } + } + } + + if (!ignore_special_locations) { + for(auto& pair : m.starting_positions_.left) { + + int x = pair.second.wml_x(); + int y = pair.second.wml_y(); + if(x & 1) { + if(x < xstart || x >= xend || y < ystart_odd || y >= yend_odd) { + continue; + } + } + else { + if(x < xstart || x >= xend || y < ystart_even || y >= yend_even) { + continue; + } + } + int x_new = x + xoffset; + int y_new = y + ((x & 1 ) ? yoffset_odd : yoffset_even); + map_location pos_new = map_location(x_new, y_new, wml_loc()); + + starting_positions_.left.erase(pair.first); + starting_positions_.insert(starting_positions::value_type(pair.first, t_translation::coordinate(pos_new.x, pos_new.y))); + } + } +} t_translation::terrain_code gamemap::get_terrain(const map_location& loc) const { diff --git a/src/map/map.hpp b/src/map/map.hpp index ccb2e330d7a1..9bf68cd8e38e 100644 --- a/src/map/map.hpp +++ b/src/map/map.hpp @@ -22,6 +22,8 @@ class config; #include "terrain/translation.hpp" #include "terrain/type_data.hpp" +#include + //class terrain_type_data; Can't forward declare because of enum /** @@ -83,8 +85,30 @@ class gamemap std::string write() const; + struct overlay_rule + { + t_translation::ter_list old_; + t_translation::ter_list new_; + terrain_type_data::merge_mode mode_; + boost::optional terrain_; + bool use_old_; + bool replace_if_failed_; + + overlay_rule() + : old_() + , new_() + , mode_(terrain_type_data::BOTH) + , terrain_() + , use_old_(false) + , replace_if_failed_(false) + { + + } + }; + /** Overlays another map onto this one at the given position. */ - void overlay(const gamemap& m, const config& rules, map_location loc); + void overlay_old(const gamemap& m, const config& rules, map_location loc); + void overlay(const gamemap& m, map_location loc, const std::vector& rules, bool is_odd, bool ignore_special_locations); /** Effective map width. */ int w() const { return w_; } diff --git a/src/scripting/game_lua_kernel.cpp b/src/scripting/game_lua_kernel.cpp index 97a167ecc8f0..d3dffdc81f4c 100644 --- a/src/scripting/game_lua_kernel.cpp +++ b/src/scripting/game_lua_kernel.cpp @@ -1012,6 +1012,115 @@ int game_lua_kernel::intf_set_terrain(lua_State *L) return 0; } +static bool luaW_tableget(lua_State *L, int index, const char* key) +{ + lua_pushstring(L, key); + lua_gettable(L, index); + if(lua_isnil(L, -1)) { + lua_pop(L, 1); + return false; + } + return true; +} + +static utils::string_view luaW_tostring(lua_State *L, int index) +{ + size_t len = 0; + const char* str = lua_tolstring (L, index, &len); + return utils::string_view(str, len); +} + +/** + * Reaplces part of rhe map. + * - Arg 1: map location. + * - Arg 2: map data string. + * - Arg 3: table for optional named arguments + * - is_odd: boolen, if Arg2 has the odd mapo format (as if it was cut from a odd map location) + * - ignore_special_locations: boolean + * - rules: table of tables +*/ +int game_lua_kernel::intf_terrain_mask(lua_State *L) +{ + map_location loc = luaW_checklocation(L, 1); + std::string t_str(luaL_checkstring(L, 2)); + bool is_odd = false; + bool ignore_special_locations = false; + std::vector rules; + + if(lua_istable(L, 3)) { + if(luaW_tableget(L, 3, "is_odd")) { + is_odd = luaW_toboolean(L, -1); + lua_pop(L, 1); + } + if(luaW_tableget(L, 3, "ignore_special_locations")) { + ignore_special_locations = luaW_toboolean(L, -1); + lua_pop(L, 1); + } + if(luaW_tableget(L, 3, "rules")) { + if(!lua_istable(L, -1)) { + return luaL_argerror(L, 3, "rules must be a table"); + } + + for (int i = 1, i_end = lua_rawlen(L, -1); i <= i_end; ++i) + { + lua_rawgeti(L, -1, i); + if(!lua_istable(L, -1)) { + return luaL_argerror(L, 3, "rules must be a table of tables"); + } + rules.push_back(gamemap::overlay_rule()); + auto& rule = rules.back(); + if(luaW_tableget(L, -1, "old")) { + rule.old_ = t_translation::read_list(luaW_tostring(L, -1)); + lua_pop(L, 1); + } + + if(luaW_tableget(L, -1, "new")) { + rule.new_ = t_translation::read_list(luaW_tostring(L, -1)); + lua_pop(L, 1); + } + + if(luaW_tableget(L, -1, "mode")) { + auto str = luaW_tostring(L, -1); + rule.mode_ = str == "base" ? terrain_type_data::BASE : (str == "overlay" ? terrain_type_data::OVERLAY : terrain_type_data::BOTH); + lua_pop(L, 1); + } + + if(luaW_tableget(L, -1, "terrain")) { + const t_translation::ter_list terrain = t_translation::read_list(luaW_tostring(L, -1)); + if(!terrain.empty()) { + rule.terrain_ = terrain[0]; + } + lua_pop(L, 1); + } + + if(luaW_tableget(L, -1, "use_old")) { + rule.use_old_ = luaW_toboolean(L, -1); + lua_pop(L, 1); + } + + if(luaW_tableget(L, -1, "replace_if_failed")) { + rule.replace_if_failed_ = luaW_toboolean(L, -1); + lua_pop(L, 1); + } + + lua_pop(L, 1); + } + lua_pop(L, 1); + } + } + + + gamemap mask_map(resources::gameboard->map().tdata(), ""); + mask_map.read(t_str, false); + board().map_->overlay(mask_map, loc, rules, is_odd, ignore_special_locations); + + if (game_display_) { + game_display_->needs_rebuild(true); + } + + return 0; +} + /** * Gets details about a terrain. * - Arg 1: terrain code string. @@ -4156,6 +4265,7 @@ game_lua_kernel::game_lua_kernel(game_state & gs, play_controller & pc, reports { "switch_ai", &intf_switch_ai }, { "synchronize_choice", &intf_synchronize_choice }, { "synchronize_choices", &intf_synchronize_choices }, + { "terrain_mask", &dispatch<&game_lua_kernel::intf_terrain_mask > }, { "zoom", &dispatch<&game_lua_kernel::intf_zoom > }, { "teleport", &dispatch<&game_lua_kernel::intf_teleport > }, { "unit_ability", &dispatch<&game_lua_kernel::intf_unit_ability > }, diff --git a/src/scripting/game_lua_kernel.hpp b/src/scripting/game_lua_kernel.hpp index cef515c9717b..0aab400b2513 100644 --- a/src/scripting/game_lua_kernel.hpp +++ b/src/scripting/game_lua_kernel.hpp @@ -91,6 +91,7 @@ class game_lua_kernel : public lua_kernel_base int intf_lock_view(lua_State *L); int intf_get_terrain(lua_State *L); int intf_set_terrain(lua_State *L); + int intf_terrain_mask(lua_State *L); int intf_get_terrain_info(lua_State *L); int intf_get_time_of_day(lua_State *L); int intf_get_village_owner(lua_State *L);