The Battle for Wesnoth  1.19.0-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"
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 knows_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  lua_pushvalue(L, index);
170  size_t len = lua_rawlen(L, -1);
171  for(size_t i = 0; i != len; ++i) {
172  lua_geti(L, -1, i + 1);
173  res.insert(luaW_checklocation(L, -1));
174  lua_pop(L, 1);
175  }
176  lua_pop(L, 1);
177  return res;
178 }
179 
181 {
182 public:
184  virtual bool matches(const gamemap_base& m, map_location l) = 0;
185  virtual ~filter_impl() {};
186 };
187 
188 //build_filter impl
189 namespace {
190 
191 std::unique_ptr<filter_impl> build_filter(lua_State* L, int res_index, knows_sets_t& ks);
192 
193 class con_filter : public filter_impl
194 {
195 public:
196  con_filter(lua_State* L, int res_index, knows_sets_t& ks)
197  :list_()
198  {
199  LOG_LMG << "creating con filter";
200  size_t len = lua_rawlen(L, -1);
201  for(size_t i = 1; i != len; ++i) {
202  lua_geti(L, -1, i + 1);
203  list_.emplace_back(build_filter(L, res_index, ks));
204  lua_pop(L, 1);
205  }
206  }
207  std::vector<std::unique_ptr<filter_impl>> list_;
208 };
209 
210 class and_filter : public con_filter
211 {
212 public:
213  and_filter(lua_State* L, int res_index, knows_sets_t& ks)
214  : con_filter(L, res_index, ks)
215  {
216  LOG_LMG << "created and filter";
217  }
218 
219  bool matches(const gamemap_base& m, map_location l) override
220  {
221  LOG_MATCHES(and);
222  for(const auto& pfilter : list_) {
223  if(!pfilter->matches(m, l)) {
224  return false;
225  }
226  }
227  return true;
228  }
229 };
230 
231 class or_filter : public con_filter
232 {
233 public:
234  or_filter(lua_State* L, int res_index, knows_sets_t& ks)
235  : con_filter(L, res_index, ks)
236  {
237  LOG_LMG << "created or filter";
238  }
239 
240  bool matches(const gamemap_base& m, map_location l) override
241  {
242  LOG_MATCHES(or);
243  for(const auto& pfilter : list_) {
244  if(pfilter->matches(m, l)) {
245  return true;
246  }
247  }
248  return false;
249  }
250 };
251 
252 class nand_filter : public con_filter
253 {
254 public:
255  nand_filter(lua_State* L, int res_index, knows_sets_t& ks)
256  : con_filter(L, res_index, ks)
257  {
258  LOG_LMG << "created nand filter";
259  }
260 
261  bool matches(const gamemap_base& m, map_location l) override
262  {
263  LOG_MATCHES(nand);
264  for(const auto& pfilter : list_) {
265  if(!pfilter->matches(m, l)) {
266  return true;
267  }
268  }
269  return false;
270  }
271 };
272 
273 class nor_filter : public con_filter
274 {
275 public:
276  nor_filter(lua_State* L, int res_index, knows_sets_t& ks)
277  : con_filter(L, res_index, ks)
278  {
279  LOG_LMG << "created nor filter";
280  }
281 
282  bool matches(const gamemap_base& m, map_location l) override
283  {
284  LOG_MATCHES(nor);
285  for(const auto& pfilter : list_) {
286  if(pfilter->matches(m, l)) {
287  return false;
288  }
289  }
290  return true;
291  }
292 };
293 
294 class cached_filter : public filter_impl
295 {
296 public:
297  cached_filter(lua_State* L, int res_index, knows_sets_t& ks)
298  : filter_()
299  , cache_()
300  {
301  LOG_LMG << "creating cached filter";
302  lua_geti(L, -1, 2);
303  filter_ = build_filter(L, res_index, ks);
304  lua_pop(L, 1);
305  }
306 
307  bool matches(const gamemap_base& m, map_location l) override
308  {
309  LOG_MATCHES(cached);
310  int cache_size = 2 * m.total_width() * m.total_height();
311  int loc_index = 2 * (l.wml_x() + l.wml_y() * m.total_width());
312 
313  if(int(cache_.size()) != cache_size) {
314  cache_ = dynamic_bitset(cache_size);
315  }
316  if(cache_[loc_index]) {
317  return cache_[loc_index + 1];
318  }
319  else {
320  bool res = filter_->matches(m, l);
321  cache_[loc_index] = true;
322  cache_[loc_index + 1] = res;
323  return res;
324  }
325  }
326 
327  std::unique_ptr<filter_impl> filter_;
328  mutable dynamic_bitset cache_;
329 };
330 
331 class x_filter : public filter_impl
332 {
333 public:
334  x_filter(lua_State* L, int /*res_index*/, knows_sets_t&)
335  : filter_()
336  {
337  LOG_LMG << "creating x filter";
338  lua_geti(L, -1, 2);
339  filter_ = parse_range(luaW_tostring(L, -1));
340  lua_pop(L, 1);
341  }
342  bool matches(const gamemap_base&, map_location l) override
343  {
344  LOG_MATCHES(x);
345  const auto value = l.wml_x();
346  return value >= 0 && value < int(filter_.size()) && filter_[value];
347  }
348  dynamic_bitset filter_;
349 };
350 
351 class y_filter : public filter_impl
352 {
353 public:
354  y_filter(lua_State* L, int /*res_index*/, knows_sets_t&)
355  : filter_()
356  {
357  LOG_LMG << "creating y filter";
358  lua_geti(L, -1, 2);
359  filter_ = parse_range(luaW_tostring(L, -1));
360  lua_pop(L, 1);
361  }
362 
363  bool matches(const gamemap_base&, map_location l) override
364  {
365  LOG_MATCHES(y);
366  const auto value = l.wml_y();
367  return value >= 0 && value < int(filter_.size()) && filter_[value];
368  }
369 
370  dynamic_bitset filter_;
371 };
372 
373 class onborder_filter : public filter_impl
374 {
375 public:
376  onborder_filter(lua_State*, int /*res_index*/, knows_sets_t&)
377  {
378  LOG_LMG << "creating onborder filter";
379  }
380 
381  bool matches(const gamemap_base& m, map_location l) override
382  {
383  LOG_MATCHES(onborder);
384  return !m.on_board(l);
385  }
386 };
387 
388 class terrain_filter : public filter_impl
389 {
390 public:
391  terrain_filter(lua_State* L, int /*res_index*/, knows_sets_t&)
392  : filter_()
393  {
394  LOG_LMG << "creating terrain filter";
395  lua_geti(L, -1, 2);
396  //fixme: use string_view
397  filter_ = t_translation::ter_match(luaW_tostring(L, -1));
398  lua_pop(L, 1);
399  }
400 
401  bool matches(const gamemap_base& m, map_location l) override
402  {
403  LOG_MATCHES(terrain);
404  const t_translation::terrain_code letter = m.get_terrain(l);
405  return t_translation::terrain_matches(letter, filter_);
406  }
407 
408  t_translation::ter_match filter_;
409 };
410 
411 static const offset_list_t even_offsets_default = {{1 , 0}, {1 , 1}, {0 , 1}, {-1 , 1}, {-1 , 0}, {0, -1}};
412 static const offset_list_t odd_offsets_default = {{1 , -1}, {1 , 0}, {0 , 1}, {-1 , 0}, {-1 , -1}, {0, -1}};
413 
414 class adjacent_filter : public filter_impl
415 {
416 public:
417  adjacent_filter(lua_State* L, int res_index, knows_sets_t& ks)
418  : filter_()
419  {
420  LOG_LMG << "creating adjacent filter";
421  if(luaW_tableget(L, -1, "adjacent")) {
422  parse_rel_sequence(luaW_tostring(L, -1), even_offsets_, odd_offsets_);
423  lua_pop(L, 1);
424  }
425  else {
426  even_offsets_ = even_offsets_default;
427  odd_offsets_ = odd_offsets_default;
428  }
429  if(luaW_tableget(L, -1, "count")) {
430  accepted_counts_ = parse_range(luaW_tostring(L, -1));
431  lua_pop(L, 1);
432  }
433  lua_geti(L, -1, 2);
434  filter_ = build_filter(L, res_index, ks);
435  lua_pop(L, 1);
436  }
437 
438  bool matches(const gamemap_base& m, map_location l) override
439  {
440  LOG_MATCHES(adjacent);
441  int count = 0;
442  // is_odd == is_even in wml coordinates.
443  offset_list_t& offsets = (l.wml_x() & 1) ? odd_offsets_ : even_offsets_;
444  for(const auto& offset : offsets) {
445  map_location ad = {l.x + offset.first, l.y + offset.second};
446  if(m.on_board_with_border(ad) && filter_->matches(m, ad)) {
447  if(accepted_counts_.size() == 0) {
448  return true;
449  }
450  ++count;
451  }
452  }
453  return int(accepted_counts_.size()) > count && accepted_counts_[count];
454  }
455  offset_list_t even_offsets_;
456  offset_list_t odd_offsets_;
457  dynamic_bitset accepted_counts_;
458  std::unique_ptr<filter_impl> filter_;
459 };
460 
461 class findin_filter : public filter_impl
462 {
463 public:
464  findin_filter(lua_State* L, int res_index, knows_sets_t& ks)
465  : set_(nullptr)
466  {
467  LOG_LMG << "creating findin filter";
468  lua_geti(L, -1, 2);
469  std::string id = std::string(luaW_tostring(L, -1));
470  lua_pop(L, 1);
471 
472  //TODO: c++14: use heterogenous lookup.
473  auto insert_res = ks.insert(knows_sets_t::value_type{id, {}});
474  if(insert_res.second && res_index > 0) {
475  // istable(L, res_index) was already checked.
476  if(luaW_tableget(L, res_index, id.c_str())) {
477  insert_res.first->second = luaW_to_locationset(L, -1);
478  lua_pop(L, 1);
479  }
480  }
481  set_ = &insert_res.first->second;
482  }
483  bool matches(const gamemap_base&, map_location l) override
484  {
485  LOG_MATCHES(findin);
486  if(set_) {
487  return set_->find(l) != set_->end();
488  }
489  return false;
490  }
491  const location_set* set_;
492 };
493 
494 class radius_filter : public filter_impl
495 {
496 public:
497 
498  radius_filter(lua_State* L, int res_index, knows_sets_t& ks)
499  : radius_()
500  , filter_radius_()
501  , filter_()
502  {
503  LOG_LMG << "creating radius filter";
504  if(luaW_tableget(L, -1, "filter_radius")) {
505  filter_radius_ = build_filter(L, res_index, ks);
506  lua_pop(L, 1);
507  }
508  lua_geti(L, -1, 2);
509  radius_ = lua_tointeger(L, -1);
510  lua_pop(L, 1);
511  lua_geti(L, -1, 3);
512  filter_ = build_filter(L, res_index, ks);
513  lua_pop(L, 1);
514  }
515 
516  bool matches(const gamemap_base& m, map_location l) override
517  {
518  LOG_MATCHES(radius);
519  std::set<map_location> result;
520 
521  get_tiles_radius({{ l }}, radius_, result,
522  [&](const map_location& l) {
523  return m.on_board_with_border(l);
524  },
525  [&](const map_location& l) {
526  return !filter_radius_ || filter_radius_->matches(m, l);
527  }
528  );
529 
530  for (map_location lr : result) {
531  if(!filter_ || filter_->matches(m, lr)) {
532  return true;
533  }
534  }
535  return false;
536  }
537 
538  int radius_;
539  std::unique_ptr<filter_impl> filter_radius_;
540  std::unique_ptr<filter_impl> filter_;
541 };
542 
543 class formula_filter : public filter_impl
544 {
545 public:
546  formula_filter(lua_State* L, int, knows_sets_t&)
547  : formula_()
548  {
549  LOG_LMG << "creating formula filter";
550  lua_geti(L, -1, 2);
551  std::string code = std::string(luaW_tostring(L, -1));
552  lua_pop(L, 1);
553 
554  try {
555  formula_ = std::make_unique<wfl::formula>(code);
556  } catch(const wfl::formula_error& e) {
557  ERR_LMG << "formula error" << e.what();
558  }
559  }
560  bool matches(const gamemap_base&, map_location l) override
561  {
562  LOG_MATCHES(formula);
563  try {
564  const wfl::location_callable callable1(l);
565  wfl::map_formula_callable callable(callable1.fake_ptr());
566  return (formula_.get() != nullptr) && formula_->evaluate(callable).as_bool();
567  } catch(const wfl::formula_error& e) {
568  ERR_LMG << "Formula error: " << e.type << " at " << e.filename << ':' << e.line << ")";
569  return false;
570  }
571  }
572  std::unique_ptr<wfl::formula> formula_;
573 };
574 
575 // todo: maybe invent a general macro for this string_switch implementation.
576 enum filter_keys { F_AND, F_OR, F_NAND, F_NOR, F_X, F_Y, F_FIND_IN, F_ADJACENT, F_TERRAIN, F_RADIUS, F_FORMULA, F_CACHED };
577 // todo: c++20: perhaps enable heterogenous lookup.
578 static const std::unordered_map<std::string, filter_keys> keys {
579  { "all", F_AND },
580  { "any", F_OR },
581  { "not_all", F_NAND },
582  { "none", F_NOR },
583  { "x", F_X },
584  { "y", F_Y },
585  { "find_in", F_FIND_IN },
586  { "adjacent", F_ADJACENT },
587  { "terrain", F_TERRAIN },
588  { "cached", F_CACHED },
589  { "formula", F_FORMULA },
590  { "radius", F_RADIUS }
591 };
592 
593 std::unique_ptr<filter_impl> build_filter(lua_State* L, int res_index, knows_sets_t& ks)
594 {
595  LOG_LMG << "buildfilter: start";
596  if(!lua_istable(L, -1)) {
597  throw invalid_lua_argument("buildfilter: expected table");
598  }
599  lua_rawgeti(L, -1, 1);
600  std::string s = std::string(luaW_tostring(L, -1));
601  LOG_LMG << "buildfilter: got: " << s;
602  auto it = keys.find(s);
603  if(it == keys.end()) {
604  //fixme use proper exception type.
605  throw invalid_lua_argument(std::string("buildfilter: invalid filter type ") + s);
606  }
607  auto key = it->second;
608  lua_pop(L, 1);
609  switch(key)
610  {
611  case F_AND:
612  return std::make_unique<and_filter>(L, res_index, ks);
613  case F_OR:
614  return std::make_unique<or_filter>(L, res_index, ks);
615  case F_NAND:
616  return std::make_unique<nand_filter>(L, res_index, ks);
617  case F_NOR:
618  return std::make_unique<nor_filter>(L, res_index, ks);
619  case F_X:
620  return std::make_unique<x_filter>(L, res_index, ks);
621  case F_Y:
622  return std::make_unique<y_filter>(L, res_index, ks);
623  case F_FIND_IN:
624  return std::make_unique<findin_filter>(L, res_index, ks);
625  case F_ADJACENT:
626  return std::make_unique<adjacent_filter>(L, res_index, ks);
627  case F_TERRAIN:
628  return std::make_unique<terrain_filter>(L, res_index, ks);
629  case F_RADIUS:
630  return std::make_unique<radius_filter>(L, res_index, ks);
631  case F_CACHED:
632  return std::make_unique<cached_filter>(L, res_index, ks);
633  case F_FORMULA:
634  return std::make_unique<formula_filter>(L, res_index, ks);
635  default:
636  throw "invalid filter key enum";
637  }
638 }
639 }
640 
641 //////////////// PUBLIC API ////////////////
642 
643 namespace lua_mapgen {
644 /**
645  * @param L the pointer to the lua interpreter.
646  * @param data_index a index to the lua stack pointing to the lua table that describes the filter.
647  * @param res_index a _positive_ index to the lua stack pointing to the lua table that describes the filter resources.
648  */
649 filter::filter(lua_State* L, int data_index, int res_index)
650 {
651  LOG_LMG << "creating filter object";
652  lua_pushvalue (L, data_index);
653  impl_ = build_filter(L, res_index, known_sets_);
654  lua_pop(L, 1);
655  LOG_LMG << "finished creating filter object";
656 }
657 
659 {
660  log_scope("filter::matches");
661  return impl_->matches(m, l);
662 }
663 
665 {
666 
667 }
668 
669 }
670 
672 {
673  location_set res;
674  LOG_LMG << "map:get_locations vaidargs";
675  if(lua_istable(L, 3)) {
676  LOG_LMG << "map:get_locations some locations";
678  LOG_LMG << "map:get_locations #args = " << s.size();
679  for (const map_location& l : s) {
680  if(f.matches(m, l)) {
681  res.insert(l);
682  }
683  }
684  }
685  else {
686  LOG_LMG << "map:get_locations all locations";
687  m.for_each_loc([&](map_location l) {
688  if(f.matches(m, l)) {
689  res.insert(l);
690  }
691  });
692  }
693  LOG_LMG << "map:get_locations #res = " << res.size();
694  luaW_push_locationset(L, res);
695  LOG_LMG << "map:get_locations end";
696  return 1;
697 
698 }
699 int intf_mg_get_locations(lua_State* L)
700 {
701  //todo: create filter form table if needed
702  LOG_LMG << "map:get_locations";
704  if(luaW_is_mgfilter(L, 2)) {
706  return intf_mg_get_locations_part2(L, m, f);
707  }
708  else if (lua_istable(L, 2)) {
709  lua_mapgen::filter f(L, 2, 0);
710  return intf_mg_get_locations_part2(L, m, f);
711  }
712  else {
713  return luaW_type_error(L, 2, "terrainfilter");
714  }
715 }
716 
717 int intf_mg_get_tiles_radius(lua_State* L)
718 {
721  int r = luaL_checkinteger(L, 3);
723  location_set res;
724  get_tiles_radius(std::move(s), r, res,
725  [&](const map_location& l) {
726  return m.on_board_with_border(l);
727  },
728  [&](const map_location& l) {
729  return f.matches(m, l);
730  }
731  );
732  luaW_push_locationset(L, res);
733  return 1;
734 }
735 
736 bool luaW_is_mgfilter(lua_State* L, int index)
737 {
738  return luaL_testudata(L, index, terrinfilterKey) != nullptr;
739 }
740 
741 
743 {
744  if(luaW_is_mgfilter(L, index)) {
745  return static_cast<lua_mapgen::filter*>(lua_touserdata(L, index));
746  }
747  return nullptr;
748 }
749 
751 {
752  if(luaW_is_mgfilter(L, index)) {
753  return *static_cast<lua_mapgen::filter*>(lua_touserdata(L, index));
754  }
755  luaW_type_error(L, index, "terrainfilter");
756  throw "luaW_type_error didn't throw";
757 }
758 
759 void lua_mgfilter_setmetatable(lua_State *L)
760 {
761  luaL_setmetatable(L, terrinfilterKey);
762 }
763 
764 template<typename... T>
765 static lua_mapgen::filter* luaW_push_mgfilter(lua_State *L, T&&... params)
766 {
767  LOG_LMG << "luaW_push_mgfilter";
768  lua_mapgen::filter* res = new(L) lua_mapgen::filter(std::forward<T>(params)...);
770  return res;
771 }
772 
773 /**
774  * Create a filter.
775 */
776 int intf_terrainfilter_create(lua_State *L)
777 {
778  try {
779  int res_index = 0;
780  if(!lua_istable(L, 1)) {
781  return luaL_argerror(L, 1, "table expected");
782  }
783  if(lua_istable(L, 2)) {
784  res_index = 2;
785  }
786  lua_mapgen::filter res(L, 1, res_index);
787  luaW_push_mgfilter(L, std::move(res));
788  return 1;
789  }
790  catch(const invalid_lua_argument& e) {
791  return luaL_argerror(L, 1, e.what());
792  }
793 }
794 
795 
796 /**
797  * Gets some data on a filter (__index metamethod).
798  * - Arg 1: full userdata containing the filter.
799  * - Arg 2: string containing the name of the property.
800  * - Ret 1: something containing the attribute.
801  */
802 static int impl_terrainfilter_get(lua_State *L)
803 {
805  UNUSED(f);
806  return 0;
807 }
808 
809 /**
810  * Sets some data on a filter (__newindex metamethod).
811  * - Arg 1: full userdata containing the filter.
812  * - Arg 2: string containing the name of the property.
813  * - Arg 3: something containing the attribute.
814  */
815 static int impl_terrainfilter_set(lua_State *L)
816 {
818  UNUSED(f);
819  char const *m = luaL_checkstring(L, 2);
820  std::string err_msg = "unknown modifiable property of map: ";
821  err_msg += m;
822  return luaL_argerror(L, 2, err_msg.c_str());
823 }
824 
825 
826 /**
827  * Clears the cache of a filter.
828  */
829 static int intf_clearcache(lua_State *L)
830 {
832  UNUSED(f);
833  return 0;
834 }
835 /**
836  * Destroys a map object before it is collected (__gc metamethod).
837  */
838 static int impl_terrainfilter_collect(lua_State *L)
839 {
841  f.~filter();
842  return 0;
843 }
844 
845 
846 namespace lua_terrainfilter {
847  std::string register_metatables(lua_State* L)
848  {
849  std::ostringstream cmd_out;
850 
851  cmd_out << "Adding terrainmamap metatable...\n";
852 
853  luaL_newmetatable(L, terrinfilterKey);
854  lua_pushcfunction(L, impl_terrainfilter_collect);
855  lua_setfield(L, -2, "__gc");
856  lua_pushcfunction(L, impl_terrainfilter_get);
857  lua_setfield(L, -2, "__index");
858  lua_pushcfunction(L, impl_terrainfilter_set);
859  lua_setfield(L, -2, "__newindex");
860  lua_pushstring(L, "terrain_filter");
861  lua_setfield(L, -2, "__metatable");
862  // terrainmap methods
863  lua_pushcfunction(L, intf_clearcache);
864  lua_setfield(L, -2, "clear_cache");
865 
866  return cmd_out.str();
867  }
868 }
virtual bool matches(const gamemap_base &m, map_location l)=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_
std::map< std::string, std::set< map_location > > known_sets_
bool matches(const gamemap_base &m, map_location l)
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:207
Standard logging facilities (interface).
#define log_scope(description)
Definition: log.hpp:274
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:798
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:741
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:790
lua_mapgen::filter * luaW_to_mgfilter(lua_State *L, int index)
lua_mapgen::filter & luaW_check_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_mg_get_locations_part2(lua_State *L, gamemap_base &m, lua_mapgen::filter &f)
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::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)
std::map< std::string, std::set< map_location > > knows_sets_t
#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).
bool luaW_is_mgfilter(lua_State *L, int index)
static const char terrinfilterKey[]
gamemap_base & luaW_checkterrainmap(lua_State *L, int index)
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.
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