29 #include "formula/callable_objects.hpp"
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>>;
51 using std::string_view;
56 #define LOG_MATCHES(NAME) \
57 LOG_LMG << #NAME << ":matches(" << l << ") line:" << __LINE__;
61 int atoi(string_view
s)
68 int res = strtol(&
s[0], end, 10);
72 std::pair<int, int> parse_single_range(string_view
s)
74 int dash_pos =
s.find(
'-');
75 if(dash_pos ==
int(string_view::npos)) {
80 string_view first =
s.substr(0, dash_pos);
81 string_view second =
s.substr(dash_pos + 1);
82 return {atoi(first), atoi(second)};
90 auto pair = parse_single_range(part);
91 int m = std::max(pair.first, pair.second);
92 if(m >=
int(res.size())) {
94 for(
int i = pair.first;
i <= pair.second; ++
i) {
108 bool last_was_n =
false;
109 while(!str.empty()) {
110 switch(str.front()) {
134 str.remove_prefix(1);
137 odd.emplace_back(
se,
s +
se/2);
138 even.emplace_back(
se,
s +
se/2);
141 odd.emplace_back(
se,
s + (
se - 1)/2);
142 even.emplace_back(
se,
s + (
se + 1)/2);
149 parse_rel(part, even, odd);
164 std::set<map_location> res;
170 if(!lua_istable(L,
index))
return res;
171 lua_pushvalue(L,
index);
172 size_t len = lua_rawlen(L, -1);
173 for(
size_t i = 0;
i != len; ++
i) {
174 lua_geti(L, -1,
i + 1);
193 std::unique_ptr<filter_impl> build_filter(lua_State* L,
int res_index,
known_sets_t& ks);
198 con_filter(lua_State* L,
int res_index,
known_sets_t& ks)
201 LOG_LMG <<
"creating con filter";
202 size_t len = lua_rawlen(L, -1);
203 for(
size_t i = 1;
i != len; ++
i) {
204 lua_geti(L, -1,
i + 1);
205 list_.emplace_back(build_filter(L, res_index, ks));
209 std::vector<std::unique_ptr<filter_impl>> list_;
212 class and_filter :
public con_filter
215 and_filter(lua_State* L,
int res_index,
known_sets_t& ks)
216 : con_filter(L, res_index, ks)
218 LOG_LMG <<
"created and filter";
224 for(
const auto& pfilter : list_) {
225 if(!pfilter->matches(m, l)) {
233 class or_filter :
public con_filter
236 or_filter(lua_State* L,
int res_index,
known_sets_t& ks)
237 : con_filter(L, res_index, ks)
239 LOG_LMG <<
"created or filter";
245 for(
const auto& pfilter : list_) {
246 if(pfilter->matches(m, l)) {
254 class nand_filter :
public con_filter
257 nand_filter(lua_State* L,
int res_index,
known_sets_t& ks)
258 : con_filter(L, res_index, ks)
260 LOG_LMG <<
"created nand filter";
266 for(
const auto& pfilter : list_) {
267 if(!pfilter->matches(m, l)) {
275 class nor_filter :
public con_filter
278 nor_filter(lua_State* L,
int res_index,
known_sets_t& ks)
279 : con_filter(L, res_index, ks)
281 LOG_LMG <<
"created nor filter";
287 for(
const auto& pfilter : list_) {
288 if(pfilter->matches(m, l)) {
299 cached_filter(lua_State* L,
int res_index,
known_sets_t& ks)
303 LOG_LMG <<
"creating cached filter";
305 filter_ = build_filter(L, res_index, ks);
315 if(
int(cache_.size()) != cache_size) {
318 if(cache_[loc_index]) {
319 return cache_[loc_index + 1];
322 bool res = filter_->matches(m, l);
323 cache_[loc_index] =
true;
324 cache_[loc_index + 1] = res;
329 std::unique_ptr<filter_impl> filter_;
339 LOG_LMG <<
"creating x filter";
347 const auto value = l.
wml_x();
348 return value >= 0 && value < int(filter_.size()) && filter_[value];
359 LOG_LMG <<
"creating y filter";
368 const auto value = l.
wml_y();
369 return value >= 0 && value < int(filter_.size()) && filter_[value];
380 LOG_LMG <<
"creating onborder filter";
396 LOG_LMG <<
"creating terrain filter";
413 static const offset_list_t even_offsets_default = {{1 , 0}, {1 , 1}, {0 , 1}, {-1 , 1}, {-1 , 0}, {0, -1}};
414 static const offset_list_t odd_offsets_default = {{1 , -1}, {1 , 0}, {0 , 1}, {-1 , 0}, {-1 , -1}, {0, -1}};
419 adjacent_filter(lua_State* L,
int res_index,
known_sets_t& ks)
422 LOG_LMG <<
"creating adjacent filter";
424 parse_rel_sequence(
luaW_tostring(L, -1), even_offsets_, odd_offsets_);
428 even_offsets_ = even_offsets_default;
429 odd_offsets_ = odd_offsets_default;
436 filter_ = build_filter(L, res_index, ks);
446 for(
const auto& offset : offsets) {
449 if(accepted_counts_.size() == 0) {
455 return int(accepted_counts_.size()) > count && accepted_counts_[count];
460 std::unique_ptr<filter_impl> filter_;
466 findin_filter(lua_State* L,
int res_index,
known_sets_t& ks)
469 LOG_LMG <<
"creating findin filter";
470 int idx = lua_absindex(L, -1);
471 switch(lua_geti(L, idx, 2)) {
478 init_from_single_loc(luaL_checkinteger(L, -2), luaL_checkinteger(L, -1));
481 if(lua_geti(L, idx, 3) == LUA_TSTRING) {
482 init_from_ranges(luaL_checkstring(L, -2), luaL_checkstring(L, -1));
484 init_from_named_set(L, luaL_checkstring(L, -1), res_index, ks);
496 void init_from_single_loc(
int x,
int y) {
502 void init_from_ranges(
const std::string& xs,
const std::string& ys) {
505 for(
size_t i = 0;
i < std::min(xvals.size(), yvals.size());
i++) {
506 for(
int x = xvals[
i].first; x <= xvals[
i].second; x++) {
507 for(
int y = yvals[
i].first; y <= yvals[
i].second; y++) {
515 void init_from_named_set(lua_State* L,
const std::string&
id,
int res_index,
known_sets_t& ks) {
517 auto insert_res = ks.insert(known_sets_t::value_type{
id, {}});
518 if(insert_res.second && res_index > 0) {
525 set_ = &insert_res.first->second;
531 return set_->find(l) != set_->end();
543 radius_filter(lua_State* L,
int res_index,
known_sets_t& ks)
548 LOG_LMG <<
"creating radius filter";
550 filter_radius_ = build_filter(L, res_index, ks);
554 radius_ = lua_tointeger(L, -1);
557 filter_ = build_filter(L, res_index, ks);
564 std::set<map_location> result;
571 return !filter_radius_ || filter_radius_->matches(m, l);
576 if(!filter_ || filter_->matches(m, lr)) {
584 std::unique_ptr<filter_impl> filter_radius_;
585 std::unique_ptr<filter_impl> filter_;
594 LOG_LMG <<
"creating formula filter";
605 return (formula_.get() !=
nullptr) && formula_->evaluate(callable).as_bool();
607 ERR_LMG <<
"Formula error: " <<
e.type <<
" at " <<
e.filename <<
':' <<
e.line <<
")";
615 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 };
617 static const std::unordered_map<std::string, filter_keys> keys {
620 {
"not_all", F_NAND },
624 {
"find_in", F_FIND_IN },
625 {
"adjacent", F_ADJACENT },
626 {
"terrain", F_TERRAIN },
627 {
"cached", F_CACHED },
628 {
"formula", F_FORMULA },
629 {
"onborder", F_ONBORDER },
630 {
"radius", F_RADIUS }
633 std::unique_ptr<filter_impl> build_filter(lua_State* L,
int res_index,
known_sets_t& ks)
635 LOG_LMG <<
"buildfilter: start";
636 if(!lua_istable(L, -1)) {
639 lua_rawgeti(L, -1, 1);
641 LOG_LMG <<
"buildfilter: got: " <<
s;
642 auto it = keys.find(
s);
643 if(it == keys.end()) {
647 auto key = it->second;
652 return std::make_unique<and_filter>(L, res_index, ks);
654 return std::make_unique<or_filter>(L, res_index, ks);
656 return std::make_unique<nand_filter>(L, res_index, ks);
658 return std::make_unique<nor_filter>(L, res_index, ks);
660 return std::make_unique<x_filter>(L, res_index, ks);
662 return std::make_unique<y_filter>(L, res_index, ks);
664 return std::make_unique<findin_filter>(L, res_index, ks);
666 return std::make_unique<adjacent_filter>(L, res_index, ks);
668 return std::make_unique<terrain_filter>(L, res_index, ks);
670 return std::make_unique<radius_filter>(L, res_index, ks);
672 return std::make_unique<cached_filter>(L, res_index, ks);
674 return std::make_unique<formula_filter>(L, res_index, ks);
676 return std::make_unique<onborder_filter>(L, res_index, ks);
678 throw "invalid filter key enum";
693 LOG_LMG <<
"creating filter object";
694 lua_pushvalue (L, data_index);
697 LOG_LMG <<
"finished creating filter object";
703 return impl_->matches(m, l);
715 LOG_LMG <<
"map:get_locations";
719 LOG_LMG <<
"map:get_locations vaidargs";
720 if(!lua_isnone(L, 3)) {
721 LOG_LMG <<
"map:get_locations some locations";
723 LOG_LMG <<
"map:get_locations #args = " <<
s.size();
725 if(
f->matches(m, l)) {
731 LOG_LMG <<
"map:get_locations all locations";
733 if(
f->matches(m, l)) {
738 LOG_LMG <<
"map:get_locations #res = " << res.size();
740 LOG_LMG <<
"map:get_locations end";
749 int r = luaL_checkinteger(L, 3);
757 return f->matches(m, l);
786 if(allow_compile && lua_istable(L,
index)) {
787 auto f = std::make_unique<lua_mapgen::filter>(L,
index, 0);
791 throw "luaW_type_error didn't throw";
799 template<
typename... T>
802 LOG_LMG <<
"luaW_push_mgfilter";
815 if(!lua_istable(L, 1)) {
816 return luaL_argerror(L, 1,
"table expected");
818 if(lua_istable(L, 2)) {
826 return luaL_argerror(L, 1,
e.what());
854 char const *m = luaL_checkstring(L, 2);
855 std::string err_msg =
"unknown modifiable property of map: ";
857 return luaL_argerror(L, 2, err_msg.c_str());
884 std::ostringstream cmd_out;
886 cmd_out <<
"Adding terrainmamap metatable...\n";
890 lua_setfield(L, -2,
"__gc");
892 lua_setfield(L, -2,
"__index");
894 lua_setfield(L, -2,
"__newindex");
895 lua_pushstring(L,
"terrain_filter");
896 lua_setfield(L, -2,
"__metatable");
899 lua_setfield(L, -2,
"clear_cache");
901 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(const std::string &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::pair< int, int > parse_range(const std::string &str)
Recognises the following patterns, and returns a {min, max} pair.
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)
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