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