32 #include <boost/dynamic_bitset.hpp>
33 #include <unordered_map>
36 #define LOG_LMG LOG_STREAM(info, log_scripting_lua_mapgen)
37 #define ERR_LMG LOG_STREAM(err, log_scripting_lua_mapgen)
49 using known_sets_t = std::map<std::string, std::set<map_location>>;
55 #define LOG_MATCHES(NAME) \
56 LOG_LMG << #NAME << ":matches(" << l << ") line:" << __LINE__;
60 std::pair<int, int> parse_single_range(std::string_view
s)
62 std::size_t dash_pos =
s.find(
'-');
63 if(dash_pos == std::string_view::npos) {
64 int res = utils::from_chars<int>(
s).value_or(0);
69 utils::from_chars<int>(
s.substr(0, dash_pos)).value_or(0),
70 utils::from_chars<int>(
s.substr(dash_pos + 1)).value_or(0)
78 auto pair = parse_single_range(part);
79 int m = std::max(pair.first, pair.second);
80 if(m >=
int(res.size())) {
82 for(
int i = pair.first;
i <= pair.second; ++
i) {
96 bool last_was_n =
false;
122 str.remove_prefix(1);
125 odd.emplace_back(
se,
s +
se/2);
126 even.emplace_back(
se,
s +
se/2);
129 odd.emplace_back(
se,
s + (
se - 1)/2);
130 even.emplace_back(
se,
s + (
se + 1)/2);
137 parse_rel(part, even, odd);
152 std::set<map_location> res;
158 if(!lua_istable(L,
index))
return res;
159 lua_pushvalue(L,
index);
160 std::size_t len = lua_rawlen(L, -1);
161 for(std::size_t
i = 0;
i != len; ++
i) {
162 lua_geti(L, -1,
i + 1);
181 std::unique_ptr<filter_impl> build_filter(lua_State* L,
int res_index,
known_sets_t& ks);
186 con_filter(lua_State* L,
int res_index,
known_sets_t& ks)
189 LOG_LMG <<
"creating con filter";
190 std::size_t len = lua_rawlen(L, -1);
191 for(std::size_t
i = 1;
i != len; ++
i) {
192 lua_geti(L, -1,
i + 1);
193 list_.emplace_back(build_filter(L, res_index, ks));
197 std::vector<std::unique_ptr<filter_impl>> list_;
200 class and_filter :
public con_filter
203 and_filter(lua_State* L,
int res_index,
known_sets_t& ks)
204 : con_filter(L, res_index, ks)
206 LOG_LMG <<
"created and filter";
212 for(
const auto& pfilter : list_) {
213 if(!pfilter->matches(m, l)) {
221 class or_filter :
public con_filter
224 or_filter(lua_State* L,
int res_index,
known_sets_t& ks)
225 : con_filter(L, res_index, ks)
227 LOG_LMG <<
"created or filter";
233 for(
const auto& pfilter : list_) {
234 if(pfilter->matches(m, l)) {
242 class nand_filter :
public con_filter
245 nand_filter(lua_State* L,
int res_index,
known_sets_t& ks)
246 : con_filter(L, res_index, ks)
248 LOG_LMG <<
"created nand filter";
254 for(
const auto& pfilter : list_) {
255 if(!pfilter->matches(m, l)) {
263 class nor_filter :
public con_filter
266 nor_filter(lua_State* L,
int res_index,
known_sets_t& ks)
267 : con_filter(L, res_index, ks)
269 LOG_LMG <<
"created nor filter";
275 for(
const auto& pfilter : list_) {
276 if(pfilter->matches(m, l)) {
287 cached_filter(lua_State* L,
int res_index,
known_sets_t& ks)
291 LOG_LMG <<
"creating cached filter";
293 filter_ = build_filter(L, res_index, ks);
303 if(
int(cache_.size()) != cache_size) {
306 if(cache_[loc_index]) {
307 return cache_[loc_index + 1];
310 bool res = filter_->matches(m, l);
311 cache_[loc_index] =
true;
312 cache_[loc_index + 1] = res;
317 std::unique_ptr<filter_impl> filter_;
327 LOG_LMG <<
"creating x filter";
335 const auto value = l.
wml_x();
336 return value >= 0 && value < int(filter_.size()) && filter_[value];
347 LOG_LMG <<
"creating y filter";
356 const auto value = l.
wml_y();
357 return value >= 0 && value < int(filter_.size()) && filter_[value];
368 LOG_LMG <<
"creating onborder filter";
384 LOG_LMG <<
"creating terrain filter";
401 static const offset_list_t even_offsets_default = {{1 , 0}, {1 , 1}, {0 , 1}, {-1 , 1}, {-1 , 0}, {0, -1}};
402 static const offset_list_t odd_offsets_default = {{1 , -1}, {1 , 0}, {0 , 1}, {-1 , 0}, {-1 , -1}, {0, -1}};
407 adjacent_filter(lua_State* L,
int res_index,
known_sets_t& ks)
410 LOG_LMG <<
"creating adjacent filter";
412 parse_rel_sequence(
luaW_tostring(L, -1), even_offsets_, odd_offsets_);
416 even_offsets_ = even_offsets_default;
417 odd_offsets_ = odd_offsets_default;
424 filter_ = build_filter(L, res_index, ks);
434 for(
const auto& offset : offsets) {
437 if(accepted_counts_.size() == 0) {
443 return int(accepted_counts_.size()) > count && accepted_counts_[count];
448 std::unique_ptr<filter_impl> filter_;
454 findin_filter(lua_State* L,
int res_index,
known_sets_t& ks)
457 LOG_LMG <<
"creating findin filter";
458 int idx = lua_absindex(L, -1);
459 switch(lua_geti(L, idx, 2)) {
466 init_from_single_loc(luaL_checkinteger(L, -2), luaL_checkinteger(L, -1));
469 if(lua_geti(L, idx, 3) == LUA_TSTRING) {
470 init_from_ranges(luaL_checkstring(L, -2), luaL_checkstring(L, -1));
472 init_from_named_set(L, luaL_checkstring(L, -2), res_index, ks);
484 void init_from_single_loc(
int x,
int y) {
490 void init_from_ranges(
const std::string& xs,
const std::string& ys) {
493 for(std::size_t
i = 0;
i < std::min(xvals.size(), yvals.size());
i++) {
494 for(
int x = xvals[
i].first; x <= xvals[
i].second; x++) {
495 for(
int y = yvals[
i].first; y <= yvals[
i].second; y++) {
503 void init_from_named_set(lua_State* L,
const std::string&
id,
int res_index,
known_sets_t& ks) {
505 auto insert_res = ks.insert(known_sets_t::value_type{
id, {}});
506 if(insert_res.second && res_index > 0) {
513 set_ = &insert_res.first->second;
519 return set_->find(l) != set_->end();
531 radius_filter(lua_State* L,
int res_index,
known_sets_t& ks)
536 LOG_LMG <<
"creating radius filter";
538 filter_radius_ = build_filter(L, res_index, ks);
542 radius_ = lua_tointeger(L, -1);
545 filter_ = build_filter(L, res_index, ks);
552 std::set<map_location> result;
559 return !filter_radius_ || filter_radius_->matches(m, l);
564 if(!filter_ || filter_->matches(m, lr)) {
572 std::unique_ptr<filter_impl> filter_radius_;
573 std::unique_ptr<filter_impl> filter_;
582 LOG_LMG <<
"creating formula filter";
593 return (formula_.get() !=
nullptr) && formula_->evaluate(callable).as_bool();
595 ERR_LMG <<
"Formula error: " <<
e.type <<
" at " <<
e.filename <<
':' <<
e.line <<
")";
603 enum filter_keys { F_AND, F_OR, F_NAND, F_NOR, F_X, F_Y, F_FIND_IN, F_ADJACENT, F_TERRAIN, F_RADIUS, F_FORMULA, F_ONBORDER, F_CACHED };
605 static const std::unordered_map<std::string, filter_keys>
keys {
608 {
"not_all", F_NAND },
612 {
"find_in", F_FIND_IN },
613 {
"adjacent", F_ADJACENT },
614 {
"terrain", F_TERRAIN },
615 {
"cached", F_CACHED },
616 {
"formula", F_FORMULA },
617 {
"onborder", F_ONBORDER },
618 {
"radius", F_RADIUS }
621 std::unique_ptr<filter_impl> build_filter(lua_State* L,
int res_index,
known_sets_t& ks)
623 LOG_LMG <<
"buildfilter: start";
624 if(!lua_istable(L, -1)) {
627 lua_rawgeti(L, -1, 1);
629 LOG_LMG <<
"buildfilter: got: " <<
s;
630 auto it =
keys.find(
s);
631 if(it ==
keys.end()) {
635 auto key = it->second;
640 return std::make_unique<and_filter>(L, res_index, ks);
642 return std::make_unique<or_filter>(L, res_index, ks);
644 return std::make_unique<nand_filter>(L, res_index, ks);
646 return std::make_unique<nor_filter>(L, res_index, ks);
648 return std::make_unique<x_filter>(L, res_index, ks);
650 return std::make_unique<y_filter>(L, res_index, ks);
652 return std::make_unique<findin_filter>(L, res_index, ks);
654 return std::make_unique<adjacent_filter>(L, res_index, ks);
656 return std::make_unique<terrain_filter>(L, res_index, ks);
658 return std::make_unique<radius_filter>(L, res_index, ks);
660 return std::make_unique<cached_filter>(L, res_index, ks);
662 return std::make_unique<formula_filter>(L, res_index, ks);
664 return std::make_unique<onborder_filter>(L, res_index, ks);
666 throw "invalid filter key enum";
681 LOG_LMG <<
"creating filter object";
682 lua_pushvalue (L, data_index);
685 LOG_LMG <<
"finished creating filter object";
691 return impl_->matches(m, l);
703 LOG_LMG <<
"map:get_locations";
707 LOG_LMG <<
"map:get_locations vaidargs";
708 if(!lua_isnone(L, 3)) {
709 LOG_LMG <<
"map:get_locations some locations";
711 LOG_LMG <<
"map:get_locations #args = " <<
s.size();
713 if(
f->matches(m, l)) {
719 LOG_LMG <<
"map:get_locations all locations";
721 if(
f->matches(m, l)) {
726 LOG_LMG <<
"map:get_locations #res = " << res.size();
728 LOG_LMG <<
"map:get_locations end";
737 int r = luaL_checkinteger(L, 3);
745 return f->matches(m, l);
774 if(allow_compile && lua_istable(L,
index)) {
775 auto f = std::make_unique<lua_mapgen::filter>(L,
index, 0);
779 throw "luaW_type_error didn't throw";
787 template<
typename... T>
790 LOG_LMG <<
"luaW_push_mgfilter";
803 if(!lua_istable(L, 1)) {
804 return luaL_argerror(L, 1,
"table expected");
806 if(lua_istable(L, 2)) {
814 return luaL_argerror(L, 1,
e.what());
840 char const *m = luaL_checkstring(L, 2);
841 std::string err_msg =
"unknown modifiable property of map: ";
843 return luaL_argerror(L, 2, err_msg.c_str());
869 std::ostringstream cmd_out;
871 cmd_out <<
"Adding terrainmamap metatable...\n";
875 lua_setfield(L, -2,
"__gc");
877 lua_setfield(L, -2,
"__index");
879 lua_setfield(L, -2,
"__newindex");
880 lua_pushstring(L,
"terrain_filter");
881 lua_setfield(L, -2,
"__metatable");
884 lua_setfield(L, -2,
"clear_cache");
886 return cmd_out.str();
virtual bool matches(const gamemap_base &m, map_location l) const =0
terrain_code get_terrain(const map_location &loc) const
Looks up terrain at a particular location.
void for_each_loc(const F &f) const
int total_width() const
Real width of the map, including borders.
bool on_board_with_border(const map_location &loc) const
int total_height() const
Real height of the map, including borders.
bool on_board(const map_location &loc) const
Tell if a location is on the map.
filter(lua_State *L, int data_index, int res_index=0)
a lua table with the following attributes [1]: the filter table,
std::unique_ptr< filter_impl > impl_
bool matches(const gamemap_base &m, map_location l) const
std::map< std::string, std::set< map_location > > known_sets_
terrain_filter(const vconfig &cfg, const filter_context *fc, const bool flat_tod)
std::string id
Text to match against addon_info.tags()
Standard logging facilities (interface).
#define log_scope(description)
int luaW_type_error(lua_State *L, int narg, const char *tname)
std::string_view luaW_tostring(lua_State *L, int index)
bool luaW_tableget(lua_State *L, int index, const char *key)
int luaW_push_locationset(lua_State *L, const std::set< map_location > &locs)
Converts a set of map locations to a Lua table pushed at the top of the stack.
bool luaW_tolocation(lua_State *L, int index, map_location &loc)
Converts an optional table or pair of integers to a map location object.
map_location luaW_checklocation(lua_State *L, int index)
Converts an optional table or pair of integers to a map location object.
lua_mapgen::filter * luaW_to_mgfilter(lua_State *L, int index)
static int impl_terrainfilter_collect(lua_State *L)
Destroys a map object before it is collected (__gc metamethod).
std::set< map_location > location_set
static int intf_clearcache(lua_State *L)
Clears the cache of a filter.
int intf_mg_get_tiles_radius(lua_State *L)
static lua_mapgen::filter * luaW_push_mgfilter(lua_State *L, T &&... params)
std::map< std::string, std::set< map_location > > known_sets_t
std::vector< std::pair< int, int > > offset_list_t
static lg::log_domain log_scripting_lua_mapgen("scripting/lua/mapgen")
int intf_terrainfilter_create(lua_State *L)
Create a filter.
int intf_mg_get_locations(lua_State *L)
#define LOG_MATCHES(NAME)
static int impl_terrainfilter_get(lua_State *L)
Gets some data on a filter (__index metamethod).
void lua_mgfilter_setmetatable(lua_State *L)
static std::set< map_location > luaW_to_locationset(lua_State *L, int index)
boost::dynamic_bitset<> dynamic_bitset
static int impl_terrainfilter_set(lua_State *L)
Sets some data on a filter (__newindex metamethod).
lua_mapgen::filter_ptr luaW_check_mgfilter(lua_State *L, int index, bool allow_compile)
bool luaW_is_mgfilter(lua_State *L, int index)
static const char terrinfilterKey[]
gamemap_base & luaW_checkterrainmap(lua_State *L, int index)
std::unique_ptr< filter, std::function< void(filter *)> > filter_ptr
std::string register_metatables(lua_State *L)
bool terrain_matches(const terrain_code &src, const terrain_code &dest)
Tests whether a specific terrain matches an expression, for matching rules see above.
std::size_t index(std::string_view str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
@ STRIP_SPACES
REMOVE_EMPTY: remove empty elements.
std::vector< std::pair< int, int > > parse_ranges_unsigned(const std::string &str)
Handles a comma-separated list of inputs to parse_range, in a context that does not expect negative v...
void split_foreach(std::string_view s, char sep, const int flags, const F &f)
std::pair< int, int > parse_range(std::string_view str)
Recognises the following patterns, and returns a {min, max} pair.
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
void get_tiles_radius(const map_location ¢er, std::size_t radius, std::set< map_location > &result)
Function that will add to result all locations within radius tiles of center (including center itself...
std::string errormessage_
const char * what() const noexcept
invalid_lua_argument(const std::string &msg)
Encapsulates the map of the game.
This structure can be used for matching terrain strings.
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
static map_location::direction se
static map_location::direction s