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 known_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 if(!lua_istable(L,
index))
return res;
170 lua_pushvalue(L,
index);
171 size_t len = lua_rawlen(L, -1);
172 for(
size_t i = 0;
i != len; ++
i) {
173 lua_geti(L, -1,
i + 1);
192 std::unique_ptr<filter_impl> build_filter(lua_State* L,
int res_index,
known_sets_t& ks);
197 con_filter(lua_State* L,
int res_index,
known_sets_t& ks)
200 LOG_LMG <<
"creating con filter";
201 size_t len = lua_rawlen(L, -1);
202 for(
size_t i = 1;
i != len; ++
i) {
203 lua_geti(L, -1,
i + 1);
204 list_.emplace_back(build_filter(L, res_index, ks));
208 std::vector<std::unique_ptr<filter_impl>> list_;
211 class and_filter :
public con_filter
214 and_filter(lua_State* L,
int res_index,
known_sets_t& ks)
215 : con_filter(L, res_index, ks)
217 LOG_LMG <<
"created and filter";
223 for(
const auto& pfilter : list_) {
224 if(!pfilter->matches(m, l)) {
232 class or_filter :
public con_filter
235 or_filter(lua_State* L,
int res_index,
known_sets_t& ks)
236 : con_filter(L, res_index, ks)
238 LOG_LMG <<
"created or filter";
244 for(
const auto& pfilter : list_) {
245 if(pfilter->matches(m, l)) {
253 class nand_filter :
public con_filter
256 nand_filter(lua_State* L,
int res_index,
known_sets_t& ks)
257 : con_filter(L, res_index, ks)
259 LOG_LMG <<
"created nand filter";
265 for(
const auto& pfilter : list_) {
266 if(!pfilter->matches(m, l)) {
274 class nor_filter :
public con_filter
277 nor_filter(lua_State* L,
int res_index,
known_sets_t& ks)
278 : con_filter(L, res_index, ks)
280 LOG_LMG <<
"created nor filter";
286 for(
const auto& pfilter : list_) {
287 if(pfilter->matches(m, l)) {
298 cached_filter(lua_State* L,
int res_index,
known_sets_t& ks)
302 LOG_LMG <<
"creating cached filter";
304 filter_ = build_filter(L, res_index, ks);
314 if(
int(cache_.size()) != cache_size) {
317 if(cache_[loc_index]) {
318 return cache_[loc_index + 1];
321 bool res = filter_->matches(m, l);
322 cache_[loc_index] =
true;
323 cache_[loc_index + 1] = res;
328 std::unique_ptr<filter_impl> filter_;
338 LOG_LMG <<
"creating x filter";
346 const auto value = l.
wml_x();
347 return value >= 0 && value < int(filter_.size()) && filter_[value];
358 LOG_LMG <<
"creating y filter";
367 const auto value = l.
wml_y();
368 return value >= 0 && value < int(filter_.size()) && filter_[value];
379 LOG_LMG <<
"creating onborder filter";
395 LOG_LMG <<
"creating terrain filter";
412 static const offset_list_t even_offsets_default = {{1 , 0}, {1 , 1}, {0 , 1}, {-1 , 1}, {-1 , 0}, {0, -1}};
413 static const offset_list_t odd_offsets_default = {{1 , -1}, {1 , 0}, {0 , 1}, {-1 , 0}, {-1 , -1}, {0, -1}};
418 adjacent_filter(lua_State* L,
int res_index,
known_sets_t& ks)
421 LOG_LMG <<
"creating adjacent filter";
423 parse_rel_sequence(
luaW_tostring(L, -1), even_offsets_, odd_offsets_);
427 even_offsets_ = even_offsets_default;
428 odd_offsets_ = odd_offsets_default;
435 filter_ = build_filter(L, res_index, ks);
445 for(
const auto& offset : offsets) {
448 if(accepted_counts_.size() == 0) {
454 return int(accepted_counts_.size()) > count && accepted_counts_[count];
459 std::unique_ptr<filter_impl> filter_;
465 findin_filter(lua_State* L,
int res_index,
known_sets_t& ks)
468 LOG_LMG <<
"creating findin filter";
469 int idx = lua_absindex(L, -1);
470 switch(lua_geti(L, idx, 2)) {
477 init_from_single_loc(luaL_checkinteger(L, -2), luaL_checkinteger(L, -1));
480 if(lua_geti(L, idx, 3) == LUA_TSTRING) {
481 init_from_ranges(luaL_checkstring(L, -2), luaL_checkstring(L, -1));
483 init_from_named_set(L, luaL_checkstring(L, -2), res_index, ks);
495 void init_from_single_loc(
int x,
int y) {
501 void init_from_ranges(
const std::string& xs,
const std::string& ys) {
504 for(
size_t i = 0;
i < std::min(xvals.size(), yvals.size());
i++) {
505 for(
int x = xvals[
i].first; x <= xvals[
i].second; x++) {
506 for(
int y = yvals[
i].first; y <= yvals[
i].second; y++) {
514 void init_from_named_set(lua_State* L,
const std::string&
id,
int res_index,
known_sets_t& ks) {
516 auto insert_res = ks.insert(known_sets_t::value_type{
id, {}});
517 if(insert_res.second && res_index > 0) {
524 set_ = &insert_res.first->second;
530 return set_->find(l) != set_->end();
542 radius_filter(lua_State* L,
int res_index,
known_sets_t& ks)
547 LOG_LMG <<
"creating radius filter";
549 filter_radius_ = build_filter(L, res_index, ks);
553 radius_ = lua_tointeger(L, -1);
556 filter_ = build_filter(L, res_index, ks);
563 std::set<map_location> result;
570 return !filter_radius_ || filter_radius_->matches(m, l);
575 if(!filter_ || filter_->matches(m, lr)) {
583 std::unique_ptr<filter_impl> filter_radius_;
584 std::unique_ptr<filter_impl> filter_;
593 LOG_LMG <<
"creating formula filter";
604 return (formula_.get() !=
nullptr) && formula_->evaluate(callable).as_bool();
606 ERR_LMG <<
"Formula error: " <<
e.type <<
" at " <<
e.filename <<
':' <<
e.line <<
")";
614 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 };
616 static const std::unordered_map<std::string, filter_keys> keys {
619 {
"not_all", F_NAND },
623 {
"find_in", F_FIND_IN },
624 {
"adjacent", F_ADJACENT },
625 {
"terrain", F_TERRAIN },
626 {
"cached", F_CACHED },
627 {
"formula", F_FORMULA },
628 {
"onborder", F_ONBORDER },
629 {
"radius", F_RADIUS }
632 std::unique_ptr<filter_impl> build_filter(lua_State* L,
int res_index,
known_sets_t& ks)
634 LOG_LMG <<
"buildfilter: start";
635 if(!lua_istable(L, -1)) {
638 lua_rawgeti(L, -1, 1);
640 LOG_LMG <<
"buildfilter: got: " <<
s;
641 auto it = keys.find(
s);
642 if(it == keys.end()) {
646 auto key = it->second;
651 return std::make_unique<and_filter>(L, res_index, ks);
653 return std::make_unique<or_filter>(L, res_index, ks);
655 return std::make_unique<nand_filter>(L, res_index, ks);
657 return std::make_unique<nor_filter>(L, res_index, ks);
659 return std::make_unique<x_filter>(L, res_index, ks);
661 return std::make_unique<y_filter>(L, res_index, ks);
663 return std::make_unique<findin_filter>(L, res_index, ks);
665 return std::make_unique<adjacent_filter>(L, res_index, ks);
667 return std::make_unique<terrain_filter>(L, res_index, ks);
669 return std::make_unique<radius_filter>(L, res_index, ks);
671 return std::make_unique<cached_filter>(L, res_index, ks);
673 return std::make_unique<formula_filter>(L, res_index, ks);
675 return std::make_unique<onborder_filter>(L, res_index, ks);
677 throw "invalid filter key enum";
692 LOG_LMG <<
"creating filter object";
693 lua_pushvalue (L, data_index);
696 LOG_LMG <<
"finished creating filter object";
702 return impl_->matches(m, l);
714 LOG_LMG <<
"map:get_locations";
718 LOG_LMG <<
"map:get_locations vaidargs";
719 if(!lua_isnone(L, 3)) {
720 LOG_LMG <<
"map:get_locations some locations";
722 LOG_LMG <<
"map:get_locations #args = " <<
s.size();
724 if(
f->matches(m, l)) {
730 LOG_LMG <<
"map:get_locations all locations";
732 if(
f->matches(m, l)) {
737 LOG_LMG <<
"map:get_locations #res = " << res.size();
739 LOG_LMG <<
"map:get_locations end";
748 int r = luaL_checkinteger(L, 3);
756 return f->matches(m, l);
785 if(allow_compile && lua_istable(L,
index)) {
786 auto f = std::make_unique<lua_mapgen::filter>(L,
index, 0);
790 throw "luaW_type_error didn't throw";
798 template<
typename... T>
801 LOG_LMG <<
"luaW_push_mgfilter";
814 if(!lua_istable(L, 1)) {
815 return luaL_argerror(L, 1,
"table expected");
817 if(lua_istable(L, 2)) {
825 return luaL_argerror(L, 1,
e.what());
851 char const *m = luaL_checkstring(L, 2);
852 std::string err_msg =
"unknown modifiable property of map: ";
854 return luaL_argerror(L, 2, err_msg.c_str());
880 std::ostringstream cmd_out;
882 cmd_out <<
"Adding terrainmamap metatable...\n";
886 lua_setfield(L, -2,
"__gc");
888 lua_setfield(L, -2,
"__index");
890 lua_setfield(L, -2,
"__newindex");
891 lua_pushstring(L,
"terrain_filter");
892 lua_setfield(L, -2,
"__metatable");
895 lua_setfield(L, -2,
"clear_cache");
897 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