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