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