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