Skip to content

Commit

Permalink
add wesnoth.terrain_mask lua function
Browse files Browse the repository at this point in the history
`[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 a3367ee)
  • Loading branch information
gfgtdf committed Oct 7, 2018
1 parent a1ee3fb commit 6a10e12
Show file tree
Hide file tree
Showing 6 changed files with 222 additions and 28 deletions.
4 changes: 1 addition & 3 deletions src/game_board.cpp
Expand Up @@ -283,10 +283,8 @@ boost::optional<std::string> 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,
Expand Down
2 changes: 1 addition & 1 deletion src/game_board.hpp
Expand Up @@ -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<std::string> 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
Expand Down
107 changes: 84 additions & 23 deletions src/map/map.cpp
Expand Up @@ -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<t_translation::terrain_code> 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;
Expand Down Expand Up @@ -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<overlay_rule>& rules, bool m_is_odd, bool ignore_special_locations)
{
int xpos = loc.wml_x();
int ypos = loc.wml_y();

const int xstart = std::max<int>(0, -xpos);
const int xend = std::min<int>(m.total_width(), total_width() -xpos);
const int xoffset = xpos;

const int ystart_even = std::max<int>(0, -ypos);
const int yend_even = std::min<int>(m.total_height(), total_height() - ypos);
const int yoffset_even = ypos;

const int ystart_odd = std::max<int>(0, -ypos +(xpos & 1) -(m_is_odd ? 1 : 0));
const int yend_odd = std::min<int>(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 = &current_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
{

Expand Down
26 changes: 25 additions & 1 deletion src/map/map.hpp
Expand Up @@ -22,6 +22,8 @@ class config;
#include "terrain/translation.hpp"
#include "terrain/type_data.hpp"

#include <boost/optional.hpp>

//class terrain_type_data; Can't forward declare because of enum

/**
Expand Down Expand Up @@ -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<t_translation::terrain_code> 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<overlay_rule>& rules, bool is_odd, bool ignore_special_locations);

/** Effective map width. */
int w() const { return w_; }
Expand Down
110 changes: 110 additions & 0 deletions src/scripting/game_lua_kernel.cpp
Expand Up @@ -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<gamemap::overlay_rule> 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.
Expand Down Expand Up @@ -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 > },
Expand Down
1 change: 1 addition & 0 deletions src/scripting/game_lua_kernel.hpp
Expand Up @@ -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);
Expand Down

0 comments on commit 6a10e12

Please sign in to comment.