Skip to content

Commit

Permalink
Refactor the game map to permit exposing it to Lua via wesnoth.curren…
Browse files Browse the repository at this point in the history
…t.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
- other than that, wesnoth.map is treated as if it were the metatable of the map object

Other stuff in here:
- Special locations are now part of the map object. The length operator is deprecated.
- Add some utility functions to help clarify the merge mode being used when assigning terrains - they just add or remove a leading or trailing ^ as necessary
- Add a utility function to extract a location from the front of a variadic parameter pack
  • Loading branch information
CelticMinstrel committed Feb 20, 2021
1 parent cc395fb commit add5c1e
Show file tree
Hide file tree
Showing 15 changed files with 646 additions and 840 deletions.
40 changes: 40 additions & 0 deletions data/lua/core.lua
Expand Up @@ -698,6 +698,46 @@ if wesnoth.kernel_type() == "Game Lua Kernel" then
end
return nil, 0
end

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.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.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')
else
--[========[Backwards compatibility for wml.tovconfig]========]
local fake_vconfig_mt = {
Expand Down
4 changes: 2 additions & 2 deletions src/editor/action/action.cpp
Expand Up @@ -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) {
Expand Down Expand Up @@ -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());
}
Expand Down
6 changes: 3 additions & 3 deletions src/editor/action/mouse/mouse_action.cpp
Expand Up @@ -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))) {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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 {
Expand Down
118 changes: 55 additions & 63 deletions src/editor/map/editor_map.cpp
Expand Up @@ -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_) {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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); });
}
}

Expand Down Expand Up @@ -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);
}


Expand Down
4 changes: 4 additions & 0 deletions src/game_board.cpp
Expand Up @@ -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
Expand Down
6 changes: 4 additions & 2 deletions src/game_board.hpp
Expand Up @@ -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>
Expand Down Expand Up @@ -157,8 +159,8 @@ class game_board : public display_context
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

Expand Down

0 comments on commit add5c1e

Please sign in to comment.