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