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