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