The Battle for Wesnoth  1.15.1+dev
lua_terrainfilter.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2018 the Battle for Wesnoth Project https://www.wesnoth.org/
3 
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or
7  (at your option) any later version.
8  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY.
10 
11  See the COPYING file for more details.
12 */
13 
16 
17 #include "formatter.hpp"
18 #include "log.hpp"
19 #include "map/location.hpp"
20 #include "map/map.hpp"
21 #include "pathutils_impl.hpp"
22 #include "scripting/lua_common.hpp"
23 #include "scripting/push_check.hpp"
25 
26 #include "formula/callable_objects.hpp"
27 #include "formula/formula.hpp"
28 #include "formula/string_utils.hpp"
29 
30 #include <boost/dynamic_bitset.hpp>
31 #include <boost/range/iterator_range.hpp>
32 #include <unordered_map>
33 #include "lua/lauxlib.h"
34 #include "lua/lua.h"
35 
36 static lg::log_domain log_scripting_lua_mapgen("scripting/lua/mapgen");
37 #define LOG_LMG LOG_STREAM(info, log_scripting_lua_mapgen)
38 #define ERR_LMG LOG_STREAM(err, log_scripting_lua_mapgen)
39 //general helper functions for parsing
40 
41 struct inalid_lua_argument : public std::exception
42 {
43  explicit inalid_lua_argument(const std::string& msg) : errormessage_(msg) {}
44  const char* what() const noexcept { return errormessage_.c_str(); }
45 
46 private:
47  std::string errormessage_;
48 };
49 
50 using knows_sets_t = std::map<std::string, std::set<map_location>>;
51 using offset_list_t = std::vector<std::pair<int, int>>;
52 using utils::string_view;
53 using dynamic_bitset = boost::dynamic_bitset<>;
54 using location_set = std::set<map_location>;
55 
56 static const char terrinfilterKey[] = "terrainfilter";
57 #define LOG_MATCHES(NAME) \
58 LOG_LMG << #NAME << ":matches(" << l << ") line:" << __LINE__ << "\n";
59 namespace utils {
60  // todoc++14: use std::make_unique
61  template<typename T, typename... Args>
62  std::unique_ptr<T> make_unique(Args&&... args)
63  {
64  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
65  }
66 
67 }
68 //helper functions for parsing
69 namespace {
70  bool iswhitespace(char c)
71  {
72  return c == ' ' || c == '\t' || c == '\n' || c == '\r';
73  }
74 
75  void trim(string_view& s)
76  {
77  while(!s.empty() && iswhitespace(s.front())) {
78  s.remove_prefix(1);
79  }
80  while(!s.empty() && iswhitespace(s.back())) {
81  s.remove_suffix(1);
82  }
83  }
84 
85  template<typename F>
86  void split_foreach(string_view s, char sep, const F& f)
87  {
88  if(s.empty()) {
89  return;
90  }
91  while(true)
92  {
93  int partend = s.find(sep);
94  if(partend == int(string_view::npos)) {
95  break;
96  }
97  f(s.substr(0, partend));
98  s.remove_prefix(partend + 1);
99  }
100  f(s);
101  }
102 
103  int atoi(string_view s)
104  {
105  if(s.empty()) {
106  return 0;
107  }
108 
109  char** end = 0;
110  int res = strtol(&s[0], end, 10);
111  return res;
112  }
113 
114  std::pair<int, int> parse_single_range(string_view s)
115  {
116  int dash_pos = s.find('-');
117  if(dash_pos == int(string_view::npos)) {
118  int res = atoi(s);
119  return {res, res};
120  }
121  else {
122  string_view first = s.substr(0, dash_pos);
123  string_view second = s.substr(dash_pos + 1);
124  return {atoi(first), atoi(second)};
125  }
126  }
127 
129  {
130  dynamic_bitset res;
131  split_foreach(s, ',', [&](string_view part){
132  trim(part);
133  auto pair = parse_single_range(part);
134  int m = std::max(pair.first, pair.second);
135  if(m >= int(res.size())) {
136  res.resize(m + 1);
137  for(int i = pair.first; i <= pair.second; ++i) {
138  res[i] = true;
139  }
140  }
141  });
142  return res;
143  }
144  void parse_rel(string_view str, offset_list_t& even, offset_list_t& odd)
145  {
146  //sw = 1*s -1*se
147  //nw = -1*se
148  //ne = 1*se - 1*s
149  int s = 0;
150  int se = 0;
151  bool last_was_n = false;
152  while(!str.empty()) {
153  switch(str.front()) {
154  case 'n':
155  --s;
156  last_was_n = true;
157  break;
158  case 's':
159  ++s;
160  last_was_n = false;
161  break;
162  case 'e':
163  ++se;
164  if(!last_was_n) {
165  --s;
166  }
167  break;
168  case 'w':
169  --se;
170  if(last_was_n) {
171  ++s;
172  }
173  break;
174  default:
175  break;
176  }
177  str.remove_prefix(1);
178  }
179  if((se & 2) == 0) {
180  odd.emplace_back(se, s + se/2);
181  even.emplace_back(se, s + se/2);
182  }
183  else {
184  odd.emplace_back(se, s + (se - 1)/2);
185  even.emplace_back(se, s + (se + 1)/2);
186  }
187  }
188 
189  void parse_rel_sequence(string_view s, offset_list_t& even, offset_list_t& odd)
190  {
191  split_foreach(s, ',', [&](string_view part){
192  trim(part);
193  parse_rel(part, even, odd);
194  });
195  }
196  /**
197  * TODO: move to a template header.
198  * Function that will add to @a result all elements of @a locs, plus all
199  * on-board locations matching @a pred that are connected to elements of
200  * locs by a chain of at most @a radius tiles, each of which matches @a pred.
201  * @a add_result a function that takes a location_range
202 */
203 
204 } //end namespace
205 
206 static int luaW_push_locationset(lua_State* L, const std::set<map_location>& locs)
207 {
208  LOG_LMG << "push_locationset\n";
209  lua_createtable(L, locs.size(), 0);
210  int i = 1;
211  for (const map_location& loc : locs)
212  {
213  lua_createtable(L, 2, 0);
214  lua_pushinteger(L, loc.wml_x());
215  lua_rawseti(L, -2, 1);
216  lua_pushinteger(L, loc.wml_y());
217  lua_rawseti(L, -2, 2);
218  lua_rawseti(L, -2, i);
219  ++i;
220  }
221  return 1;
222 
223 }
224 
225 static std::set<map_location> luaW_to_locationset(lua_State* L, int index)
226 {
227  std::set<map_location> res;
228  lua_pushvalue(L, index);
229  size_t len = lua_rawlen(L, -1);
230  for(size_t i = 0; i != len; ++i) {
231  lua_geti(L, -1, i + 1);
232  res.insert(luaW_checklocation(L, -1));
233  lua_pop(L, 1);
234  }
235  lua_pop(L, 1);
236  return res;
237 }
238 
240 {
241 public:
243  virtual bool matches(const mapgen_gamemap& m, map_location l) = 0;
244  virtual ~filter_impl() {};
245 };
246 
247 //build_filter impl
248 namespace {
249 
250 std::unique_ptr<filter_impl> build_filter(lua_State* L, int res_index, knows_sets_t& ks);
251 
252 class con_filter : public filter_impl
253 {
254 public:
255  con_filter(lua_State* L, int res_index, knows_sets_t& ks)
256  :list_()
257  {
258  LOG_LMG << "creating con filter\n";
259  size_t len = lua_rawlen(L, -1);
260  for(size_t i = 1; i != len; ++i) {
261  lua_geti(L, -1, i + 1);
262  list_.emplace_back(build_filter(L, res_index, ks));
263  lua_pop(L, 1);
264  }
265  }
266  std::vector<std::unique_ptr<filter_impl>> list_;
267 };
268 
269 class and_filter : public con_filter
270 {
271 public:
272  and_filter(lua_State* L, int res_index, knows_sets_t& ks)
273  : con_filter(L, res_index, ks)
274  {
275  LOG_LMG << "created and filter\n";
276  }
277 
278  bool matches(const mapgen_gamemap& m, map_location l) override
279  {
280  LOG_MATCHES(and);
281  for(const auto& pfilter : list_) {
282  if(!pfilter->matches(m, l)) {
283  return false;
284  }
285  }
286  return true;
287  }
288 };
289 
290 class or_filter : public con_filter
291 {
292 public:
293  or_filter(lua_State* L, int res_index, knows_sets_t& ks)
294  : con_filter(L, res_index, ks)
295  {
296  LOG_LMG << "created or filter\n";
297  }
298 
299  bool matches(const mapgen_gamemap& m, map_location l) override
300  {
301  LOG_MATCHES(or);
302  for(const auto& pfilter : list_) {
303  if(pfilter->matches(m, l)) {
304  return true;
305  }
306  }
307  return false;
308  }
309 };
310 
311 class nand_filter : public con_filter
312 {
313 public:
314  nand_filter(lua_State* L, int res_index, knows_sets_t& ks)
315  : con_filter(L, res_index, ks)
316  {
317  LOG_LMG << "created nand filter\n";
318  }
319 
320  bool matches(const mapgen_gamemap& m, map_location l) override
321  {
322  LOG_MATCHES(nand);
323  for(const auto& pfilter : list_) {
324  if(!pfilter->matches(m, l)) {
325  return true;
326  }
327  }
328  return false;
329  }
330 };
331 
332 class nor_filter : public con_filter
333 {
334 public:
335  nor_filter(lua_State* L, int res_index, knows_sets_t& ks)
336  : con_filter(L, res_index, ks)
337  {
338  LOG_LMG << "created nor filter\n";
339  }
340 
341  bool matches(const mapgen_gamemap& m, map_location l) override
342  {
343  LOG_MATCHES(nor);
344  for(const auto& pfilter : list_) {
345  if(pfilter->matches(m, l)) {
346  return false;
347  }
348  }
349  return true;
350  }
351 };
352 
353 class cached_filter : public filter_impl
354 {
355 public:
356  cached_filter(lua_State* L, int res_index, knows_sets_t& ks)
357  : filter_()
358  , cache_()
359  {
360  LOG_LMG << "creating cached filter\n";
361  lua_geti(L, -1, 2);
362  filter_ = build_filter(L, res_index, ks);
363  lua_pop(L, 1);
364  }
365 
366  bool matches(const mapgen_gamemap& m, map_location l) override
367  {
368  LOG_MATCHES(cached);
369  int cache_size = 2 * m.total_width() * m.total_height();
370  int loc_index = 2 * (l.wml_x() + l.wml_y() * m.total_width());
371 
372  if(int(cache_.size()) != cache_size) {
373  cache_ = dynamic_bitset(cache_size);
374  }
375  if(cache_[loc_index]) {
376  return cache_[loc_index + 1];
377  }
378  else {
379  bool res = filter_->matches(m, l);
380  cache_[loc_index] = true;
381  cache_[loc_index + 1] = res;
382  return res;
383  }
384  }
385 
386  std::unique_ptr<filter_impl> filter_;
387  mutable dynamic_bitset cache_;
388 };
389 
390 class x_filter : public filter_impl
391 {
392 public:
393  x_filter(lua_State* L, int /*res_index*/, knows_sets_t&)
394  : filter_()
395  {
396  LOG_LMG << "creating x filter\n";
397  lua_geti(L, -1, 2);
398  filter_ = parse_range(luaW_tostring(L, -1));
399  lua_pop(L, 1);
400  }
401  bool matches(const mapgen_gamemap&, map_location l) override
402  {
403  LOG_MATCHES(x);
404  return l.x >= 0 && l.x < int(filter_.size()) && filter_[l.x];
405  }
406  dynamic_bitset filter_;
407 };
408 
409 class y_filter : public filter_impl
410 {
411 public:
412  y_filter(lua_State* L, int /*res_index*/, knows_sets_t&)
413  : filter_()
414  {
415  LOG_LMG << "creating y filter\n";
416  lua_geti(L, -1, 2);
417  filter_ = parse_range(luaW_tostring(L, -1));
418  lua_pop(L, 1);
419  }
420 
421  bool matches(const mapgen_gamemap&, map_location l) override
422  {
423  LOG_MATCHES(y);
424  return l.y >= 0 && l.y < int(filter_.size()) && filter_[l.y];
425  }
426 
427  dynamic_bitset filter_;
428 };
429 
430 class onborder_filter : public filter_impl
431 {
432 public:
433  onborder_filter(lua_State*, int /*res_index*/, knows_sets_t&)
434  {
435  LOG_LMG << "creating onborder filter\n";
436  }
437 
438  bool matches(const mapgen_gamemap& m, map_location l) override
439  {
440  LOG_MATCHES(onborder);
441  return !m.on_map_noborder(l);
442  }
443 };
444 
445 class terrain_filter : public filter_impl
446 {
447 public:
448  terrain_filter(lua_State* L, int /*res_index*/, knows_sets_t&)
449  : filter_()
450  {
451  LOG_LMG << "creating terrain filter\n";
452  lua_geti(L, -1, 2);
453  //fixme: use string_view
454  filter_ = t_translation::ter_match(luaW_tostring(L, -1));
455  lua_pop(L, 1);
456  }
457 
458  bool matches(const mapgen_gamemap& m, map_location l) override
459  {
461  const t_translation::terrain_code letter = m[l];
462  return t_translation::terrain_matches(letter, filter_);
463  }
464 
465  t_translation::ter_match filter_;
466 };
467 
468 static const offset_list_t even_offsets_default = {{1 , 0}, {1 , 1}, {0 , 1}, {-1 , 1}, {-1 , 0}, {0, -1}};
469 static const offset_list_t odd_offsets_default = {{1 , -1}, {1 , 0}, {0 , 1}, {-1 , 0}, {-1 , -1}, {0, -1}};
470 
471 class adjacent_filter : public filter_impl
472 {
473 public:
474  adjacent_filter(lua_State* L, int res_index, knows_sets_t& ks)
475  : filter_()
476  {
477  LOG_LMG << "creating adjacent filter\n";
478  if(luaW_tableget(L, -1, "adjacent")) {
479  parse_rel_sequence(luaW_tostring(L, -1), even_offsets_, odd_offsets_);
480  lua_pop(L, 1);
481  }
482  else {
483  even_offsets_ = even_offsets_default;
484  odd_offsets_ = odd_offsets_default;
485  }
486  if(luaW_tableget(L, -1, "count")) {
487  accepted_counts_ = parse_range(luaW_tostring(L, -1));
488  lua_pop(L, 1);
489  }
490  lua_geti(L, -1, 2);
491  filter_ = build_filter(L, res_index, ks);
492  lua_pop(L, 1);
493  }
494 
495  bool matches(const mapgen_gamemap& m, map_location l) override
496  {
497  LOG_MATCHES(adjacent);
498  int count = 0;
499  // is_odd == is_even in wml coordinates.
500  offset_list_t& offsets = (l.wml_x() & 1) ? odd_offsets_ : even_offsets_;
501  for(const auto& offset : offsets) {
502  map_location ad = {l.x + offset.first, l.y + offset.second};
503  if(m.on_map(ad) && filter_->matches(m, ad)) {
504  if(accepted_counts_.size() == 0) {
505  return true;
506  }
507  ++count;
508  }
509  }
510  return int(accepted_counts_.size()) > count && accepted_counts_[count];
511  }
512  offset_list_t even_offsets_;
513  offset_list_t odd_offsets_;
514  dynamic_bitset accepted_counts_;
515  std::unique_ptr<filter_impl> filter_;
516 };
517 
518 class findin_filter : public filter_impl
519 {
520 public:
521  findin_filter(lua_State* L, int res_index, knows_sets_t& ks)
522  : set_(nullptr)
523  {
524  LOG_LMG << "creating findin filter\n";
525  lua_geti(L, -1, 2);
526  std::string id = std::string(luaW_tostring(L, -1));
527  lua_pop(L, 1);
528 
529  //TODO: c++14: use heterogenous lookup.
530  auto insert_res = ks.insert(knows_sets_t::value_type{id, {}});
531  if(insert_res.second && res_index > 0) {
532  // istable(L, res_index) was already checked.
533  if(luaW_tableget(L, res_index, id.c_str())) {
534  insert_res.first->second = luaW_to_locationset(L, -1);
535  lua_pop(L, 1);
536  }
537  }
538  set_ = &insert_res.first->second;
539  }
540  bool matches(const mapgen_gamemap&, map_location l) override
541  {
542  LOG_MATCHES(findin);
543  if(set_) {
544  return set_->find(l) != set_->end();
545  }
546  return false;
547  }
548  const location_set* set_;
549 };
550 
551 class radius_filter : public filter_impl
552 {
553 public:
554 
555  radius_filter(lua_State* L, int res_index, knows_sets_t& ks)
556  : radius_()
557  , filter_radius_()
558  , filter_()
559  {
560  LOG_LMG << "creating radius filter\n";
561  if(luaW_tableget(L, -1, "filter_radius")) {
562  filter_radius_ = build_filter(L, res_index, ks);
563  lua_pop(L, 1);
564  }
565  lua_geti(L, -1, 2);
566  radius_ = lua_tonumber(L, -1);
567  lua_pop(L, 1);
568  lua_geti(L, -1, 3);
569  filter_ = build_filter(L, res_index, ks);
570  lua_pop(L, 1);
571  }
572 
573  bool matches(const mapgen_gamemap& m, map_location l) override
574  {
575  LOG_MATCHES(radius);
576  std::set<map_location> result;
577 
578  get_tiles_radius({{ l }}, radius_, result,
579  [&](const map_location& l) {
580  return m.on_map(l);
581  },
582  [&](const map_location& l) {
583  return !filter_radius_ || filter_radius_->matches(m, l);
584  }
585  );
586 
587  for (map_location lr : result) {
588  if(!filter_ || filter_->matches(m, lr)) {
589  return true;
590  }
591  }
592  return false;
593  }
594 
595  int radius_;
596  std::unique_ptr<filter_impl> filter_radius_;
597  std::unique_ptr<filter_impl> filter_;
598 };
599 
600 class formula_filter : public filter_impl
601 {
602 public:
603  formula_filter(lua_State* L, int, knows_sets_t&)
604  : formula_()
605  {
606  LOG_LMG << "creating formula filter\n";
607  lua_geti(L, -1, 2);
608  std::string code = std::string(luaW_tostring(L, -1));
609  lua_pop(L, 1);
610 
611  try {
612  formula_ = utils::make_unique<wfl::formula>(code);
613  } catch(const wfl::formula_error& e) {
614  ERR_LMG << "formula error" << e.what() << "\n";
615  }
616  }
617  bool matches(const mapgen_gamemap&, map_location l) override
618  {
619  LOG_MATCHES(formula);
620  try {
621  const wfl::location_callable callable1(l);
622  wfl::map_formula_callable callable(callable1.fake_ptr());
623  return (formula_.get() != nullptr) && formula_->evaluate(callable).as_bool();
624  } catch(const wfl::formula_error& e) {
625  ERR_LMG << "Formula error: " << e.type << " at " << e.filename << ':' << e.line << ")\n";
626  return false;
627  }
628  }
629  std::unique_ptr<wfl::formula> formula_;
630 };
631 
632 // todo: maybe invent a gerneral macro for this string_switch implementation.
633 enum filter_keys { F_AND, F_OR, F_NAND, F_NOR, F_X, F_Y, F_FIND_IN, F_ADJACENT, F_TERRAIN, F_RADUIS, F_FORMULA, F_CACHED };
634 //todoc++14: std::unordered_map doesn'tsupport herterogrnous lookup.
635 //todo consider renaming and -> all ,or ->any, nor -> none, nand -> notall
636 static const std::unordered_map<std::string, filter_keys> keys {
637  { "all", F_AND },
638  { "any", F_OR },
639  { "not_all", F_NAND },
640  { "none", F_NOR },
641  { "x", F_X },
642  { "y", F_Y },
643  { "find_in", F_FIND_IN },
644  { "adjacent", F_ADJACENT },
645  { "terrain", F_TERRAIN },
646  { "cached", F_CACHED },
647  { "formula", F_FORMULA },
648  { "radius", F_RADUIS }
649 };
650 
651 std::unique_ptr<filter_impl> build_filter(lua_State* L, int res_index, knows_sets_t& ks)
652 {
653  LOG_LMG << "buildfilter: start\n";
654  if(!lua_istable(L, -1)) {
655  throw inalid_lua_argument("buildfilter: expected table");
656  }
657  lua_rawgeti(L, -1, 1);
658  std::string s = std::string(luaW_tostring(L, -1));
659  LOG_LMG << "buildfilter: got: " << s << "\n";
660  auto it = keys.find(s);
661  if(it == keys.end()) {
662  //fixme use proper exception type.
663  throw inalid_lua_argument(std::string("buildfilter: invalid filter type ") + s);
664  }
665  auto key = it->second;
666  lua_pop(L, 1);
667  switch(key)
668  {
669  case F_AND:
670  return utils::make_unique<and_filter>(L, res_index, ks);
671  case F_OR:
672  return utils::make_unique<or_filter>(L, res_index, ks);
673  case F_NAND:
674  return utils::make_unique<nand_filter>(L, res_index, ks);
675  case F_NOR:
676  return utils::make_unique<nor_filter>(L, res_index, ks);
677  case F_X:
678  return utils::make_unique<x_filter>(L, res_index, ks);
679  case F_Y:
680  return utils::make_unique<y_filter>(L, res_index, ks);
681  case F_FIND_IN:
682  return utils::make_unique<findin_filter>(L, res_index, ks);
683  case F_ADJACENT:
684  return utils::make_unique<adjacent_filter>(L, res_index, ks);
685  case F_TERRAIN:
686  return utils::make_unique<terrain_filter>(L, res_index, ks);
687  case F_RADUIS:
688  return utils::make_unique<radius_filter>(L, res_index, ks);
689  case F_CACHED:
690  return utils::make_unique<cached_filter>(L, res_index, ks);
691  case F_FORMULA:
692  return utils::make_unique<formula_filter>(L, res_index, ks);
693  default:
694  throw "invalid filter key enum";
695  }
696 }
697 }
698 
699 //////////////// PUBLIC API ////////////////
700 
701 namespace lua_mapgen {
702 /// @a data_index a index to the lua stack pointing to the lua table that describes the filter.
703 /// @a res_index a _positive_ index to the lua stack pointing to the lua table that describes the filter resources.
704 filter::filter(lua_State* L, int data_index, int res_index)
705 {
706  LOG_LMG << "creating filter object\n";
707  lua_pushvalue (L, data_index);
708  impl_ = build_filter(L, res_index, known_sets_);
709  lua_pop(L, 1);
710  LOG_LMG << "finished creating filter object\n";
711 }
712 
713 bool filter::matches(const mapgen_gamemap& m, map_location l)
714 {
715  log_scope("filter::matches");
716  return impl_->matches(m, l);
717 }
718 
719 filter::~filter()
720 {
721 
722 }
723 
724 }
725 
727 {
728  location_set res;
729  LOG_LMG << "map:get_locations vaidargs\n";
730  if(lua_istable(L, 3)) {
731  LOG_LMG << "map:get_locations some locations\n";
733  LOG_LMG << "map:get_locations #args = " << s.size() << "\n";
734  for (const map_location& l : s) {
735  if(f.matches(m, l)) {
736  res.insert(l);
737  }
738  }
739  }
740  else {
741  LOG_LMG << "map:get_locations all locations\n";
742  m.for_each_loc([&](map_location l) {
743  if(f.matches(m, l)) {
744  res.insert(l);
745  }
746  });
747  }
748  LOG_LMG << "map:get_locations #res = " << res.size() << "\n";
749  luaW_push_locationset(L, res);
750  LOG_LMG << "map:get_locations end\n";
751  return 1;
752 
753 }
755 {
756  //todo: create filter form table if needed
757  LOG_LMG << "map:get_locations\n";
759  if(luaW_is_mgfilter(L, 2)) {
761  return intf_mg_get_locations_part2(L, m, f);
762  }
763  else if (lua_istable(L, 2)) {
764  lua_mapgen::filter f(L, 2, 0);
765  return intf_mg_get_locations_part2(L, m, f);
766  }
767  else {
768  return luaW_type_error(L, 2, "terrainfilter");
769  }
770 }
771 
773 {
777  location_set res;
778  int r = luaL_checkinteger(L, 4);
779  get_tiles_radius(std::move(s), r, res,
780  [&](const map_location& l) {
781  return m.on_map(l);
782  },
783  [&](const map_location& l) {
784  return f.matches(m, l);
785  }
786  );
787  luaW_push_locationset(L, res);
788  return 1;
789 }
790 
792 {
793  return luaL_testudata(L, index, terrinfilterKey) != nullptr;
794 }
795 
796 
798 {
799  if(luaW_is_mgfilter(L, index)) {
800  return static_cast<lua_mapgen::filter*>(lua_touserdata(L, index));
801  }
802  return nullptr;
803 }
804 
806 {
807  if(luaW_is_mgfilter(L, index)) {
808  return *static_cast<lua_mapgen::filter*>(lua_touserdata(L, index));
809  }
810  luaW_type_error(L, index, "terrainfilter");
811  throw "luaW_type_error didn't throw";
812 }
813 
815 {
817 }
818 
819 template<typename... T>
821 {
822  LOG_LMG << "luaW_push_mgfilter\n";
823  lua_mapgen::filter* res = new(L) lua_mapgen::filter(std::forward<T>(params)...);
825  return res;
826 }
827 
828 /**
829  * Create a filter.
830 */
832 {
833  try {
834  int res_index = 0;
835  if(!lua_istable(L, 1)) {
836  return luaL_argerror(L, 1, "table expected");
837  }
838  if(lua_istable(L, 2)) {
839  res_index = 2;
840  }
841  lua_mapgen::filter res(L, 1, res_index);
842  luaW_push_mgfilter(L, std::move(res));
843  return 1;
844  }
845  catch(const inalid_lua_argument& e) {
846  return luaL_argerror(L, 1, e.what());
847  }
848 }
849 
850 
851 /**
852  * Gets some data on a filter (__index metamethod).
853  * - Arg 1: full userdata containing the filter.
854  * - Arg 2: string containing the name of the property.
855  * - Ret 1: something containing the attribute.
856  */
858 {
860  UNUSED(f);
861  return 0;
862 }
863 
864 /**
865  * Sets some data on a filter (__newindex metamethod).
866  * - Arg 1: full userdata containing the filter.
867  * - Arg 2: string containing the name of the property.
868  * - Arg 3: something containing the attribute.
869  */
871 {
873  UNUSED(f);
874  char const *m = luaL_checkstring(L, 2);
875  std::string err_msg = "unknown modifiable property of map: ";
876  err_msg += m;
877  return luaL_argerror(L, 2, err_msg.c_str());
878 }
879 
880 
881 /**
882  * Clears the cache of a filter.
883  */
885 {
887  UNUSED(f);
888  return 0;
889 }
890 /**
891  * Destroys a map object before it is collected (__gc metamethod).
892  */
894 {
896  f.~filter();
897  return 0;
898 }
899 
900 
901 namespace lua_terrainfilter {
903  {
904  std::ostringstream cmd_out;
905 
906  cmd_out << "Adding terrainmamap metatable...\n";
907 
910  lua_setfield(L, -2, "__gc");
912  lua_setfield(L, -2, "__index");
914  lua_setfield(L, -2, "__newindex");
915  lua_pushstring(L, "terrain_filter");
916  lua_setfield(L, -2, "__metatable");
917  // terainmap methods
919  lua_setfield(L, -2, "clear_cache");
920 
921  return cmd_out.str();
922  }
923 }
bool luaW_tableget(lua_State *L, int index, const char *key)
Definition: lua_common.cpp:946
BOOST_CXX14_CONSTEXPR void remove_suffix(size_type n)
LUA_API void lua_createtable(lua_State *L, int narray, int nrec)
Definition: lapi.cpp:684
#define lua_pushcfunction(L, f)
Definition: lua.h:350
utils::string_view luaW_tostring(lua_State *L, int index)
Definition: lua_common.cpp:961
BOOST_CXX14_CONSTEXPR size_type find(basic_string_view s, size_type pos=0) const BOOST_NOEXCEPT
std::pair< int, int > parse_range(const std::string &str)
bool matches(const mapgen_gamemap &m, map_location l)
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_API int lua_rawgeti(lua_State *L, int idx, lua_Integer n)
Definition: lapi.cpp:658
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
std::string filename
Definition: formula.hpp:107
LUA_API void lua_rawseti(lua_State *L, int idx, lua_Integer n)
Definition: lapi.cpp:817
BOOST_CXX14_CONSTEXPR void remove_prefix(size_type n)
BOOST_CXX14_CONSTEXPR basic_string_view substr(size_type pos, size_type n=npos) const
int wml_x() const
Definition: location.hpp:157
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
Definition: translation.hpp:50
std::string register_metatables(lua_State *L)
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:109
#define lua_tonumber(L, i)
Definition: lua.h:341
LUALIB_API void luaL_setmetatable(lua_State *L, const char *tname)
Definition: lauxlib.cpp:312
basic_string_view< char, std::char_traits< char > > string_view
map_location luaW_checklocation(lua_State *L, int index)
Converts an optional table or pair of integers to a map location object.
Definition: lua_common.cpp:704
static int luaW_push_locationset(lua_State *L, const std::set< map_location > &locs)
lua_mapgen::filter & luaW_check_mgfilter(lua_State *L, int index)
#define lua_pop(L, n)
Definition: lua.h:344
LUALIB_API int luaL_argerror(lua_State *L, int arg, const char *extramsg)
Definition: lauxlib.cpp:164
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)
static int intf_mg_get_locations_part2(lua_State *L, mapgen_gamemap &m, lua_mapgen::filter &f)
int intf_mg_get_tiles_radius(lua_State *L)
bool on_map(const map_location &loc) const
int wml_y() const
Definition: location.hpp:158
static int impl_terainfilter_get(lua_State *L)
Gets some data on a filter (__index metamethod).
std::string type
Definition: formula.hpp:105
boost::dynamic_bitset<> dynamic_bitset
inalid_lua_argument(const std::string &msg)
BOOST_CONSTEXPR bool empty() const BOOST_NOEXCEPT
void for_each_loc(const F &f) const
#define LOG_MATCHES(NAME)
static int impl_terainfilter_collect(lua_State *L)
Destroys a map object before it is collected (__gc metamethod).
std::unique_ptr< T > make_unique(Args &&... args)
static lg::log_domain log_scripting_lua_mapgen("scripting/lua/mapgen")
LUALIB_API void * luaL_testudata(lua_State *L, int ud, const char *tname)
Definition: lauxlib.cpp:318
const char * what() const noexcept
Definition: exceptions.hpp:37
static const ::config * terrain
The terrain used to create the cache.
Definition: minimap.cpp:130
static map_location::DIRECTION se
Encapsulates the map of the game.
Definition: location.hpp:42
#define UNUSED(x)
Definition: global.hpp:34
LUALIB_API int luaL_newmetatable(lua_State *L, const char *tname)
Definition: lauxlib.cpp:299
LUA_API void * lua_touserdata(lua_State *L, int idx)
Definition: lapi.cpp:413
bool luaW_is_mgfilter(lua_State *L, int index)
static std::set< map_location > luaW_to_locationset(lua_State *L, int index)
#define ERR_LMG
int intf_terainfilter_create(lua_State *L)
Create a filter.
std::size_t i
Definition: function.cpp:933
BOOST_CONSTEXPR const_reference back() const
formula_callable_ptr fake_ptr()
Definition: callable.hpp:41
LUALIB_API lua_Integer luaL_checkinteger(lua_State *L, int arg)
Definition: lauxlib.cpp:430
void lua_mgfilter_setmetatable(lua_State *L)
static map_location::DIRECTION s
void get_tiles_radius(const map_location &center, 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...
Definition: pathutils.cpp:69
pump_impl & impl_
Definition: pump.cpp:134
#define log_scope(description)
Definition: log.hpp:186
LUA_API void lua_pushvalue(lua_State *L, int idx)
Definition: lapi.cpp:237
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.
Definition: unicode.cpp:71
LUA_API size_t lua_rawlen(lua_State *L, int idx)
Definition: lapi.cpp:392
std::set< map_location > location_set
mapgen_gamemap & luaW_checkterrainmap(lua_State *L, int index)
int total_height() const
Real height of the map, including borders.
BOOST_CONSTEXPR const_reference front() const
#define f
virtual ~filter_impl()
#define lua_istable(L, n)
Definition: lua.h:353
static int impl_terainfilter_set(lua_State *L)
Sets some data on a filter (__newindex metamethod).
Standard logging facilities (interface).
LUA_API int lua_geti(lua_State *L, int idx, lua_Integer n)
Definition: lapi.cpp:628
bool on_map_noborder(const map_location &loc) const
#define e
int total_width() const
Real width of the map, including borders.
const char * what() const noexcept
mock_char c
LUA_API void lua_pushinteger(lua_State *L, lua_Integer n)
Definition: lapi.cpp:466
This structure can be used for matching terrain strings.
int intf_mg_get_locations(lua_State *L)
LUA_API const char * lua_pushstring(lua_State *L, const char *s)
Definition: lapi.cpp:491
LUA_API void lua_setfield(lua_State *L, int idx, const char *k)
Definition: lapi.cpp:777
#define LOG_LMG
#define luaL_checkstring(L, n)
Definition: lauxlib.h:124