29 #include "formula/callable_objects.hpp" 33 #include <boost/dynamic_bitset.hpp> 34 #include <boost/range/iterator_range.hpp> 35 #include <unordered_map> 39 #define LOG_LMG LOG_STREAM(info, log_scripting_lua_mapgen) 40 #define ERR_LMG LOG_STREAM(err, log_scripting_lua_mapgen) 52 using knows_sets_t = std::map<std::string, std::set<map_location>>;
54 using std::string_view;
59 #define LOG_MATCHES(NAME) \ 60 LOG_LMG << #NAME << ":matches(" << l << ") line:" << __LINE__; 64 int atoi(string_view
s)
71 int res = strtol(&s[0], end, 10);
75 std::pair<int, int> parse_single_range(string_view s)
77 int dash_pos = s.find(
'-');
78 if(dash_pos ==
int(string_view::npos)) {
83 string_view first = s.substr(0, dash_pos);
84 string_view second = s.substr(dash_pos + 1);
85 return {atoi(first), atoi(second)};
93 auto pair = parse_single_range(part);
94 int m = std::max(pair.first, pair.second);
95 if(m >=
int(res.size())) {
97 for(
int i = pair.first;
i <= pair.second; ++
i) {
111 bool last_was_n =
false;
112 while(!str.empty()) {
113 switch(str.front()) {
137 str.remove_prefix(1);
140 odd.emplace_back(se, s + se/2);
141 even.emplace_back(se, s + se/2);
144 odd.emplace_back(se, s + (se - 1)/2);
145 even.emplace_back(se, s + (se + 1)/2);
152 parse_rel(part, even, odd);
167 std::set<map_location> res;
173 lua_pushvalue(L, index);
174 size_t len = lua_rawlen(L, -1);
175 for(
size_t i = 0;
i != len; ++
i) {
176 lua_geti(L, -1,
i + 1);
195 std::unique_ptr<filter_impl> build_filter(lua_State* L,
int res_index,
knows_sets_t& ks);
200 con_filter(lua_State* L,
int res_index,
knows_sets_t& ks)
203 LOG_LMG <<
"creating con filter";
204 size_t len = lua_rawlen(L, -1);
205 for(
size_t i = 1;
i != len; ++
i) {
206 lua_geti(L, -1,
i + 1);
207 list_.emplace_back(build_filter(L, res_index, ks));
211 std::vector<std::unique_ptr<filter_impl>> list_;
214 class and_filter :
public con_filter
217 and_filter(lua_State* L,
int res_index,
knows_sets_t& ks)
218 : con_filter(L, res_index, ks)
220 LOG_LMG <<
"created and filter";
226 for(
const auto& pfilter : list_) {
227 if(!pfilter->matches(m, l)) {
235 class or_filter :
public con_filter
238 or_filter(lua_State* L,
int res_index,
knows_sets_t& ks)
239 : con_filter(L, res_index, ks)
241 LOG_LMG <<
"created or filter";
247 for(
const auto& pfilter : list_) {
248 if(pfilter->matches(m, l)) {
256 class nand_filter :
public con_filter
259 nand_filter(lua_State* L,
int res_index,
knows_sets_t& ks)
260 : con_filter(L, res_index, ks)
262 LOG_LMG <<
"created nand filter";
268 for(
const auto& pfilter : list_) {
269 if(!pfilter->matches(m, l)) {
277 class nor_filter :
public con_filter
280 nor_filter(lua_State* L,
int res_index,
knows_sets_t& ks)
281 : con_filter(L, res_index, ks)
283 LOG_LMG <<
"created nor filter";
289 for(
const auto& pfilter : list_) {
290 if(pfilter->matches(m, l)) {
301 cached_filter(lua_State* L,
int res_index,
knows_sets_t& ks)
305 LOG_LMG <<
"creating cached filter";
307 filter_ = build_filter(L, res_index, ks);
317 if(
int(cache_.size()) != cache_size) {
320 if(cache_[loc_index]) {
321 return cache_[loc_index + 1];
324 bool res = filter_->matches(m, l);
325 cache_[loc_index] =
true;
326 cache_[loc_index + 1] = res;
331 std::unique_ptr<filter_impl> filter_;
341 LOG_LMG <<
"creating x filter";
349 const auto value = l.
wml_x();
350 return value >= 0 && value < int(filter_.size()) && filter_[value];
361 LOG_LMG <<
"creating y filter";
370 const auto value = l.
wml_y();
371 return value >= 0 && value < int(filter_.size()) && filter_[value];
382 LOG_LMG <<
"creating onborder filter";
398 LOG_LMG <<
"creating terrain filter";
415 static const offset_list_t even_offsets_default = {{1 , 0}, {1 , 1}, {0 , 1}, {-1 , 1}, {-1 , 0}, {0, -1}};
416 static const offset_list_t odd_offsets_default = {{1 , -1}, {1 , 0}, {0 , 1}, {-1 , 0}, {-1 , -1}, {0, -1}};
421 adjacent_filter(lua_State* L,
int res_index,
knows_sets_t& ks)
424 LOG_LMG <<
"creating adjacent filter";
426 parse_rel_sequence(
luaW_tostring(L, -1), even_offsets_, odd_offsets_);
430 even_offsets_ = even_offsets_default;
431 odd_offsets_ = odd_offsets_default;
438 filter_ = build_filter(L, res_index, ks);
448 for(
const auto& offset : offsets) {
451 if(accepted_counts_.size() == 0) {
457 return int(accepted_counts_.size()) > count && accepted_counts_[count];
462 std::unique_ptr<filter_impl> filter_;
468 findin_filter(lua_State* L,
int res_index,
knows_sets_t& ks)
471 LOG_LMG <<
"creating findin filter";
477 auto insert_res = ks.insert(knows_sets_t::value_type{
id, {}});
478 if(insert_res.second && res_index > 0) {
485 set_ = &insert_res.first->second;
491 return set_->find(l) != set_->end();
502 radius_filter(lua_State* L,
int res_index,
knows_sets_t& ks)
507 LOG_LMG <<
"creating radius filter";
509 filter_radius_ = build_filter(L, res_index, ks);
513 radius_ = lua_tointeger(L, -1);
516 filter_ = build_filter(L, res_index, ks);
523 std::set<map_location> result;
530 return !filter_radius_ || filter_radius_->matches(m, l);
535 if(!filter_ || filter_->matches(m, lr)) {
543 std::unique_ptr<filter_impl> filter_radius_;
544 std::unique_ptr<filter_impl> filter_;
553 LOG_LMG <<
"creating formula filter";
559 formula_ = std::make_unique<wfl::formula>(code);
570 return (formula_.get() !=
nullptr) && formula_->evaluate(callable).as_bool();
576 std::unique_ptr<wfl::formula> formula_;
580 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 };
582 static const std::unordered_map<std::string, filter_keys> keys {
585 {
"not_all", F_NAND },
589 {
"find_in", F_FIND_IN },
590 {
"adjacent", F_ADJACENT },
591 {
"terrain", F_TERRAIN },
592 {
"cached", F_CACHED },
593 {
"formula", F_FORMULA },
594 {
"radius", F_RADIUS }
597 std::unique_ptr<filter_impl> build_filter(lua_State* L,
int res_index,
knows_sets_t& ks)
599 LOG_LMG <<
"buildfilter: start";
600 if(!lua_istable(L, -1)) {
603 lua_rawgeti(L, -1, 1);
605 LOG_LMG <<
"buildfilter: got: " <<
s;
606 auto it = keys.find(s);
607 if(it == keys.end()) {
611 auto key = it->second;
616 return std::make_unique<and_filter>(L, res_index, ks);
618 return std::make_unique<or_filter>(L, res_index, ks);
620 return std::make_unique<nand_filter>(L, res_index, ks);
622 return std::make_unique<nor_filter>(L, res_index, ks);
624 return std::make_unique<x_filter>(L, res_index, ks);
626 return std::make_unique<y_filter>(L, res_index, ks);
628 return std::make_unique<findin_filter>(L, res_index, ks);
630 return std::make_unique<adjacent_filter>(L, res_index, ks);
632 return std::make_unique<terrain_filter>(L, res_index, ks);
634 return std::make_unique<radius_filter>(L, res_index, ks);
636 return std::make_unique<cached_filter>(L, res_index, ks);
638 return std::make_unique<formula_filter>(L, res_index, ks);
640 throw "invalid filter key enum";
653 filter::filter(lua_State* L,
int data_index,
int res_index)
655 LOG_LMG <<
"creating filter object";
656 lua_pushvalue (L, data_index);
657 impl_ = build_filter(L, res_index, known_sets_);
659 LOG_LMG <<
"finished creating filter object";
665 return impl_->matches(m, l);
678 LOG_LMG <<
"map:get_locations vaidargs";
679 if(lua_istable(L, 3)) {
680 LOG_LMG <<
"map:get_locations some locations";
682 LOG_LMG <<
"map:get_locations #args = " << s.size();
690 LOG_LMG <<
"map:get_locations all locations";
697 LOG_LMG <<
"map:get_locations #res = " << res.size();
699 LOG_LMG <<
"map:get_locations end";
706 LOG_LMG <<
"map:get_locations";
712 else if (lua_istable(L, 2)) {
725 int r = luaL_checkinteger(L, 3);
760 throw "luaW_type_error didn't throw";
768 template<
typename... T>
771 LOG_LMG <<
"luaW_push_mgfilter";
784 if(!lua_istable(L, 1)) {
785 return luaL_argerror(L, 1,
"table expected");
787 if(lua_istable(L, 2)) {
795 return luaL_argerror(L, 1, e.
what());
823 char const *m = luaL_checkstring(L, 2);
824 std::string err_msg =
"unknown modifiable property of map: ";
826 return luaL_argerror(L, 2, err_msg.c_str());
853 std::ostringstream cmd_out;
855 cmd_out <<
"Adding terrainmamap metatable...\n";
859 lua_setfield(L, -2,
"__gc");
861 lua_setfield(L, -2,
"__index");
863 lua_setfield(L, -2,
"__newindex");
864 lua_pushstring(L,
"terrain_filter");
865 lua_setfield(L, -2,
"__metatable");
868 lua_setfield(L, -2,
"clear_cache");
870 return cmd_out.str();
bool luaW_tableget(lua_State *L, int index, const char *key)
bool matches(const gamemap_base &m, map_location l)
std::pair< int, int > parse_range(const std::string &str)
std::map< std::string, std::set< map_location > > knows_sets_t
int luaW_type_error(lua_State *L, int narg, const char *tname)
static const char terrinfilterKey[]
lua_mapgen::filter * luaW_to_mgfilter(lua_State *L, int index)
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::vector< std::pair< int, int > > offset_list_t
gamemap_base & luaW_checkterrainmap(lua_State *L, int index)
const char * what() const noexcept
static int impl_terrainfilter_get(lua_State *L)
Gets some data on a filter (__index metamethod).
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
std::string register_metatables(lua_State *L)
void for_each_loc(const F &f) const
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
map_location luaW_checklocation(lua_State *L, int index)
Converts an optional table or pair of integers to a map location object.
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.
lua_mapgen::filter & luaW_check_mgfilter(lua_State *L, int index)
static int intf_clearcache(lua_State *L)
Clears the cache of a filter.
static lua_mapgen::filter * luaW_push_mgfilter(lua_State *L, T &&... params)
int intf_mg_get_tiles_radius(lua_State *L)
terrain_code get_terrain(const map_location &loc) const
Looks up terrain at a particular location.
bool on_board_with_border(const map_location &loc) const
boost::dynamic_bitset<> dynamic_bitset
int intf_terrainfilter_create(lua_State *L)
Create a filter.
REMOVE_EMPTY: remove empty elements.
#define LOG_MATCHES(NAME)
static lg::log_domain log_scripting_lua_mapgen("scripting/lua/mapgen")
const char * what() const noexcept
static map_location::DIRECTION se
Encapsulates the map of the game.
int total_width() const
Real width of the map, including borders.
std::string id
Text to match against addon_info.tags()
bool luaW_is_mgfilter(lua_State *L, int index)
static std::set< map_location > luaW_to_locationset(lua_State *L, int index)
void lua_mgfilter_setmetatable(lua_State *L)
static int impl_terrainfilter_set(lua_State *L)
Sets some data on a filter (__newindex metamethod).
static map_location::DIRECTION s
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...
#define log_scope(description)
std::string errormessage_
bool on_board(const map_location &loc) const
Tell if a location is on the map.
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.
int total_height() const
Real height of the map, including borders.
void split_foreach(std::string_view s, char sep, const int flags, const F &f)
std::set< map_location > location_set
std::string_view luaW_tostring(lua_State *L, int index)
Standard logging facilities (interface).
This structure can be used for matching terrain strings.
invalid_lua_argument(const std::string &msg)
static int impl_terrainfilter_collect(lua_State *L)
Destroys a map object before it is collected (__gc metamethod).
int intf_mg_get_locations(lua_State *L)
static int intf_mg_get_locations_part2(lua_State *L, gamemap_base &m, lua_mapgen::filter &f)
bool luaW_tolocation(lua_State *L, int index, map_location &loc)
Converts an optional table or pair of integers to a map location object.