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