Refactor the game map to permit exposing it to Lua via wesnoth.current.map

The method of accessing terrain on the map has drastically changed.
- wesnoth.get_terrain and wesnoth.set_terrain are both deprecated
- wesnoth.terrain_mask still works but is moved into the wesnoth.map module and now takes the map object as the first parameter
- The map's terrain is now accessed exclusively via indexing on the map object, ie map[{x,y}]
- You set terrain by assigning a terrain code; the position of ^ in the terrain code now determines the merge mode
- The replace_if_failed option is now manifested as a function that converts any terrain code into a special value that, when assigned to a location on the map, uses the replace if failed logic.

The map object has a few attributes in it:
- width and height are the total size, including borders
- playable_width and playable_height are the values returned from wesnoth.get_map_size, which is now deprecated
- border_size is the third value from wesnoth.get_map_size
- data converts the map to a string
- Special locations are now part of the map object. The length operator is deprecated.
- other than that, wesnoth.map is treated as if it were the metatable of the map object
This commit is contained in:
Celtic Minstrel 2021-02-20 09:26:51 -05:00
parent 51cf2621f7
commit e6efc7de6c
15 changed files with 646 additions and 838 deletions

View file

@ -64,4 +64,46 @@ if wesnoth.kernel_type() ~= "Application Lua Kernel" then
return code
end
end
end
if wesnoth.kernel_type() == "Game Lua Kernel" then
wesnoth.terrain_mask = wesnoth.deprecate_api('wesnoth.terrain_mask', 'wesnoth.current.map:terrain_mask', 1, nil, function(...)
wesnoth.current.map:terrain_mask(...)
end)
wesnoth.get_terrain = wesnoth.deprecate_api('wesnoth.get_terrain', 'wesnoth.current.map[loc]', 1, nil, function(x, y)
local loc = wesnoth.map.read_location(x, y)
if loc == nil then error('get_terrain: expected location') end
return wesnoth.current.map[loc]
end)
wesnoth.set_terrain = wesnoth.deprecate_api('wesnoth.set_terrain', 'wesnoth.current.map[loc]=', 1, nil, function(...)
local loc, n = wesnoth.map.read_location(...)
if n == 0 then error('set_terrain: expected location') end
local new_ter, mode, replace_if_failed = select(n + 1, ...)
if new_ter == '' or type(new_ter) ~= 'string' then error('set_terrain: expected terrain string') end
if replace_if_failed then
mode = mode or 'both'
new_ter = wesnoth.map.replace_if_failed(new_ter, mode, true)
elseif mode == 'both' or mode == 'base' or mode == 'overlay' then
new_ter = wesnoth.map['replace_' .. mode](new_ter)
else
error('set_terrain: invalid mode')
end
wesnoth.current.map[loc] = new_ter
end)
wesnoth.get_map_size = wesnoth.deprecate_api('wesnoth.get_map_size', 'wesnoth.current.map.playable_width,playable_height,border_size', 1, nil, function()
local m = wesnoth.current.map
return m.playable_width, m.playable_height, m.border_size
end)
wesnoth.special_locations = wesnoth.deprecate_api('wesnoth.special_locations', 'wesnoth.current.map:special_locations', 1, nil, setmetatable({}, {
__index = function(_, k) return wesnoth.current.map.special_locations[k] end,
__newindex = function(_, k, v) wesnoth.current.map.special_locations[k] = v end,
__len = function(_)
local n = 0
for k,v in pairs(wesnoth.current.map.special_locations) do
n = n + 1
end
return n
end,
__pairs = function(_) return pairs(wesnoth.current.map.special_locations) end,
}), 'Note: the length operator has been removed')
end

View file

