28 #include "formula/callable_objects.hpp"
31 #include <boost/dynamic_bitset.hpp>
32 #include <unordered_map>
35 #define LOG_LMG LOG_STREAM(info, log_scripting_lua_mapgen)
36 #define ERR_LMG LOG_STREAM(err, log_scripting_lua_mapgen)
48 using knows_sets_t = std::map<std::string, std::set<map_location>>;
50 using std::string_view;
55 #define LOG_MATCHES(NAME) \
56 LOG_LMG << #NAME << ":matches(" << l << ") line:" << __LINE__;
60 int atoi(string_view
s)
67 int res = strtol(&
s[0], end, 10);
71 std::pair<int, int> parse_single_range(string_view
s)
73 int dash_pos =
s.find(
'-');
74 if(dash_pos ==
int(string_view::npos)) {
79 string_view first =
s.substr(0, dash_pos);
80 string_view second =
s.substr(dash_pos + 1);
81 return {atoi(first), atoi(second)};
89 auto pair = parse_single_range(part);
90 int m = std::max(pair.first, pair.second);
91 if(m >=
int(res.size())) {
93 for(
int i = pair.first;
i <= pair.second; ++
i) {
107 bool last_was_n =
false;
108 while(!str.empty()) {
109 switch(str.front()) {
133 str.remove_prefix(1);
136 odd.emplace_back(
se,
s +
se/2);
137 even.emplace_back(
se,
s +
se/2);
140 odd.emplace_back(
se,
s + (
se - 1)/2);
141 even.emplace_back(
se,
s + (
se + 1)/2);
148 parse_rel(part, even, odd);
163 std::set<map_location> res;
169 lua_pushvalue(L,
index);
170 size_t len = lua_rawlen(L, -1);
171 for(
size_t i = 0;
i != len; ++
i) {
172 lua_geti(L, -1,
i + 1);
191 std::unique_ptr<filter_impl> build_filter(lua_State* L,
int res_index,
knows_sets_t& ks);
196 con_filter(lua_State* L,
int res_index,
knows_sets_t& ks)
199 LOG_LMG <<
"creating con filter";
200 size_t len = lua_rawlen(L, -1);
201 for(
size_t i = 1;
i != len; ++
i) {
202 lua_geti(L, -1,
i + 1);
203 list_.emplace_back(build_filter(L, res_index, ks));
207 std::vector<std::unique_ptr<filter_impl>> list_;
210 class and_filter :
public con_filter
213 and_filter(lua_State* L,
int res_index,
knows_sets_t& ks)
214 : con_filter(L, res_index, ks)
216 LOG_LMG <<
"created and filter";
222 for(
const auto& pfilter : list_) {
223 if(!pfilter->matches(m, l)) {
231 class or_filter :
public con_filter
234 or_filter(lua_State* L,
int res_index,
knows_sets_t& ks)
235 : con_filter(L, res_index, ks)
237 LOG_LMG <<
"created or filter";
243 for(
const auto& pfilter : list_) {
244 if(pfilter->matches(m, l)) {
252 class nand_filter :
public con_filter
255 nand_filter(lua_State* L,
int res_index,
knows_sets_t& ks)
256 : con_filter(L, res_index, ks)
258 LOG_LMG <<
"created nand filter";
264 for(
const auto& pfilter : list_) {
265 if(!pfilter->matches(m, l)) {
273 class nor_filter :
public con_filter
276 nor_filter(lua_State* L,
int res_index,
knows_sets_t& ks)
277 : con_filter(L, res_index, ks)
279 LOG_LMG <<
"created nor filter";
285 for(
const auto& pfilter : list_) {
286 if(pfilter->matches(m, l)) {
297 cached_filter(lua_State* L,
int res_index,
knows_sets_t& ks)
301 LOG_LMG <<
"creating cached filter";
303 filter_ = build_filter(L, res_index, ks);
313 if(
int(cache_.size()) != cache_size) {
316 if(cache_[loc_index]) {
317 return cache_[loc_index + 1];
320 bool res = filter_->matches(m, l);
321 cache_[loc_index] =
true;
322 cache_[loc_index + 1] = res;
327 std::unique_ptr<filter_impl> filter_;
337 LOG_LMG <<
"creating x filter";
345 const auto value = l.
wml_x();
346 return value >= 0 && value < int(filter_.size()) && filter_[value];
357 LOG_LMG <<
"creating y filter";
366 const auto value = l.
wml_y();
367 return value >= 0 && value < int(filter_.size()) && filter_[value];
378 LOG_LMG <<
"creating onborder filter";
394 LOG_LMG <<
"creating terrain filter";
411 static const offset_list_t even_offsets_default = {{1 , 0}, {1 , 1}, {0 , 1}, {-1 , 1}, {-1 , 0}, {0, -1}};
412 static const offset_list_t odd_offsets_default = {{1 , -1}, {1 , 0}, {0 , 1}, {-1 , 0}, {-1 , -1}, {0, -1}};
417 adjacent_filter(lua_State* L,
int res_index,
knows_sets_t& ks)
420 LOG_LMG <<
"creating adjacent filter";
422 parse_rel_sequence(
luaW_tostring(L, -1), even_offsets_, odd_offsets_);
426 even_offsets_ = even_offsets_default;
427 odd_offsets_ = odd_offsets_default;
434 filter_ = build_filter(L, res_index, ks);
444 for(
const auto& offset : offsets) {
447 if(accepted_counts_.size() == 0) {
453 return int(accepted_counts_.size()) > count && accepted_counts_[count];
458 std::unique_ptr<filter_impl> filter_;
464 findin_filter(lua_State* L,
int res_index,
knows_sets_t& ks)
467 LOG_LMG <<
"creating findin filter";
473 auto insert_res = ks.insert(knows_sets_t::value_type{
id, {}});
474 if(insert_res.second && res_index > 0) {
481 set_ = &insert_res.first->second;
487 return set_->find(l) != set_->end();
498 radius_filter(lua_State* L,
int res_index,
knows_sets_t& ks)
503 LOG_LMG <<
"creating radius filter";
505 filter_radius_ = build_filter(L, res_index, ks);
509 radius_ = lua_tointeger(L, -1);
512 filter_ = build_filter(L, res_index, ks);
519 std::set<map_location> result;
526 return !filter_radius_ || filter_radius_->matches(m, l);
531 if(!filter_ || filter_->matches(m, lr)) {
539 std::unique_ptr<filter_impl> filter_radius_;
540 std::unique_ptr<filter_impl> filter_;
549 LOG_LMG <<
"creating formula filter";
555 formula_ = std::make_unique<wfl::formula>(code);
557 ERR_LMG <<
"formula error" <<
e.what();
566 return (formula_.get() !=
nullptr) && formula_->evaluate(callable).as_bool();
568 ERR_LMG <<
"Formula error: " <<
e.type <<
" at " <<
e.filename <<
':' <<
e.line <<
")";
572 std::unique_ptr<wfl::formula> formula_;
576 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_CACHED };
578 static const std::unordered_map<std::string, filter_keys> keys {
581 {
"not_all", F_NAND },
585 {
"find_in", F_FIND_IN },
586 {
"adjacent", F_ADJACENT },
587 {
"terrain", F_TERRAIN },
588 {
"cached", F_CACHED },
589 {
"formula", F_FORMULA },
590 {
"radius", F_RADIUS }
593 std::unique_ptr<filter_impl> build_filter(lua_State* L,
int res_index,
knows_sets_t& ks)
595 LOG_LMG <<
"buildfilter: start";
596 if(!lua_istable(L, -1)) {
599 lua_rawgeti(L, -1, 1);
601 LOG_LMG <<
"buildfilter: got: " <<
s;
602 auto it = keys.find(
s);
603 if(it == keys.end()) {
607 auto key = it->second;
612 return std::make_unique<and_filter>(L, res_index, ks);
614 return std::make_unique<or_filter>(L, res_index, ks);
616 return std::make_unique<nand_filter>(L, res_index, ks);
618 return std::make_unique<nor_filter>(L, res_index, ks);
620 return std::make_unique<x_filter>(L, res_index, ks);
622 return std::make_unique<y_filter>(L, res_index, ks);
624 return std::make_unique<findin_filter>(L, res_index, ks);
626 return std::make_unique<adjacent_filter>(L, res_index, ks);
628 return std::make_unique<terrain_filter>(L, res_index, ks);
630 return std::make_unique<radius_filter>(L, res_index, ks);
632 return std::make_unique<cached_filter>(L, res_index, ks);
634 return std::make_unique<formula_filter>(L, res_index, ks);
636 throw "invalid filter key enum";
651 LOG_LMG <<
"creating filter object";
652 lua_pushvalue (L, data_index);
655 LOG_LMG <<
"finished creating filter object";
661 return impl_->matches(m, l);
674 LOG_LMG <<
"map:get_locations vaidargs";
675 if(lua_istable(L, 3)) {
676 LOG_LMG <<
"map:get_locations some locations";
678 LOG_LMG <<
"map:get_locations #args = " <<
s.size();
680 if(
f.matches(m, l)) {
686 LOG_LMG <<
"map:get_locations all locations";
688 if(
f.matches(m, l)) {
693 LOG_LMG <<
"map:get_locations #res = " << res.size();
695 LOG_LMG <<
"map:get_locations end";
702 LOG_LMG <<
"map:get_locations";
708 else if (lua_istable(L, 2)) {
721 int r = luaL_checkinteger(L, 3);
729 return f.matches(m, l);
756 throw "luaW_type_error didn't throw";
764 template<
typename... T>
767 LOG_LMG <<
"luaW_push_mgfilter";
780 if(!lua_istable(L, 1)) {
781 return luaL_argerror(L, 1,
"table expected");
783 if(lua_istable(L, 2)) {
791 return luaL_argerror(L, 1,
e.what());
819 char const *m = luaL_checkstring(L, 2);
820 std::string err_msg =
"unknown modifiable property of map: ";
822 return luaL_argerror(L, 2, err_msg.c_str());
849 std::ostringstream cmd_out;
851 cmd_out <<
"Adding terrainmamap metatable...\n";
855 lua_setfield(L, -2,
"__gc");
857 lua_setfield(L, -2,
"__index");
859 lua_setfield(L, -2,
"__newindex");
860 lua_pushstring(L,
"terrain_filter");
861 lua_setfield(L, -2,
"__metatable");
864 lua_setfield(L, -2,
"clear_cache");
866 return cmd_out.str();
virtual bool matches(const gamemap_base &m, map_location l)=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_
std::map< std::string, std::set< map_location > > known_sets_
bool matches(const gamemap_base &m, map_location l)
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)
lua_mapgen::filter & luaW_check_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_mg_get_locations_part2(lua_State *L, gamemap_base &m, lua_mapgen::filter &f)
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::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)
std::map< std::string, std::set< map_location > > knows_sets_t
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).
bool luaW_is_mgfilter(lua_State *L, int index)
static const char terrinfilterKey[]
gamemap_base & luaW_checkterrainmap(lua_State *L, int index)
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.
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