@ -241,7 +241,7 @@ std::unique_ptr<editor_action> editor_action_starting_position::perform(map_cont
{
std::unique_ptr<editor_action> undo;
const std::string* old_loc_id = mc.map().is_starting_position(loc_);
const std::string* old_loc_id = mc.map().is_special_location(loc_);
map_location old_loc = mc.map().special_location(loc_id_);
if(old_loc_id != nullptr) {
@ -271,7 +271,7 @@ std::unique_ptr<editor_action> editor_action_starting_position::perform(map_cont
void editor_action_starting_position::perform_without_undo(map_context& mc) const
{
const std::string* old_id = mc.map().is_starting_position(loc_);
const std::string* old_id = mc.map().is_special_location(loc_);
if(old_id != nullptr) {
mc.map().set_special_location(*old_id, map_location());
}

View file

@ -106,7 +106,7 @@ std::unique_ptr<editor_action> mouse_action::key_event(
|| event.key.keysym.sym == SDLK_DELETE) {
int res = event.key.keysym.sym - '0';
if (res > gamemap::MAX_PLAYERS || event.key.keysym.sym == SDLK_DELETE) res = 0;
const std::string* old_id = disp.map().is_starting_position(previous_move_hex_);
const std::string* old_id = disp.map().is_special_location(previous_move_hex_);
if (res == 0 && old_id != nullptr) {
a = std::make_unique<editor_action_starting_position>(map_location(), *old_id);
} else if (res > 0 && (old_id == nullptr || *old_id == std::to_string(res))) {
@ -402,7 +402,7 @@ std::unique_ptr<editor_action> mouse_action_starting_position::up_left(editor_di
if (!disp.map().on_board(hex)) {
return nullptr;
}
auto player_starting_at_hex = disp.map().is_starting_position(hex);
auto player_starting_at_hex = disp.map().is_special_location(hex);
if (has_ctrl_modifier()) {
if (player_starting_at_hex) {
@ -437,7 +437,7 @@ std::unique_ptr<editor_action> mouse_action_starting_position::click_left(editor
std::unique_ptr<editor_action> mouse_action_starting_position::up_right(editor_display& disp, int x, int y)
{
map_location hex = disp.hex_clicked_on(x, y);
auto player_starting_at_hex = disp.map().is_starting_position(hex);
auto player_starting_at_hex = disp.map().is_special_location(hex);
if (player_starting_at_hex != nullptr) {
return std::make_unique<editor_action_starting_position>(map_location(), *player_starting_at_hex);
} else {

View file

@ -84,20 +84,20 @@ editor_map::~editor_map()
void editor_map::sanity_check()
{
int errors = 0;
if (total_width() != tiles_.w) {
ERR_ED << "total_width is " << total_width() << " but tiles_.size() is " << tiles_.w << std::endl;
if (total_width() != tiles().w) {
ERR_ED << "total_width is " << total_width() << " but tiles().size() is " << tiles().w << std::endl;
++errors;
}
if (total_height() != tiles_.h) {
ERR_ED << "total_height is " << total_height() << " but tiles_[0].size() is " << tiles_.h << std::endl;
if (total_height() != tiles().h) {
ERR_ED << "total_height is " << total_height() << " but tiles()[0].size() is " << tiles().h << std::endl;
++errors;
}
if (w() + 2 * border_size() != total_width()) {
ERR_ED << "h is " << h_ << " and border_size is " << border_size() << " but total_width is " << total_width() << std::endl;
ERR_ED << "h is " << h() << " and border_size is " << border_size() << " but total_width is " << total_width() << std::endl;
++errors;
}
if (h() + 2 * border_size() != total_height()) {
ERR_ED << "w is " << w_ << " and border_size is " << border_size() << " but total_height is " << total_height() << std::endl;
ERR_ED << "w is " << w() << " and border_size is " << border_size() << " but total_height is " << total_height() << std::endl;
++errors;
}
for (const map_location& loc : selection_) {
@ -137,7 +137,7 @@ std::set<map_location> editor_map::set_starting_position_labels(display& disp)
std::string label;
for (const auto& pair : starting_positions_.left) {
for (const auto& pair : special_locations().left) {
bool is_number = std::find_if(pair.first.begin(), pair.first.end(), [](char c) { return !std::isdigit(c); }) == pair.first.end();
if (is_number) {
@ -246,8 +246,8 @@ void editor_map::resize(int width, int height, int x_offset, int y_offset,
// fix the starting positions
if(x_offset || y_offset) {
for (auto it = starting_positions_.left.begin(); it != starting_positions_.left.end(); ++it) {
starting_positions_.left.modify_data(it, [=](t_translation::coordinate & loc) { loc.add(-x_offset, -y_offset); });
for (auto it = special_locations().left.begin(); it != special_locations().left.end(); ++it) {
special_locations().left.modify_data(it, [=](t_translation::coordinate & loc) { loc.add(-x_offset, -y_offset); });
}
}
@ -298,130 +298,122 @@ bool editor_map::same_size_as(const gamemap& other) const
void editor_map::expand_right(int count, const t_translation::terrain_code & filler)
{
t_translation::ter_map tiles_new(tiles_.w + count, tiles_.h);
w_ += count;
for (int x = 0, x_end = tiles_.w; x != x_end; ++x) {
for (int y = 0, y_end = tiles_.h; y != y_end; ++y) {
tiles_new.get(x, y) = tiles_.get(x, y);
t_translation::ter_map tiles_new(tiles().w + count, tiles().h);
for (int x = 0, x_end = tiles().w; x != x_end; ++x) {
for (int y = 0, y_end = tiles().h; y != y_end; ++y) {
tiles_new.get(x, y) = tiles().get(x, y);
}
}
for (int x = tiles_.w, x_end = tiles_.w + count; x != x_end; ++x) {
for (int y = 0, y_end = tiles_.h; y != y_end; ++y) {
tiles_new.get(x, y) = filler == t_translation::NONE_TERRAIN ? tiles_.get(tiles_.w - 1, y) : filler;
for (int x = tiles().w, x_end = tiles().w + count; x != x_end; ++x) {
for (int y = 0, y_end = tiles().h; y != y_end; ++y) {
tiles_new.get(x, y) = filler == t_translation::NONE_TERRAIN ? tiles().get(tiles().w - 1, y) : filler;
}
}
tiles_ = std::move(tiles_new);
tiles() = std::move(tiles_new);
}
void editor_map::expand_left(int count, const t_translation::terrain_code & filler)
{
t_translation::ter_map tiles_new(tiles_.w + count, tiles_.h);
w_ += count;
for (int x = 0, x_end = tiles_.w; x != x_end; ++x) {
for (int y = 0, y_end = tiles_.h; y != y_end; ++y) {
tiles_new.get(x + count, y) = tiles_.get(x, y);
t_translation::ter_map tiles_new(tiles().w + count, tiles().h);
for (int x = 0, x_end = tiles().w; x != x_end; ++x) {
for (int y = 0, y_end = tiles().h; y != y_end; ++y) {
tiles_new.get(x + count, y) = tiles().get(x, y);
}
}
for (int x = 0, x_end = count; x != x_end; ++x) {
for (int y = 0, y_end = tiles_.h; y != y_end; ++y) {
tiles_new.get(x, y) = filler == t_translation::NONE_TERRAIN ? tiles_.get(0, y) : filler;
for (int y = 0, y_end = tiles().h; y != y_end; ++y) {
tiles_new.get(x, y) = filler == t_translation::NONE_TERRAIN ? tiles().get(0, y) : filler;
}
}
tiles_ = std::move(tiles_new);
tiles() = std::move(tiles_new);
}
void editor_map::expand_top(int count, const t_translation::terrain_code & filler)
{
t_translation::ter_map tiles_new(tiles_.w, tiles_.h + count);
h_ += count;
for (int x = 0, x_end = tiles_.w; x != x_end; ++x) {
for (int y = 0, y_end = tiles_.h; y != y_end; ++y) {
tiles_new.get(x, y + count) = tiles_.get(x, y);
t_translation::ter_map tiles_new(tiles().w, tiles().h + count);
for (int x = 0, x_end = tiles().w; x != x_end; ++x) {
for (int y = 0, y_end = tiles().h; y != y_end; ++y) {
tiles_new.get(x, y + count) = tiles().get(x, y);
}
}
for (int x = 0, x_end = tiles_.w; x != x_end; ++x) {
for (int x = 0, x_end = tiles().w; x != x_end; ++x) {
for (int y = 0, y_end = count; y != y_end; ++y) {
tiles_new.get(x, y) = filler == t_translation::NONE_TERRAIN ? tiles_.get(x, 0) : filler;
tiles_new.get(x, y) = filler == t_translation::NONE_TERRAIN ? tiles().get(x, 0) : filler;
}
}
tiles_ = std::move(tiles_new);
tiles() = std::move(tiles_new);
}
void editor_map::expand_bottom(int count, const t_translation::terrain_code & filler)
{
t_translation::ter_map tiles_new(tiles_.w, tiles_.h + count);
h_ += count;
for (int x = 0, x_end = tiles_.w; x != x_end; ++x) {
for (int y = 0, y_end = tiles_.h; y != y_end; ++y) {
tiles_new.get(x, y) = tiles_.get(x, y);
t_translation::ter_map tiles_new(tiles().w, tiles().h + count);
for (int x = 0, x_end = tiles().w; x != x_end; ++x) {
for (int y = 0, y_end = tiles().h; y != y_end; ++y) {
tiles_new.get(x, y) = tiles().get(x, y);
}
}
for (int x = 0, x_end = tiles_.w; x != x_end; ++x) {
for (int y = tiles_.h, y_end = tiles_.h + count; y != y_end; ++y) {
tiles_new.get(x, y) = filler == t_translation::NONE_TERRAIN ? tiles_.get(x, tiles_.h - 1) : filler;
for (int x = 0, x_end = tiles().w; x != x_end; ++x) {
for (int y = tiles().h, y_end = tiles().h + count; y != y_end; ++y) {
tiles_new.get(x, y) = filler == t_translation::NONE_TERRAIN ? tiles().get(x, tiles().h - 1) : filler;
}
}
tiles_ = std::move(tiles_new);
tiles() = std::move(tiles_new);
}
void editor_map::shrink_right(int count)
{
if(count < 0 || count > tiles_.w) {
if(count < 0 || count > tiles().w) {
throw editor_map_operation_exception();
}
t_translation::ter_map tiles_new(tiles_.w - count, tiles_.h);
t_translation::ter_map tiles_new(tiles().w - count, tiles().h);
for (int x = 0, x_end = tiles_new.w; x != x_end; ++x) {
for (int y = 0, y_end = tiles_new.h; y != y_end; ++y) {
tiles_new.get(x, y) = tiles_.get(x, y);
tiles_new.get(x, y) = tiles().get(x, y);
}
}
w_ -= count;
tiles_ = std::move(tiles_new);
tiles() = std::move(tiles_new);
}
void editor_map::shrink_left(int count)
{
if (count < 0 || count > tiles_.w) {
if (count < 0 || count > tiles().w) {
throw editor_map_operation_exception();
}
t_translation::ter_map tiles_new(tiles_.w - count, tiles_.h);
t_translation::ter_map tiles_new(tiles().w - count, tiles().h);
for (int x = 0, x_end = tiles_new.w; x != x_end; ++x) {
for (int y = 0, y_end = tiles_new.h; y != y_end; ++y) {
tiles_new.get(x, y) = tiles_.get(x + count, y);
tiles_new.get(x, y) = tiles().get(x + count, y);
}
}
w_ -= count;
tiles_ = std::move(tiles_new);
tiles() = std::move(tiles_new);
}
void editor_map::shrink_top(int count)
{
if (count < 0 || count > tiles_.h) {
if (count < 0 || count > tiles().h) {
throw editor_map_operation_exception();
}
t_translation::ter_map tiles_new(tiles_.w, tiles_.h - count);
t_translation::ter_map tiles_new(tiles().w, tiles().h - count);
for (int x = 0, x_end = tiles_new.w; x != x_end; ++x) {
for (int y = 0, y_end = tiles_new.h; y != y_end; ++y) {
tiles_new.get(x, y) = tiles_.get(x, y + count);
tiles_new.get(x, y) = tiles().get(x, y + count);
}
}
h_ -= count;
tiles_ = std::move(tiles_new);
tiles() = std::move(tiles_new);
}
void editor_map::shrink_bottom(int count)
{
if (count < 0 || count > tiles_.h) {
if (count < 0 || count > tiles().h) {
throw editor_map_operation_exception();
}
t_translation::ter_map tiles_new(tiles_.w, tiles_.h - count);
t_translation::ter_map tiles_new(tiles().w, tiles().h - count);
for (int x = 0, x_end = tiles_new.w; x != x_end; ++x) {
for (int y = 0, y_end = tiles_new.h; y != y_end; ++y) {
tiles_new.get(x, y) = tiles_.get(x, y);
tiles_new.get(x, y) = tiles().get(x, y);
}
}
h_ -= count;
tiles_ = std::move(tiles_new);
tiles() = std::move(tiles_new);
}

View file

@ -337,7 +337,11 @@ bool game_board::change_terrain(
} else if(mode_str == "overlay") {
mode = terrain_type_data::OVERLAY;
}
return change_terrain(loc, terrain, mode, replace_if_failed);
}
bool game_board::change_terrain(const map_location &loc, const t_translation::terrain_code &terrain, terrain_type_data::merge_mode& mode, bool replace_if_failed) {
/*
* When a hex changes from a village terrain to a non-village terrain, and
* a team owned that village it loses that village. When a hex changes from

View file

@ -16,6 +16,8 @@
#include "display_context.hpp"
#include "team.hpp"
#include "terrain/translation.hpp"
#include "terrain/type_data.hpp"
#include "units/map.hpp"
#include "units/id.hpp"
#include <optional>
@ -157,8 +159,8 @@ public:
bool try_add_unit_to_recall_list(const map_location& loc, const unit_ptr u);
std::optional<std::string> replace_map(const gamemap & r);
bool change_terrain(const map_location &loc, const std::string &t,
const std::string & mode, bool replace_if_failed); //used only by lua and debug commands
bool change_terrain(const map_location &loc, const std::string &t, const std::string & mode, bool replace_if_failed); //used only by lua and debug commands
bool change_terrain(const map_location &loc, const t_translation::terrain_code &t, terrain_type_data::merge_mode& mode, bool replace_if_failed); //used only by lua and debug commands
// Global accessor from unit.hpp

View file

@ -28,6 +28,7 @@
#include "serialization/string_utils.hpp"
#include "terrain/terrain.hpp"
#include "terrain/type_data.hpp"
#include "wml_exception.hpp"
#include <algorithm>
#include <sstream>
@ -101,12 +102,10 @@ void gamemap::write_terrain(const map_location &loc, config& cfg) const
cfg["terrain"] = t_translation::write_terrain_code(get_terrain(loc));
}
gamemap::gamemap(const std::string& data)
: tiles_(1, 1)
, tdata_()
, villages_()
, w_(-1)
, h_(-1)
gamemap::gamemap(const std::string& data):
gamemap_base(1, 1),
tdata_(),
villages_()
{
if(const auto* gcm = game_config_manager::get()) {
tdata_ = gcm->terrain_types();
@ -119,19 +118,24 @@ gamemap::gamemap(const std::string& data)
read(data);
}
gamemap::~gamemap()
gamemap_base::gamemap_base(int w, int h, terrain_code t)
: tiles_(w, h, t)
, starting_positions_()
{
}
gamemap_base::~gamemap_base()
{
}
void gamemap::read(const std::string& data, const bool allow_invalid)
{
tiles_ = t_translation::ter_map();
tiles() = t_translation::ter_map();
villages_.clear();
starting_positions_.clear();
special_locations().clear();
if(data.empty()) {
w_ = 0;
h_ = 0;
if(allow_invalid) return;
}
@ -140,7 +144,7 @@ void gamemap::read(const std::string& data, const bool allow_invalid)
const std::string& data_only = std::string(data, offset);
try {
tiles_ = t_translation::read_game_map(data_only, starting_positions_, t_translation::coordinate{ border_size(), border_size() });
tiles() = t_translation::read_game_map(data_only, special_locations(), t_translation::coordinate{ border_size(), border_size() });
} catch(const t_translation::error& e) {
// We re-throw the error but as map error.
@ -149,18 +153,13 @@ void gamemap::read(const std::string& data, const bool allow_invalid)
}
// Post processing on the map
w_ = total_width() - 2 * border_size();
h_ = total_height() - 2 * border_size();
//Disabled since there are callcases which pass along a valid map header but empty
//map data. Still, loading (and actually applying) an empty map causes problems later on.
//Other callcases which need to load a dummy map use completely empty data :(.
//VALIDATE((w_ >= 1 && h_ >= 1), "A map needs at least 1 tile, the map cannot be loaded.");
VALIDATE((total_width() >= 1 && total_height() >= 1), "A map needs at least 1 tile, the map cannot be loaded.");
for(int x = 0; x < total_width(); ++x) {
for(int y = 0; y < total_height(); ++y) {
// Is the terrain valid?
t_translation::terrain_code t = tiles_.get(x, y);
t_translation::terrain_code t = tiles().get(x, y);
if(tdata_->map().count(t) == 0) {
if(!tdata_->is_known(t)) {
std::stringstream ss;
@ -173,7 +172,7 @@ void gamemap::read(const std::string& data, const bool allow_invalid)
// Is it a village?
if(x >= border_size() && y >= border_size()
&& x < total_width()- border_size() && y < total_height()- border_size()
&& tdata_->is_village(tiles_.get(x, y))) {
&& tdata_->is_village(tiles().get(x, y))) {
villages_.push_back(map_location(x - border_size(), y - border_size()));
}
}
@ -209,40 +208,24 @@ int gamemap::read_header(const std::string& data)
std::string gamemap::write() const
{
return t_translation::write_game_map(tiles_, starting_positions_, t_translation::coordinate{ border_size(), border_size() }) + "\n";
return t_translation::write_game_map(tiles(), special_locations(), t_translation::coordinate{ border_size(), border_size() }) + "\n";
}
void gamemap::overlay(const gamemap& m, map_location loc, const std::vector<overlay_rule>& rules, bool m_is_odd, bool ignore_special_locations)
{
//the following line doesn't compile on all compiler without the 'this->'
overlay_impl(tiles_, starting_positions_, m.tiles_, m.starting_positions_, [this](auto&&... arg) { this->set_terrain(std::forward<decltype(arg)>(arg)...); }, loc, rules, m_is_odd, ignore_special_locations);
}
void gamemap::overlay_impl(
// const but changed via set_terrain
const t_translation::ter_map& m1,
starting_positions& m1_st,
const t_translation::ter_map& m2,
const starting_positions& m2_st,
std::function<void (const map_location&, const t_translation::terrain_code&, terrain_type_data::merge_mode, bool)> set_terrain,
map_location loc,
const std::vector<overlay_rule>& rules,
bool m_is_odd,
bool ignore_special_locations)
void gamemap_base::overlay(const gamemap_base& 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>(m2.w, m1.w -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>(m2.h, m1.h - 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>(m2.h, m1.h - 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) {
@ -257,8 +240,8 @@ void gamemap::overlay_impl(
const int x2 = x1 + xoffset;
const int y2 = y1 + yoffset;
const t_translation::terrain_code t = m2.get(x1,y1);
const t_translation::terrain_code current = m1.get(x2, y2);
const t_translation::terrain_code t = m.get_terrain({x1,y1, wml_loc()});
const t_translation::terrain_code current = get_terrain({x2, y2, wml_loc()});
if(t == t_translation::FOGGED || t == t_translation::VOID_TERRAIN) {
continue;
@ -288,7 +271,7 @@ void gamemap::overlay_impl(
}
if (!ignore_special_locations) {
for(auto& pair : m2_st.left) {
for(auto& pair : m.special_locations().left) {
int x = pair.second.wml_x();
int y = pair.second.wml_y();
@ -306,22 +289,22 @@ void gamemap::overlay_impl(
int y_new = y + ((x & 1 ) ? yoffset_odd : yoffset_even);
map_location pos_new = map_location(x_new, y_new, wml_loc());
m1_st.left.erase(pair.first);
m1_st.insert(starting_positions::value_type(pair.first, t_translation::coordinate(pos_new.x, pos_new.y)));
starting_positions_.left.erase(pair.first);
starting_positions_.insert(location_map::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
t_translation::terrain_code gamemap_base::get_terrain(const map_location& loc) const
{
if(on_board_with_border(loc)) {
return (*this)[loc];
return tiles_.get(loc.x + border_size(), loc.y + border_size());
}
return loc == map_location::null_location() ? t_translation::NONE_TERRAIN : t_translation::terrain_code();
}
map_location gamemap::special_location(const std::string& id) const
map_location gamemap_base::special_location(const std::string& id) const
{
auto it = starting_positions_.left.find(id);
if (it != starting_positions_.left.end()) {
@ -333,31 +316,46 @@ map_location gamemap::special_location(const std::string& id) const
}
}
map_location gamemap::starting_position(int n) const
map_location gamemap_base::starting_position(int n) const
{
return special_location(std::to_string(n));
}
int gamemap::num_valid_starting_positions() const
namespace {
bool is_number(const std::string& id) {
return std::find_if(id.begin(), id.end(), [](char c) { return !std::isdigit(c); }) == id.end();
}
}
int gamemap_base::num_valid_starting_positions() const
{
int res = 0;
for (auto pair : starting_positions_) {
const std::string& id = pair.left;
bool is_number = std::find_if(id.begin(), id.end(), [](char c) { return !std::isdigit(c); }) == id.end();
if (is_number) {
if (is_number(id)) {
res = std::max(res, std::stoi(id));
}
}
return res;
}
const std::string* gamemap::is_starting_position(const map_location& loc) const
int gamemap_base::is_starting_position(const map_location& loc) const
{
if(const std::string* locName = is_special_location(loc)) {
if(is_number(*locName)) {
return std::stoi(*locName);
}
}
return 0;
}
const std::string* gamemap_base::is_special_location(const map_location& loc) const
{
auto it = starting_positions_.right.find(loc);
return it == starting_positions_.right.end() ? nullptr : &it->second;
}
void gamemap::set_special_location(const std::string& id, const map_location& loc)
void gamemap_base::set_special_location(const std::string& id, const map_location& loc)
{
bool valid = loc.valid();
auto it_left = starting_positions_.left.find(id);
@ -374,21 +372,21 @@ void gamemap::set_special_location(const std::string& id, const map_location& lo
}
}
void gamemap::set_starting_position(int side, const map_location& loc)
void gamemap_base::set_starting_position(int side, const map_location& loc)
{
set_special_location(std::to_string(side), loc);
}
bool gamemap::on_board(const map_location& loc) const
bool gamemap_base::on_board(const map_location& loc) const
{
return loc.valid() && loc.x < w_ && loc.y < h_;
return loc.valid() && loc.x < w() && loc.y < h();
}
bool gamemap::on_board_with_border(const map_location& loc) const
bool gamemap_base::on_board_with_border(const map_location& loc) const
{
return !tiles_.data.empty() && // tiles_ is not empty when initialized.
loc.x >= -border_size() && loc.x < w_ + border_size() &&
loc.y >= -border_size() && loc.y < h_ + border_size();
loc.x >= -border_size() && loc.x < w() + border_size() &&
loc.y >= -border_size() && loc.y < h() + border_size();
}
void gamemap::set_terrain(const map_location& loc, const t_translation::terrain_code & terrain, const terrain_type_data::merge_mode mode, bool replace_if_failed) {
@ -418,7 +416,7 @@ void gamemap::set_terrain(const map_location& loc, const t_translation::terrain_
(*this)[loc] = new_terrain;
}
std::vector<map_location> gamemap::parse_location_range(const std::string &x, const std::string &y,
std::vector<map_location> gamemap_base::parse_location_range(const std::string &x, const std::string &y,
bool with_border) const
{
std::vector<map_location> res;
@ -463,3 +461,17 @@ std::vector<map_location> gamemap::parse_location_range(const std::string &x, co
}
return res;
}
std::string gamemap_base::to_string() const
{
return t_translation::write_game_map(tiles_, starting_positions_, { 1, 1 }) + "\n";
}
const std::vector<map_location> gamemap_base::starting_positions() const {
int n = num_valid_starting_positions();
std::vector<map_location> res;
for(int i = 1; i <= n; i++) {
res.push_back(starting_position(i));
}
return res;
}

View file

@ -23,6 +23,143 @@ class config;
//class terrain_type_data; Can't forward declare because of enum
// This could be moved to a separate file pair...
class gamemap_base
{
public:
using terrain_code = t_translation::terrain_code;
using terrain_map = t_translation::ter_map;
using location_map = t_translation::starting_positions;
virtual ~gamemap_base();
/** The default border style for a map. */
static const int default_border = 1;
/**
* Maximum number of players supported.
*
* Warning: when you increase this, you need to add
* more definitions to the team_colors.cfg file.
*/
static const int MAX_PLAYERS = 9;
std::string to_string() const;
/** Effective map width. */
int w() const { return total_width() - 2 * border_size(); }
/** Effective map height. */
int h() const { return total_height() - 2 * border_size(); }
/** Size of the map border. */
int border_size() const { return default_border; }
/** Real width of the map, including borders. */
int total_width() const { return tiles_.w; }
/** Real height of the map, including borders */
int total_height() const { return tiles_.h; }
/** Tell if the map is of 0 size. */
bool empty() const
{
return w() <= 0 || h() <= 0;
}
/**
* Tell if a location is on the map.
*/
bool on_board(const map_location& loc) const;
bool on_board_with_border(const map_location& loc) const;
/**
* Clobbers over the terrain at location 'loc', with the given terrain.
* Uses mode and replace_if_failed like merge_terrains().
*/
virtual void set_terrain(const map_location& loc, const terrain_code & terrain, const terrain_type_data::merge_mode mode = terrain_type_data::BOTH, bool replace_if_failed = false) = 0;
/**
* Looks up terrain at a particular location.
*
* Hexes off the map may be looked up, and their 'emulated' terrain will
* also be returned. This allows proper drawing of the edges of the map.
*/
terrain_code get_terrain(const map_location& loc) const;
location_map& special_locations() { return starting_positions_; }
const location_map& special_locations() const { return starting_positions_; }
const std::vector<map_location> starting_positions() const;
void set_special_location(const std::string& id, const map_location& loc);
map_location special_location(const std::string& id) const;
/** Manipulate starting positions of the different sides. */
void set_starting_position(int side, const map_location& loc);
map_location starting_position(int side) const;
/** Counts the number of sides that have valid starting positions on this map */
int num_valid_starting_positions() const;
/** returns the side number of the side starting at position loc, 0 if no such side exists. */
int is_starting_position(const map_location& loc) const;
/** returns the name of the special location at position loc, null if no such location exists. */
const std::string* is_special_location(const map_location& loc) const;
/** Parses ranges of locations into a vector of locations, using this map's dimensions as bounds. */
std::vector<map_location> parse_location_range(const std::string& xvals, const std::string &yvals, bool with_border = false) const;
struct overlay_rule
{
t_translation::ter_list old_;
t_translation::ter_list new_;
terrain_type_data::merge_mode mode_;
std::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_base& m, map_location loc, const std::vector<overlay_rule>& rules = std::vector<overlay_rule>(), bool is_odd = false, bool ignore_special_locations = false);
template<typename F>
void for_each_loc(const F& f) const
{
for(int x = 0; x < total_width(); ++x) {
for(int y = 0; y < total_height(); ++y) {
f(map_location{x, y , wml_loc()});
}
}
}
//Doesn't include border.
template<typename F>
void for_each_walkable_loc(const F& f) const
{
for(int x = 0; x < w(); ++x) {
for(int y = 0; y < h(); ++y) {
f(map_location{x, y});
}
}
}
protected:
gamemap_base() = default;
gamemap_base(int w, int h, terrain_code default_ter = terrain_code());
terrain_map& tiles() {return tiles_;}
const terrain_map& tiles() const {return tiles_;}
private:
terrain_map tiles_;
location_map starting_positions_;
};
/**
* Encapsulates the map of the game.
*
@ -30,7 +167,7 @@ class config;
* Each type of terrain is represented by a multiletter terrain code.
* @todo Update for new map-format.
*/
class gamemap
class gamemap : public gamemap_base
{
public:
@ -75,115 +212,26 @@ public:
*/
gamemap(const std::string& data); // throw(incorrect_map_format_error)
virtual ~gamemap();
void read(const std::string& data, const bool allow_invalid = true);
std::string write() const;
struct overlay_rule
{
t_translation::ter_list old_;
t_translation::ter_list new_;
terrain_type_data::merge_mode mode_;
std::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, map_location loc, const std::vector<overlay_rule>& rules = std::vector<overlay_rule>(), bool is_odd = false, bool ignore_special_locations = false);
static void overlay_impl(
// const but changed via set_terrain
const t_translation::ter_map& m1,
t_translation::starting_positions& m1_st,
const t_translation::ter_map& m2,
const t_translation::starting_positions& m2_st,
std::function<void (const map_location&, const t_translation::terrain_code&, terrain_type_data::merge_mode, bool)> set_terrain,
map_location loc,
const std::vector<overlay_rule>& rules,
bool is_odd,
bool ignore_special_locations);
/** Effective map width, in hexes. */
int w() const { return w_; }
/** Effective map height, in hexes. */
int h() const { return h_; }
/** Size of the map border. */
int border_size() const { return default_border; }
/** Real width of the map, including borders. */
int total_width() const { return tiles_.w; }
/** Real height of the map, including borders */
int total_height() const { return tiles_.h; }
const t_translation::terrain_code operator[](const map_location& loc) const
{
return tiles_.get(loc.x + border_size(), loc.y + border_size());
return tiles().get(loc.x + border_size(), loc.y + border_size());
}
private:
//private method, use set_terrain instead which also updates villages_.
t_translation::terrain_code& operator[](const map_location& loc)
{
return tiles_.get(loc.x + border_size(), loc.y + border_size());
return tiles().get(loc.x + border_size(), loc.y + border_size());
}
public:
/**
* Looks up terrain at a particular location.
*
* Hexes off the map may be looked up, and their 'emulated' terrain will
* also be returned. This allows proper drawing of the edges of the map.
*/
t_translation::terrain_code get_terrain(const map_location& loc) const;
void set_terrain(const map_location& loc, const terrain_code & terrain, const terrain_type_data::merge_mode mode = terrain_type_data::BOTH, bool replace_if_failed = false) override;
/** Writes the terrain at loc to cfg. */
void write_terrain(const map_location &loc, config& cfg) const;
/** Manipulate starting positions of the different sides. */
void set_starting_position(int side, const map_location& loc);
map_location starting_position(int side) const;
void set_special_location(const std::string& id, const map_location& loc);
map_location special_location(const std::string& id) const;
/** returns the side number of the side starting at position loc, 0 if no such side exists. */
const std::string* is_starting_position(const map_location& loc) const;
int num_valid_starting_positions() const;
/**
* Tell if a location is on the map.
*
* Should be called before indexing using [].
* @todo inline for performance? -- Ilor
*/
bool on_board(const map_location& loc) const;
bool on_board_with_border(const map_location& loc) const;
/** Tell if the map is of 0 size. */
bool empty() const
{
return w_ == 0 || h_ == 0;
}
/** Return a list of the locations of villages on the map. */
const std::vector<map_location>& villages() const { return villages_; }
@ -193,55 +241,6 @@ public:
/** Gets the list of terrains. */
const t_translation::ter_list& get_terrain_list() const;
/**
* Clobbers over the terrain at location 'loc', with the given terrain.
* Uses mode and replace_if_failed like merge_terrains().
*/
void set_terrain(const map_location& loc, const t_translation::terrain_code & terrain, const terrain_type_data::merge_mode mode=terrain_type_data::BOTH, bool replace_if_failed = false);
/**
* Maximum number of players supported.
*
* Warning: when you increase this, you need to add
* more definitions to the team_colors.cfg file.
*/
enum { MAX_PLAYERS = 9 };
/** The default border style for a map. */
static const int default_border = 1;
/** Parses ranges of locations into a vector of locations, using this map's dimensions as bounds. */
std::vector<map_location> parse_location_range(const std::string& xvals,
const std::string &yvals, bool with_border = false) const;
using starting_positions = t_translation::starting_positions;
const starting_positions& special_locations() const { return starting_positions_; }
template<typename F>
void for_each_loc(const F& f) const
{
for (int x = -border_size(); x < w() + border_size(); ++x) {
for (int y = -border_size(); y < h() + border_size(); ++y) {
f({ x, y });
}
}
}
//Doesn't include border.
template<typename F>
void for_each_walkable_loc(const F& f) const
{
for (int x = 0; x < w(); ++x) {
for (int y = 0; y < h(); ++y) {
f({ x, y });
}
}
}
protected:
t_translation::ter_map tiles_;
starting_positions starting_positions_;
private:
/**
@ -255,8 +254,4 @@ private:
protected:
std::vector<map_location> villages_;
/** Sizes of the map area. */
int w_;
int h_;
};

View file

@ -79,6 +79,7 @@
#include "scripting/lua_pathfind_cost_calculator.hpp"
#include "scripting/lua_race.hpp"
#include "scripting/lua_team.hpp"
#include "scripting/lua_terrainmap.hpp"
#include "scripting/lua_unit_type.hpp"
#include "scripting/push_check.hpp"
#include "synced_commands.hpp"
@ -211,83 +212,6 @@ std::vector<int> game_lua_kernel::get_sides_vector(const vconfig& cfg)
return filter.get_teams();
}
static int special_locations_len(lua_State *L)
{
lua_pushnumber(L, lua_kernel_base::get_lua_kernel<game_lua_kernel>(L).map().special_locations().size());
return 1;
}
static int special_locations_next(lua_State *L)
{
const t_translation::starting_positions::left_map& left = lua_kernel_base::get_lua_kernel<game_lua_kernel>(L).map().special_locations().left;
t_translation::starting_positions::left_const_iterator it;
if (lua_isnoneornil(L, 2)) {
it = left.begin();
}
else {
it = left.find(luaL_checkstring(L, 2));
if (it == left.end()) {
return 0;
}
++it;
}
if (it == left.end()) {
return 0;
}
lua_pushstring(L, it->first.c_str());
luaW_pushlocation(L, it->second);
return 2;
}
static int special_locations_pairs(lua_State *L)
{
lua_pushcfunction(L, &special_locations_next);
lua_pushvalue(L, -2);
lua_pushnil(L);
return 3;
}
static int special_locations_index(lua_State *L)
{
const t_translation::starting_positions::left_map& left = lua_kernel_base::get_lua_kernel<game_lua_kernel>(L).map().special_locations().left;
auto it = left.find(luaL_checkstring(L, 2));
if (it == left.end()) {
return 0;
}
else {
luaW_pushlocation(L, it->second);
return 1;
}
}
static int special_locations_newindex(lua_State *L)
{
lua_pushstring(L, "special locations cannot be modified using wesnoth.special_locations");
return lua_error(L);
}
static void push_locations_table(lua_State *L)
{
lua_newtable(L); // The functor table
lua_newtable(L); // The functor metatable
lua_pushstring(L, "__len");
lua_pushcfunction(L, &special_locations_len);
lua_rawset(L, -3);
lua_pushstring(L, "__index");
lua_pushcfunction(L, &special_locations_index);
lua_rawset(L, -3);
lua_pushstring(L, "__newindex");
lua_pushcfunction(L, &special_locations_newindex);
lua_rawset(L, -3);
lua_pushstring(L, "__pairs");
lua_pushcfunction(L, &special_locations_pairs);
lua_rawset(L, -3);
lua_setmetatable(L, -2); // Apply the metatable to the functor table
}
namespace {
/**
* Temporary entry to a queued_event stack
@ -939,150 +863,6 @@ int game_lua_kernel::intf_lock_view(lua_State *L)
return 0;
}
/**
* Gets a terrain code.
* - Arg 1: map location.
* - Ret 1: string.
*/
int game_lua_kernel::intf_get_terrain(lua_State *L)
{
map_location loc = luaW_checklocation(L, 1);
const t_translation::terrain_code& t = board().map().
get_terrain(loc);
lua_pushstring(L, t_translation::write_terrain_code(t).c_str());
return 1;
}
/**
* Sets a terrain code.
* - Arg 1: map location.
* - Arg 2: terrain code string.
* - Arg 3: layer: (overlay|base|both, default=both)
* - Arg 4: replace_if_failed, default = no
*/
int game_lua_kernel::intf_set_terrain(lua_State *L)
{
map_location loc = luaW_checklocation(L, 1);
std::string t_str(luaL_checkstring(L, 2));
std::string mode_str = "both";
bool replace_if_failed = false;
if (!lua_isnone(L, 3)) {
if (!lua_isnil(L, 3)) {
mode_str = luaL_checkstring(L, 3);
}
if(!lua_isnoneornil(L, 4)) {
replace_if_failed = luaW_toboolean(L, 4);
}
}
bool result = board().change_terrain(loc, t_str, mode_str, replace_if_failed);
if (game_display_) {
game_display_->needs_rebuild(result);
}
return 0;
}
/**
* Reaplces part of the 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")) {
//todo: reduce code dublication by using read_rules_vector.
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, "layer")) {
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("");
mask_map.read(t_str, false);
board().map().overlay(mask_map, loc, rules, is_odd, ignore_special_locations);
for(team& t : board().teams()) {
t.fix_villages(board().map());
}
if (game_display_) {
game_display_->needs_rebuild(true);
}
return 0;
}
/**
* Gets details about a terrain.
* - Arg 1: terrain code string.
@ -1272,22 +1052,6 @@ int game_lua_kernel::intf_set_village_owner(lua_State *L)
return 0;
}
/**
* Returns the map size.
* - Ret 1: width.
* - Ret 2: height.
* - Ret 3: border size.
*/
int game_lua_kernel::intf_get_map_size(lua_State *L)
{
const gamemap &map = board().map();
lua_pushinteger(L, map.w());
lua_pushinteger(L, map.h());
lua_pushinteger(L, map.border_size());
return 3;
}
/**
* Returns the currently overed tile.
* - Ret 1: x.
@ -1468,6 +1232,10 @@ int game_lua_kernel::impl_current_get(lua_State *L)
return_int_attrib("turn", play_controller_.turn());
return_string_attrib("synced_state", synced_state());
return_bool_attrib("user_can_invoke_commands", !play_controller_.is_lingering() && play_controller_.gamestate().init_side_done() && !events::commands_disabled && gamedata().phase() == game_data::PLAY);
if(strcmp(m, "map") == 0) {
return intf_terrainmap_get(L);
}
if (strcmp(m, "event_context") == 0)
{
@ -4235,9 +4003,7 @@ game_lua_kernel::game_lua_kernel(game_state & gs, play_controller & pc, reports
{ "get_all_vars", &dispatch<&game_lua_kernel::intf_get_all_vars > },
{ "get_end_level_data", &dispatch<&game_lua_kernel::intf_get_end_level_data > },
{ "get_locations", &dispatch<&game_lua_kernel::intf_get_locations > },
{ "get_map_size", &dispatch<&game_lua_kernel::intf_get_map_size > },
{ "get_sound_source", &dispatch<&game_lua_kernel::intf_get_sound_source > },
{ "get_terrain", &dispatch<&game_lua_kernel::intf_get_terrain > },
{ "get_terrain_info", &dispatch<&game_lua_kernel::intf_get_terrain_info > },
{ "get_time_of_day", &dispatch<&game_lua_kernel::intf_get_time_of_day > },
{ "get_max_liminal_bonus", &dispatch<&game_lua_kernel::intf_get_max_liminal_bonus > },
@ -4266,13 +4032,11 @@ game_lua_kernel::game_lua_kernel(game_state & gs, play_controller & pc, reports
{ "set_end_campaign_text", &dispatch<&game_lua_kernel::intf_set_end_campaign_text > },
{ "create_side", &dispatch<&game_lua_kernel::intf_create_side > },
{ "set_next_scenario", &dispatch<&game_lua_kernel::intf_set_next_scenario > },
{ "set_terrain", &dispatch<&game_lua_kernel::intf_set_terrain > },
{ "set_variable", &dispatch<&game_lua_kernel::intf_set_variable > },
{ "set_village_owner", &dispatch<&game_lua_kernel::intf_set_village_owner > },
{ "simulate_combat", &dispatch<&game_lua_kernel::intf_simulate_combat > },
{ "synchronize_choice", &intf_synchronize_choice },
{ "synchronize_choices", &intf_synchronize_choices },
{ "terrain_mask", &dispatch<&game_lua_kernel::intf_terrain_mask > },
{ "teleport", &dispatch<&game_lua_kernel::intf_teleport > },
{ "place_shroud", &dispatch2<&game_lua_kernel::intf_shroud_op, true > },
{ "remove_shroud", &dispatch2<&game_lua_kernel::intf_shroud_op, false > },
@ -4317,6 +4081,9 @@ game_lua_kernel::game_lua_kernel(game_state & gs, play_controller & pc, reports
// Create the unit_types table
cmd_log_ << lua_unit_type::register_table(L);
// Create the unit_types table
cmd_log_ << lua_terrainmap::register_metatables(L, false);
// Create the ai elements table.
cmd_log_ << "Adding ai elements table...\n";
@ -4341,6 +4108,14 @@ game_lua_kernel::game_lua_kernel(game_state & gs, play_controller & pc, reports
lua_pushcfunction(L, &lua_common::intf_tovconfig);
lua_setfield(L, -2, "tovconfig");
lua_pop(L, 1);
// Add replace_if_failed to the map module
luaW_getglobal(L, "wesnoth", "map");
lua_pushcfunction(L, &intf_replace_if_failed);
lua_setfield(L, -2, "replace_if_failed");
lua_pushcfunction(L, &intf_terrain_mask);
lua_setfield(L, -2, "terrain_mask");
lua_pop(L, 1);
// Create the units module
cmd_log_ << "Adding units module...\n";
@ -4480,8 +4255,6 @@ game_lua_kernel::game_lua_kernel(game_state & gs, play_controller & pc, reports
lua_getglobal(L, "wesnoth");
lua_newtable(L);
lua_setfield(L, -2, "game_events");
push_locations_table(L);
lua_setfield(L, -2, "special_locations");
lua_pop(L, 1);
// Create the theme_items table.

View file

@ -92,9 +92,6 @@ class game_lua_kernel : public lua_kernel_base
int intf_unit_ability(lua_State *L);
int intf_view_locked(lua_State *L);
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_max_liminal_bonus(lua_State *L);

View file

@ -199,7 +199,7 @@ class filter_impl
{
public:
filter_impl() {};
virtual bool matches(const mapgen_gamemap& m, map_location l) = 0;
virtual bool matches(const gamemap_base& m, map_location l) = 0;
virtual ~filter_impl() {};
};
@ -234,7 +234,7 @@ public:
LOG_LMG << "created and filter\n";
}
bool matches(const mapgen_gamemap& m, map_location l) override
bool matches(const gamemap_base& m, map_location l) override
{
LOG_MATCHES(and);
for(const auto& pfilter : list_) {
@ -255,7 +255,7 @@ public:
LOG_LMG << "created or filter\n";
}
bool matches(const mapgen_gamemap& m, map_location l) override
bool matches(const gamemap_base& m, map_location l) override
{
LOG_MATCHES(or);
for(const auto& pfilter : list_) {
@ -276,7 +276,7 @@ public:
LOG_LMG << "created nand filter\n";
}
bool matches(const mapgen_gamemap& m, map_location l) override
bool matches(const gamemap_base& m, map_location l) override
{
LOG_MATCHES(nand);
for(const auto& pfilter : list_) {
@ -297,7 +297,7 @@ public:
LOG_LMG << "created nor filter\n";
}
bool matches(const mapgen_gamemap& m, map_location l) override
bool matches(const gamemap_base& m, map_location l) override
{
LOG_MATCHES(nor);
for(const auto& pfilter : list_) {
@ -322,7 +322,7 @@ public:
lua_pop(L, 1);
}
bool matches(const mapgen_gamemap& m, map_location l) override
bool matches(const gamemap_base& m, map_location l) override
{
LOG_MATCHES(cached);
int cache_size = 2 * m.total_width() * m.total_height();
@ -357,7 +357,7 @@ public:
filter_ = parse_range(luaW_tostring(L, -1));
lua_pop(L, 1);
}
bool matches(const mapgen_gamemap&, map_location l) override
bool matches(const gamemap_base&, map_location l) override
{
LOG_MATCHES(x);
return l.x >= 0 && l.x < int(filter_.size()) && filter_[l.x];
@ -377,7 +377,7 @@ public:
lua_pop(L, 1);
}
bool matches(const mapgen_gamemap&, map_location l) override
bool matches(const gamemap_base&, map_location l) override
{
LOG_MATCHES(y);
return l.y >= 0 && l.y < int(filter_.size()) && filter_[l.y];
@ -394,10 +394,10 @@ public:
LOG_LMG << "creating onborder filter\n";
}
bool matches(const mapgen_gamemap& m, map_location l) override
bool matches(const gamemap_base& m, map_location l) override
{
LOG_MATCHES(onborder);
return !m.on_map_noborder(l);
return !m.on_board(l);
}
};
@ -414,10 +414,10 @@ public:
lua_pop(L, 1);
}
bool matches(const mapgen_gamemap& m, map_location l) override
bool matches(const gamemap_base& m, map_location l) override
{
LOG_MATCHES(terrain);
const t_translation::terrain_code letter = m[l];
const t_translation::terrain_code letter = m.get_terrain(l);
return t_translation::terrain_matches(letter, filter_);
}
@ -451,7 +451,7 @@ public:
lua_pop(L, 1);
}
bool matches(const mapgen_gamemap& m, map_location l) override
bool matches(const gamemap_base& m, map_location l) override
{
LOG_MATCHES(adjacent);
int count = 0;
@ -459,7 +459,7 @@ public:
offset_list_t& offsets = (l.wml_x() & 1) ? odd_offsets_ : even_offsets_;
for(const auto& offset : offsets) {
map_location ad = {l.x + offset.first, l.y + offset.second};
if(m.on_map(ad) && filter_->matches(m, ad)) {
if(m.on_board_with_border(ad) && filter_->matches(m, ad)) {
if(accepted_counts_.size() == 0) {
return true;
}
@ -496,7 +496,7 @@ public:
}
set_ = &insert_res.first->second;
}
bool matches(const mapgen_gamemap&, map_location l) override
bool matches(const gamemap_base&, map_location l) override
{
LOG_MATCHES(findin);
if(set_) {
@ -529,14 +529,14 @@ public:
lua_pop(L, 1);
}
bool matches(const mapgen_gamemap& m, map_location l) override
bool matches(const gamemap_base& m, map_location l) override
{
LOG_MATCHES(radius);
std::set<map_location> result;
get_tiles_radius({{ l }}, radius_, result,
[&](const map_location& l) {
return m.on_map(l);
return m.on_board_with_border(l);
},
[&](const map_location& l) {
return !filter_radius_ || filter_radius_->matches(m, l);
@ -573,7 +573,7 @@ public:
ERR_LMG << "formula error" << e.what() << "\n";
}
}
bool matches(const mapgen_gamemap&, map_location l) override
bool matches(const gamemap_base&, map_location l) override
{
LOG_MATCHES(formula);
try {
@ -672,7 +672,7 @@ filter::filter(lua_State* L, int data_index, int res_index)
LOG_LMG << "finished creating filter object\n";
}
bool filter::matches(const mapgen_gamemap& m, map_location l)
bool filter::matches(const gamemap_base& m, map_location l)
{
log_scope("filter::matches");
return impl_->matches(m, l);
@ -685,7 +685,7 @@ filter::~filter()
}
static int intf_mg_get_locations_part2(lua_State* L, mapgen_gamemap& m, lua_mapgen::filter& f)
static int intf_mg_get_locations_part2(lua_State* L, gamemap_base& m, lua_mapgen::filter& f)
{
location_set res;
LOG_LMG << "map:get_locations vaidargs\n";
@ -717,7 +717,7 @@ int intf_mg_get_locations(lua_State* L)
{
//todo: create filter form table if needed
LOG_LMG << "map:get_locations\n";
mapgen_gamemap& m = luaW_checkterrainmap(L, 1);
gamemap_base& m = luaW_checkterrainmap(L, 1);
if(luaW_is_mgfilter(L, 2)) {
lua_mapgen::filter& f = luaW_check_mgfilter(L, 2);
return intf_mg_get_locations_part2(L, m, f);
@ -733,14 +733,14 @@ int intf_mg_get_locations(lua_State* L)
int intf_mg_get_tiles_radius(lua_State* L)
{
mapgen_gamemap& m = luaW_checkterrainmap(L, 1);
gamemap_base& m = luaW_checkterrainmap(L, 1);
lua_mapgen::filter& f = luaW_check_mgfilter(L, 3);
location_set s = luaW_to_locationset(L, 2);
location_set res;
int r = luaL_checkinteger(L, 4);
get_tiles_radius(std::move(s), r, res,
[&](const map_location& l) {
return m.on_map(l);
return m.on_board_with_border(l);
},
[&](const map_location& l) {
return f.matches(m, l);

View file

@ -18,6 +18,7 @@
#include <memory>
#include <map>
#include <set>
#include "map/map.hpp"
#include "scripting/lua_common.hpp"
struct lua_State;
@ -45,7 +46,7 @@ namespace lua_mapgen
~filter();
bool matches(const mapgen_gamemap& m, map_location l);
bool matches(const gamemap_base& m, map_location l);
//todo: add a clear cache function.
private:
std::map<std::string, std::set<map_location>> known_sets_;

View file

@ -22,6 +22,9 @@
#include "scripting/lua_common.hpp"
#include "scripting/push_check.hpp"
#include "scripting/game_lua_kernel.hpp"
#include "resources.hpp"
#include "game_board.hpp"
#include "play_controller.hpp"
#include "lua/lauxlib.h"
#include "lua/lua.h"
@ -30,77 +33,26 @@ static lg::log_domain log_scripting_lua("scripting/lua");
#define LOG_LUA LOG_STREAM(info, log_scripting_lua)
#define ERR_LUA LOG_STREAM(err, log_scripting_lua)
static const char terrainmapKey[] = "terrainmap";
static const char maplocationKey[] = "special_locations";
static const char terrainmapKey[] = "terrain map";
static const char maplocationKey[] = "special locations";
static const char mapReplaceIfFailedKey[] = "replace_if_failed terrain code";
namespace replace_if_failed_idx {
enum {CODE = 1, MODE = 2};
}
using std::string_view;
//////// SPECIAL LOCATION ////////
bool luaW_isslocs(lua_State* L, int index)
{
return luaL_testudata(L, index, maplocationKey) != nullptr;
}
mapgen_gamemap* luaW_toslocs(lua_State *L, int index)
{
if(!lua_istable(L, index)) {
return nullptr;
}
lua_rawgeti(L, index, 1);
mapgen_gamemap* m = luaW_toterrainmap(L, -1);
lua_pop(L, 1);
return m;
}
mapgen_gamemap& luaW_check_slocs(lua_State *L, int index)
{
if(mapgen_gamemap* m = luaW_toslocs(L, index)) {
return *m;
}
luaW_type_error(L, index, "terrainmap");
throw "luaW_type_error didn't thow.";
}
void lua_slocs_setmetatable(lua_State *L)
{
luaL_setmetatable(L, maplocationKey);
}
/**
* @a index the index of the map object.
*/
void luaW_pushslocs(lua_State *L, int index)
{
lua_pushvalue(L, index);
//stack: map
lua_createtable(L, 1, 0);
//stack: map, slocs
lua_pushvalue(L, -2);
//stack: map, slocs, map
lua_rawseti(L, -2, 1);
//stack: map, slocs
luaL_setmetatable(L, maplocationKey);
//stack: map, slocs
lua_remove(L, -2);
//stack: slocs
}
int impl_slocs_get(lua_State* L)
{
//todo: calling map.special_locations[1] will return the underlying map
// object instead of the first starting position, because the lua
// special locations is actually a table with the map object at
// index 1. The probably easiest way to fix this inconsistency is
// to just disallow all integer indices here.
mapgen_gamemap& m = luaW_check_slocs(L, 1);
gamemap_base& m = luaW_checkterrainmap(L, 1);
string_view id = luaL_checkstring(L, 2);
auto res = m.special_location(std::string(id));
if(res.wml_x() >= 0) {
if(res.valid()) {
luaW_pushlocation(L, res);
}
else {
} else {
//functions with variable return numbers have been causing problem in the past
lua_pushnil(L);
}
@ -109,7 +61,7 @@ int impl_slocs_get(lua_State* L)
int impl_slocs_set(lua_State* L)
{
mapgen_gamemap& m = luaW_check_slocs(L, 1);
gamemap_base& m = luaW_checkterrainmap(L, 1);
string_view id = luaL_checkstring(L, 2);
map_location loc = luaW_checklocation(L, 3);
@ -117,119 +69,143 @@ int impl_slocs_set(lua_State* L)
return 0;
}
int impl_slocs_next(lua_State *L)
{
gamemap_base& m = luaW_checkterrainmap(L, lua_upvalueindex(1));
const t_translation::starting_positions::left_map& left = m.special_locations().left;
t_translation::starting_positions::left_const_iterator it;
if (lua_isnoneornil(L, 2)) {
it = left.begin();
}
else {
it = left.find(luaL_checkstring(L, 2));
if (it == left.end()) {
return 0;
}
++it;
}
if (it == left.end()) {
return 0;
}
lua_pushstring(L, it->first.c_str());
luaW_pushlocation(L, it->second);
return 2;
}
int impl_slocs_iter(lua_State *L)
{
lua_settop(L, 1);
lua_pushvalue(L, 1);
lua_pushcclosure(L, &impl_slocs_next, 1);
lua_pushvalue(L, 1);
lua_pushnil(L);
return 3;
}
//////// MAP ////////
mapgen_gamemap::mapgen_gamemap(std::string_view s)
: tiles_()
, starting_positions_()
{
if(s.empty()) {
return;
}
//throws t_translation::error
//todo: make read_game_map take a string_view
tiles_ = t_translation::read_game_map(s, starting_positions_, t_translation::coordinate{ 1, 1 });
tiles() = t_translation::read_game_map(s, special_locations(), t_translation::coordinate{ 1, 1 });
}
mapgen_gamemap::mapgen_gamemap(int w, int h, terrain_code t)
: tiles_(w, h, t)
, starting_positions_()
: gamemap_base(w, h, t)
{
}
std::string mapgen_gamemap::to_string() const
// This can produce invalid combinations in rare case
// where an overlay doesn't have an independent terrain definition,
// or if you set an overlay with no base and merge mode other than OVERLAY.
void simplemerge(t_translation::terrain_code old_t, t_translation::terrain_code& new_t, const terrain_type_data::merge_mode mode)
{
return t_translation::write_game_map(tiles_, starting_positions_, { 1, 1 }) + "\n";
switch(mode) {
case terrain_type_data::OVERLAY:
new_t = t_translation::terrain_code(old_t.base, new_t.overlay);
break;
case terrain_type_data::BASE:
new_t = t_translation::terrain_code(new_t.base, old_t.overlay);
break;
case terrain_type_data::BOTH:
new_t = t_translation::terrain_code(new_t.base, new_t.overlay);
break;
}
}
void mapgen_gamemap::set_terrain(const map_location& loc, const terrain_code & terrain, const terrain_type_data::merge_mode mode)
void mapgen_gamemap::set_terrain(const map_location& loc, const terrain_code & terrain, const terrain_type_data::merge_mode mode, bool)
{
terrain_code& t = (*this)[loc];
terrain_code old = t;
t = terrain;
terrain_code old = get_terrain(loc);
terrain_code t = terrain;
simplemerge(old, t, mode);
tiles().get(loc.x + border_size(), loc.y + border_size()) = t;
}
void mapgen_gamemap::simplemerge(terrain_code old_t, terrain_code& new_t, const terrain_type_data::merge_mode mode)
{
if(mode == terrain_type_data::OVERLAY) {
new_t = t_translation::terrain_code(old_t.base, new_t.overlay);
}
if(mode == terrain_type_data::BASE) {
new_t = t_translation::terrain_code(new_t.base, old_t.overlay);
}
}
struct lua_map_ref {
virtual gamemap_base& get_map() = 0;
virtual ~lua_map_ref() {}
};
void mapgen_gamemap::set_special_location(const std::string& id, const map_location& loc)
{
bool valid = loc.valid();
auto it_left = starting_positions_.left.find(id);
if (it_left != starting_positions_.left.end()) {
if (valid) {
starting_positions_.left.replace_data(it_left, loc);
}
else {
starting_positions_.left.erase(it_left);
}
// Mapgen map reference, owned by Lua
struct lua_map_ref_gen : public lua_map_ref {
mapgen_gamemap map;
template<typename... T>
lua_map_ref_gen(T&&... params) : map(std::forward<T>(params)...) {}
gamemap_base& get_map() override {
return map;
}
else {
starting_positions_.left.insert(it_left, std::pair(id, loc));
}
}
};
map_location mapgen_gamemap::special_location(const std::string& id) const
{
auto it = starting_positions_.left.find(id);
if (it != starting_positions_.left.end()) {
auto& coordinate = it->second;
return map_location(coordinate.x, coordinate.y);
// Main map reference, owned by the engine
struct lua_map_ref_main : public lua_map_ref {
gamemap& map;
lua_map_ref_main(gamemap& ref) : map(ref) {}
gamemap_base& get_map() override {
return map;
}
else {
return map_location();
};
// Non-owning map reference to either type (used for special location userdata)
struct lua_map_ref_locs : public lua_map_ref {
gamemap_base& map;
lua_map_ref_locs(gamemap_base& ref) : map(ref) {}
gamemap_base& get_map() override {
return map;
}
}
};
bool luaW_isterrainmap(lua_State* L, int index)
{
return luaL_testudata(L, index, terrainmapKey) != nullptr;
return luaL_testudata(L, index, terrainmapKey) != nullptr || luaL_testudata(L, index, maplocationKey) != nullptr;
}
mapgen_gamemap* luaW_toterrainmap(lua_State *L, int index)
gamemap_base* luaW_toterrainmap(lua_State *L, int index)
{
if(luaW_isterrainmap(L, index)) {
return static_cast<mapgen_gamemap*>(lua_touserdata(L, index));
return &static_cast<lua_map_ref*>(lua_touserdata(L, index))->get_map();
}
return nullptr;
}
mapgen_gamemap& luaW_checkterrainmap(lua_State *L, int index)
gamemap_base& luaW_checkterrainmap(lua_State *L, int index)
{
if(luaW_isterrainmap(L, index)) {
return *static_cast<mapgen_gamemap*>(lua_touserdata(L, index));
return static_cast<lua_map_ref*>(lua_touserdata(L, index))->get_map();
}
luaW_type_error(L, index, "terrainmap");
throw "luaW_type_error didn't throw";
}
void lua_terrainmap_setmetatable(lua_State *L)
{
luaL_setmetatable(L, terrainmapKey);
}
template<typename... T>
mapgen_gamemap* luaW_pushmap(lua_State *L, T&&... params)
{
mapgen_gamemap* res = new(L) mapgen_gamemap(std::forward<T>(params)...);
lua_terrainmap_setmetatable(L);
return res;
}
/**
* Create a map.
* - Arg 1: string describing the map data.
* - Arg 1: string descripbing the map data.
* - or:
* - Arg 1: int, width
* - Arg 2: int, height
@ -241,14 +217,20 @@ int intf_terrainmap_create(lua_State *L)
int w = lua_tonumber(L, 1);
int h = lua_tonumber(L, 2);
auto terrain = t_translation::read_terrain_code(luaL_checkstring(L, 3));
luaW_pushmap(L, w, h, terrain);
return 1;
}
else {
new(L) lua_map_ref_gen(w, h, terrain);
} else {
string_view data_str = luaL_checkstring(L, 1);
luaW_pushmap(L, data_str);
return 1;
new(L) lua_map_ref_gen(data_str);
}
luaL_setmetatable(L, terrainmapKey);
return 1;
}
int intf_terrainmap_get(lua_State* L)
{
new(L) lua_map_ref_main(const_cast<gamemap&>(resources::gameboard->map()));
luaL_setmetatable(L, terrainmapKey);
return 1;
}
/**
@ -256,11 +238,54 @@ int intf_terrainmap_create(lua_State *L)
*/
static int impl_terrainmap_collect(lua_State *L)
{
mapgen_gamemap *u = static_cast<mapgen_gamemap*>(lua_touserdata(L, 1));
u->mapgen_gamemap::~mapgen_gamemap();
lua_map_ref* m = static_cast<lua_map_ref*>(lua_touserdata(L, 1));
m->lua_map_ref::~lua_map_ref();
return 0;
}
static void luaW_push_terrain(lua_State* L, gamemap_base& map, map_location loc)
{
auto t = map.get_terrain(loc);
lua_pushstring(L, t_translation::write_terrain_code(t).c_str());
}
static void impl_merge_terrain(lua_State* L, gamemap_base& map, map_location loc)
{
auto mode = terrain_type_data::BOTH;
bool replace_if_failed = false;
string_view t_str;
if(luaL_testudata(L, 3, mapReplaceIfFailedKey)) {
replace_if_failed = true;
lua_getiuservalue(L, 3, replace_if_failed_idx::CODE);
t_str = luaL_checkstring(L, -1);
lua_getiuservalue(L, 3, replace_if_failed_idx::MODE);
mode = terrain_type_data::merge_mode(luaL_checkinteger(L, -1));
} else {
t_str = luaL_checkstring(L, 3);
if(t_str.front() == '^') {
mode = terrain_type_data::OVERLAY;
} else if(t_str.back() == '^') {
mode = terrain_type_data::BASE;
}
}
auto ter = t_translation::read_terrain_code(t_str);
if(auto gm = dynamic_cast<gamemap*>(&map)) {
if(resources::gameboard) {
bool result = resources::gameboard->change_terrain(loc, ter, mode, replace_if_failed);
for(team& t : resources::gameboard->teams()) {
t.fix_villages(*gm);
}
if(resources::controller) {
resources::controller->get_display().needs_rebuild(result);
}
}
} else map.set_terrain(loc, ter, mode, replace_if_failed);
}
/**
* Gets some data on a map (__index metamethod).
* - Arg 1: full userdata containing the map.
@ -269,19 +294,29 @@ static int impl_terrainmap_collect(lua_State *L)
*/
static int impl_terrainmap_get(lua_State *L)
{
mapgen_gamemap& tm = luaW_checkterrainmap(L, 1);
gamemap_base& tm = luaW_checkterrainmap(L, 1);
map_location loc;
if(luaW_tolocation(L, 2, loc)) {
luaW_push_terrain(L, tm, loc);
return 1;
}
char const *m = luaL_checkstring(L, 2);
// Find the corresponding attribute.
return_int_attrib("width", tm.total_width());
return_int_attrib("height", tm.total_height());
return_int_attrib("playable_width", tm.w());
return_int_attrib("playable_height", tm.h());
return_int_attrib("border_size", tm.border_size());
return_string_attrib("data", tm.to_string());
if(strcmp(m, "special_locations") == 0) {
luaW_pushslocs(L, 1);
new(L) lua_map_ref_locs(tm);
luaL_setmetatable(L, maplocationKey);
return 1;
}
if(luaW_getmetafield(L, 1, m)) {
if(luaW_getglobal(L, "wesnoth", "map", m)) {
return 1;
}
return 0;
@ -295,59 +330,21 @@ static int impl_terrainmap_get(lua_State *L)
*/
static int impl_terrainmap_set(lua_State *L)
{
mapgen_gamemap& tm = luaW_checkterrainmap(L, 1);
UNUSED(tm);
gamemap_base& tm = luaW_checkterrainmap(L, 1);
map_location loc;
// The extra check that value (arg 3) isn't a number is because without it,
// map[4] = 5 would be interpreted as map[{4, 5}] = nil, due to the way
// luaW_tolocation modifies the stack if it finds a pair of numbers on it.
if(lua_type(L, 3) != LUA_TNUMBER && luaW_tolocation(L, 2, loc)) {
impl_merge_terrain(L, tm, loc);
return 0;
}
char const *m = luaL_checkstring(L, 2);
std::string err_msg = "unknown modifiable property of map: ";
err_msg += m;
return luaL_argerror(L, 2, err_msg.c_str());
}
/**
* Sets a terrain code.
* - Arg 1: map location.
* - Arg 2: terrain code string.
* - Arg 3: layer: (overlay|base|both, default=both)
*/
static int intf_set_terrain(lua_State *L)
{
mapgen_gamemap& tm = luaW_checkterrainmap(L, 1);
map_location loc = luaW_checklocation(L, 2);
string_view t_str = luaL_checkstring(L, 3);
auto terrain = t_translation::read_terrain_code(t_str);
auto mode = terrain_type_data::BOTH;
if(!lua_isnoneornil(L, 4)) {
string_view mode_str = luaL_checkstring(L, 4);
if(mode_str == "base") {
mode = terrain_type_data::BASE;
}
else if(mode_str == "overlay") {
mode = terrain_type_data::OVERLAY;
}
}
tm.set_terrain(loc, terrain, mode);
return 0;
}
/**
* Gets a terrain code.
* - Arg 1: map location.
* - Ret 1: string.
*/
static int intf_get_terrain(lua_State *L)
{
mapgen_gamemap& tm = luaW_checkterrainmap(L, 1);
map_location loc = luaW_checklocation(L, 2);
auto t = tm[loc];
lua_pushstring(L, t_translation::write_terrain_code(t).c_str());
return 1;
}
static std::vector<gamemap::overlay_rule> read_rules_vector(lua_State *L, int index)
{
std::vector<gamemap::overlay_rule> rules;
@ -363,12 +360,13 @@ static std::vector<gamemap::overlay_rule> read_rules_vector(lua_State *L, int in
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")) {
if(luaW_tableget(L, -1, "layer")) {
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);
@ -405,19 +403,18 @@ static std::vector<gamemap::overlay_rule> read_rules_vector(lua_State *L, int in
* - ignore_special_locations: boolean
* - rules: table of tables
*/
int mapgen_gamemap::intf_mg_terrain_mask(lua_State *L)
int intf_terrain_mask(lua_State *L)
{
mapgen_gamemap& tm1 = luaW_checkterrainmap(L, 1);
gamemap_base& map = luaW_checkterrainmap(L, 1);
map_location loc = luaW_checklocation(L, 2);
mapgen_gamemap& tm2 = luaW_checkterrainmap(L, 3);
bool is_odd = false;
bool ignore_special_locations = false;
std::vector<gamemap::overlay_rule> rules;
if(lua_istable(L, 4)) {
is_odd = luaW_table_get_def<bool>(L, 4, "is_odd", false);
ignore_special_locations = luaW_table_get_def<bool>(L, 4, "ignore_special_locations", false);
is_odd = luaW_table_get_def(L, 4, "is_odd", false);
ignore_special_locations = luaW_table_get_def(L, 4, "ignore_special_locations", false);
if(luaW_tableget(L, 4, "rules")) {
if(!lua_istable(L, -1)) {
@ -427,28 +424,78 @@ int mapgen_gamemap::intf_mg_terrain_mask(lua_State *L)
lua_pop(L, 1);
}
}
if(lua_isstring(L, 3)) {
const std::string t_str = luaL_checkstring(L, 3);
std::unique_ptr<gamemap_base> mask;
if(dynamic_cast<gamemap*>(&map)) {
auto mask_ptr = new gamemap("");
mask_ptr->read(t_str, false);
mask.reset(mask_ptr);
} else {
mask.reset(new mapgen_gamemap(t_str));
}
map.overlay(*mask, loc, rules, is_odd, ignore_special_locations);
} else {
gamemap_base& mask = luaW_checkterrainmap(L, 3);
map.overlay(mask, loc, rules, is_odd, ignore_special_locations);
}
if(resources::gameboard) {
if(auto gmap = dynamic_cast<gamemap*>(&map)) {
for(team& t : resources::gameboard->teams()) {
t.fix_villages(*gmap);
}
}
}
gamemap::overlay_impl(
tm1.tiles_,
tm1.starting_positions_,
tm2.tiles_,
tm2.starting_positions_,
[&](const map_location& loc, const t_translation::terrain_code& t, terrain_type_data::merge_mode mode, bool) { tm1.set_terrain(loc, t, mode); },
loc,
rules,
is_odd,
ignore_special_locations
);
if(resources::controller) {
resources::controller->get_display().needs_rebuild(true);
}
return 0;
}
int intf_replace_if_failed(lua_State* L)
{
auto mode = terrain_type_data::BOTH;
if(!lua_isnoneornil(L, 2)) {
string_view mode_str = luaL_checkstring(L, 2);
if(mode_str == "base") {
mode = terrain_type_data::BASE;
} else if(mode_str == "overlay") {
mode = terrain_type_data::OVERLAY;
} else if(mode_str != "both") {
return luaL_argerror(L, 2, "must be one of 'base', 'overlay', or 'both'");
}
}
lua_newuserdatauv(L, 0, 2);
lua_pushinteger(L, int(mode));
lua_setiuservalue(L, -2, replace_if_failed_idx::MODE);
lua_pushvalue(L, 1);
lua_setiuservalue(L, -2, replace_if_failed_idx::CODE);
luaL_setmetatable(L, mapReplaceIfFailedKey);
return 1;
}
static int impl_replace_if_failed_tostring(lua_State* L)
{
static const char* mode_strs[] = {"base", "overlay", "both"};
lua_getiuservalue(L, 1, replace_if_failed_idx::CODE);
string_view t_str = luaL_checkstring(L, -1);
lua_getiuservalue(L, 1, replace_if_failed_idx::MODE);
int mode = luaL_checkinteger(L, -1);
lua_pushfstring(L, "replace_if_failed('%s', '%s')", t_str.data(), mode_strs[mode]);
return 1;
}
namespace lua_terrainmap {
std::string register_metatables(lua_State* L)
std::string register_metatables(lua_State* L, bool use_tf)
{
std::ostringstream cmd_out;
cmd_out << "Adding terrainmap metatable...\n";
cmd_out << "Adding terrain map metatable...\n";
luaL_newmetatable(L, terrainmapKey);
lua_pushcfunction(L, impl_terrainmap_collect);
@ -457,28 +504,32 @@ namespace lua_terrainmap {
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, impl_terrainmap_set);
lua_setfield(L, -2, "__newindex");
lua_pushstring(L, "terrainmap");
lua_pushstring(L, terrainmapKey);
lua_setfield(L, -2, "__metatable");
// terrainmap methods
lua_pushcfunction(L, intf_set_terrain);
lua_setfield(L, -2, "set_terrain");
lua_pushcfunction(L, intf_get_terrain);
lua_setfield(L, -2, "get_terrain");
lua_pushcfunction(L, intf_mg_get_locations);
lua_setfield(L, -2, "get_locations");
lua_pushcfunction(L, intf_mg_get_tiles_radius);
lua_setfield(L, -2, "get_tiles_radius");
lua_pushcfunction(L, &mapgen_gamemap::intf_mg_terrain_mask);
lua_setfield(L, -2, "terrain_mask");
if(use_tf) {
lua_pushcfunction(L, intf_mg_get_locations);
lua_setfield(L, -2, "get_locations");
lua_pushcfunction(L, intf_mg_get_tiles_radius);
lua_setfield(L, -2, "get_tiles_radius");
}
luaL_newmetatable(L, mapReplaceIfFailedKey);
lua_pushcfunction(L, impl_replace_if_failed_tostring);
lua_setfield(L, -2, "__tostring");
lua_pushstring(L, mapReplaceIfFailedKey);
lua_setfield(L, -2, "__metatable");
cmd_out << "Adding terrainmap2 metatable...\n";
cmd_out << "Adding special locations metatable...\n";
luaL_newmetatable(L, maplocationKey);
lua_pushcfunction(L, impl_slocs_get);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, impl_slocs_set);
lua_setfield(L, -2, "__newindex");
lua_pushstring(L, "special_locations");
lua_pushcfunction(L, impl_slocs_iter);
lua_setfield(L, -2, "__pairs");
lua_pushstring(L, maplocationKey);
lua_setfield(L, -2, "__metatable");
return cmd_out.str();

View file

@ -17,6 +17,7 @@
#include <string>
#include "scripting/lua_common.hpp"
#include "map/location.hpp"
#include "map/map.hpp"
#include "terrain/translation.hpp"
#include "terrain/type_data.hpp"
@ -24,60 +25,13 @@ struct lua_State;
class lua_unit;
struct map_location;
// this clas is similar to the orginal gamemap class but they have no 'is' rlation:
// mapgen_gamemap, unlike gamemap offers 'raw' access to the data
// gamemap, unlike mapgen_gamemap uses terrain type data.
class mapgen_gamemap
{
// Unlike the original gamemap, this offers 'raw' access to the data.
// The original gamemap uses terrain type data.
class mapgen_gamemap : public gamemap_base {
public:
using terrain_code = t_translation::terrain_code;
using terrain_map = t_translation::ter_map;
using starting_positions = t_translation::starting_positions;
explicit mapgen_gamemap(std::string_view data);
mapgen_gamemap(int w, int h, terrain_code);
std::string to_string() const;
/** Effective map width. */
int w() const { return total_width() - 2; }
/** Effective map height. */
int h() const { return total_height() - 2; }
/** Real width of the map, including borders. */
int total_width() const { return tiles_.w; }
/** Real height of the map, including borders */
int total_height() const { return tiles_.h; }
bool on_map(const map_location& loc) const
{
return loc.wml_x() >= 0 && loc.wml_x() < total_width() && loc.wml_y() >= 0 && loc.wml_y() < total_height();
}
bool on_map_noborder(const map_location& loc) const
{
return loc.wml_x() > 0 && loc.wml_x() < total_width() - 1 && loc.wml_y() > 0 && loc.wml_y() < total_height() - 1;
}
terrain_code& operator[](const map_location& loc)
{
return tiles_.get(loc.wml_x(), loc.wml_y());
}
const terrain_code& operator[](const map_location& loc) const
{
return tiles_.get(loc.wml_x(), loc.wml_y());
}
void set_terrain(const map_location& loc, const terrain_code & terrain, const terrain_type_data::merge_mode mode);
static void simplemerge(terrain_code old, terrain_code& t, const terrain_type_data::merge_mode mode);
starting_positions& special_locations() { return starting_positions_; }
const starting_positions& special_locations() const { return starting_positions_; }
void set_special_location(const std::string& id, const map_location& loc);
map_location special_location(const std::string& id) const;
void set_terrain(const map_location& loc, const terrain_code & terrain, const terrain_type_data::merge_mode mode = terrain_type_data::BOTH, bool replace_if_failed = false) override;
template<typename F>
void for_each_loc(const F& f) const
@ -88,38 +42,23 @@ public:
}
}
}
static int intf_mg_terrain_mask(lua_State *L);
private:
t_translation::ter_map tiles_;
starting_positions starting_positions_;
};
bool luaW_isslocs(lua_State* L, int index);
mapgen_gamemap* luaW_toslocs(lua_State *L, int index);
mapgen_gamemap& luaW_check_slocs(lua_State *L, int index);
void lua_slocs_setmetatable(lua_State *L);
void luaW_pushslocs(lua_State *L, int index);
int impl_slocs_get(lua_State* L);
int impl_slocs_set(lua_State* L);
int intf_terrain_mask(lua_State *L);
bool luaW_isterrainmap(lua_State* L, int index);
mapgen_gamemap* luaW_toterrainmap(lua_State *L, int index);
gamemap_base* luaW_toterrainmap(lua_State *L, int index);
mapgen_gamemap& luaW_checkterrainmap(lua_State *L, int index);
gamemap_base& luaW_checkterrainmap(lua_State *L, int index);
void lua_terrainmap_setmetatable(lua_State *L);
mapgen_gamemap* luaW_pushmap(lua_State *L, mapgen_gamemap&& u);
int intf_terrainmap_create(lua_State *L);
int intf_terrainmap_get(lua_State *L);
int intf_replace_if_failed(lua_State* L);
namespace lua_terrainmap {
std::string register_metatables(lua_State *L);
std::string register_metatables(lua_State *L, bool use_tf);
}

View file

@ -237,7 +237,7 @@ mapgen_lua_kernel::mapgen_lua_kernel(const config* vars)
assert(lua_gettop(L) == 0);
cmd_log_ << lua_terrainmap::register_metatables(L);
cmd_log_ << lua_terrainmap::register_metatables(L, true);
cmd_log_ << lua_terrainfilter::register_metatables(L);
}