16 #define GETTEXT_DOMAIN "wesnoth-lib" 32 #include "formula/callable_objects.hpp" 38 #include <boost/range/adaptor/transformed.hpp> 41 #define ERR_NG LOG_STREAM(err, log_engine) 42 #define WRN_NG LOG_STREAM(warn, log_engine) 45 #define ERR_WML LOG_STREAM(err, log_wml) 91 parsed_terrain(nullptr),
92 adjacent_matches(nullptr),
93 adjacent_match_cache(),
99 if (!this->fc_->get_disp_context().map().on_board_with_border(loc)) {
104 if (!lua_function.empty() && fc_->get_lua_kernel()) {
105 if (!fc_->get_lua_kernel()->run_filter(lua_function.c_str(), loc)) {
111 if (cfg_.has_attribute(
"area") &&
112 fc_->get_tod_man().get_area_by_id(cfg_[
"area"]).count(loc) == 0)
115 if(cfg_.has_attribute(
"gives_income") &&
116 cfg_[
"gives_income"].to_bool() != fc_->get_disp_context().map().is_village(loc))
119 if(cfg_.has_attribute(
"terrain")) {
120 if(cache_.parsed_terrain ==
nullptr) {
123 if(!cache_.parsed_terrain->is_empty) {
137 if (cfg_.has_attribute(
"find_in")) {
138 if (
const game_data * gd = fc_->get_game_data()) {
150 if (!found)
return false;
158 if (cfg_.has_attribute(
"location_id")) {
159 std::set<map_location> matching_locs;
160 for(
const auto&
id :
utils::split(cfg_[
"location_id"])) {
161 map_location test_loc = fc_->get_disp_context().map().special_location(
id);
162 if(test_loc.
valid()) {
163 matching_locs.insert(test_loc);
166 if (matching_locs.count(loc) == 0) {
172 if(cfg_.has_child(
"filter")) {
176 if (!cache_.ufilter_) {
177 cache_.ufilter_.reset(
new unit_filter(cfg_.child(
"filter").make_safe()));
178 cache_.ufilter_->set_use_flat_tod(flat_);
180 if (!cache_.ufilter_->matches(*u, loc))
185 if (cfg_.has_child(
"filter_vision")) {
187 vconfig::child_list::const_iterator
i, i_end = vis_filt.end();
188 for (i = vis_filt.begin(); i != i_end; ++
i) {
189 bool visible = (*i)[
"visible"].to_bool(
true);
190 bool respect_fog = (*i)[
"respect_fog"].to_bool(
true);
193 std::vector<int> sides = ssf.
get_teams();
196 for (
const int side : sides) {
197 const team &viewing_team = fc_->get_disp_context().get_team(side);
198 bool viewer_sees = respect_fog ? !viewing_team.
fogged(loc) : !viewing_team.
shrouded(loc);
199 if (visible == viewer_sees) {
204 if (!found) {
return false;}
209 if(cfg_.has_child(
"filter_adjacent_location")) {
212 vconfig::child_list::const_iterator
i, i_end, i_begin = adj_cfgs.begin();
213 for (i = i_begin, i_end = adj_cfgs.end(); i != i_end; ++
i) {
215 vconfig::child_list::difference_type
index = i - i_begin;
216 std::vector<map_location::DIRECTION> dirs = (*i).has_attribute(
"adjacent")
218 std::vector<map_location::DIRECTION>::const_iterator j, j_end = dirs.end();
219 for (j = dirs.begin(); j != j_end; ++j) {
221 if (fc_->get_disp_context().map().on_board(adj)) {
222 if(cache_.adjacent_matches ==
nullptr) {
223 while(index >= std::distance(cache_.adjacent_match_cache.begin(), cache_.adjacent_match_cache.end())) {
224 const vconfig& adj_cfg = adj_cfgs[cache_.adjacent_match_cache.size()];
225 std::pair<terrain_filter, std::map<map_location,bool>> amc_pair(
226 terrain_filter(adj_cfg, *
this),
227 std::map<map_location,bool>());
228 cache_.adjacent_match_cache.push_back(amc_pair);
230 terrain_filter &amc_filter = cache_.adjacent_match_cache[
index].first;
231 std::map<map_location,bool> &amc = cache_.adjacent_match_cache[
index].second;
233 if(lookup == amc.end()) {
234 if(amc_filter(adj)) {
240 }
else if(lookup->second) {
244 assert(index < std::distance(cache_.adjacent_matches->begin(), cache_.adjacent_matches->end()));
245 std::set<map_location> &amc = (*cache_.adjacent_matches)[index];
246 if(amc.find(adj) != amc.end()) {
253 std::vector<std::pair<int,int>> counts = (*i).has_attribute(
"count")
261 const t_string& t_tod_type = cfg_[
"time_of_day"];
262 const t_string& t_tod_id = cfg_[
"time_of_day_id"];
263 const std::string& tod_type = t_tod_type;
264 const std::string& tod_id = t_tod_id;
265 if(!tod_type.empty() || !tod_id.empty()) {
270 tod = fc_->get_tod_man().get_time_of_day(loc);
272 tod = fc_->get_tod_man().get_illuminated_time_of_day(fc_->get_disp_context().units(), fc_->get_disp_context().map(),loc);
275 if(!tod_type.empty()) {
276 const std::vector<std::string>& vals =
utils::split(tod_type);
278 if(std::find(vals.begin(),vals.end(), unit_alignments::chaotic) == vals.end()) {
282 if(std::find(vals.begin(),vals.end(), unit_alignments::lawful) == vals.end()) {
285 }
else if(std::find(vals.begin(),vals.end(), unit_alignments::neutral) == vals.end() &&
286 std::find(vals.begin(),vals.end(), unit_alignments::liminal) == vals.end()) {
291 if(!tod_id.empty()) {
292 if(tod_id != tod.
id) {
293 if(std::find(tod_id.begin(),tod_id.end(),
',') != tod_id.end() &&
294 std::search(tod_id.begin(),tod_id.end(),
295 tod.
id.begin(),tod.
id.end()) != tod_id.end()) {
296 const std::vector<std::string>& vals =
utils::split(tod_id);
297 if(std::find(vals.begin(),vals.end(),tod.
id) == vals.end()) {
309 const vconfig& filter_owner = cfg_.
child(
"filter_owner");
310 if(!filter_owner.
null()) {
311 if(!owner_side.
empty()) {
312 WRN_NG <<
"duplicate side information in a SLF, ignoring inline owner_side=";
314 if(!fc_->get_disp_context().map().is_village(loc))
317 const std::vector<int>& sides = ssf.
get_teams();
319 if(sides.empty() && fc_->get_disp_context().village_owner(loc) == 0)
321 for(
const int side : sides) {
322 if(fc_->get_disp_context().get_team(side).owns_village(loc)) {
330 else if(!owner_side.
empty()) {
331 const int side_num = owner_side.
to_int(0);
332 if(fc_->get_disp_context().village_owner(loc) != side_num) {
337 if(cfg_.has_attribute(
"formula")) {
342 auto ref = std::make_shared<wfl::unit_callable>(*ref_unit);
369 return filt_.match(loc, ref_);
375 if(cfg_[
"x"] ==
"recall" && cfg_[
"y"] ==
"recall") {
376 return !fc_->get_disp_context().map().on_board(loc);
378 std::set<map_location> hexes;
379 std::vector<map_location> loc_vec(1, loc);
381 std::unique_ptr<scoped_wml_variable> ref_unit_var;
383 if(fc_->get_disp_context().map().on_board(ref_unit->
get_location())) {
391 std::size_t radius = cfg_[
"radius"].to_size_t(0);
392 if(radius > max_loop_) {
393 ERR_NG <<
"terrain_filter: radius greater than " << max_loop_
398 hexes.insert(loc_vec.begin(), loc_vec.end());
399 else if ( cfg_.has_child(
"filter_radius") ) {
400 terrain_filter r_filter(cfg_.child(
"filter_radius"), *
this);
404 get_tiles_radius(fc_->get_disp_context().map(), loc_vec, radius, hexes,
false, r_filter);
410 std::size_t loop_count = 0;
411 std::set<map_location>::const_iterator
i;
412 for(i = hexes.begin(); i != hexes.end(); ++
i) {
413 bool matches = match_internal(*i, ref_unit,
false);
416 for(
const auto& [key, filter] : cfg_.all_ordered()) {
419 matches = matches && terrain_filter(filter, *
this).match_impl(*i, ref_unit);
422 else if(key ==
"or") {
423 matches = matches || terrain_filter(filter, *
this).match_impl(*i, ref_unit);
426 else if(key ==
"not") {
427 matches = matches && !terrain_filter(filter, *
this).match_impl(*i, ref_unit);
434 if(++loop_count > max_loop_) {
435 std::set<map_location>::const_iterator temp =
i;
436 if(++temp != hexes.end()) {
437 ERR_NG <<
"terrain_filter: loop count greater than " << max_loop_
456 template<
typename T,
typename F1,
typename F2,
typename F3>
460 if (f1(loc) && f2(loc) && f3(loc)) {
466 template<
typename T,
typename F1,
typename F2>
469 if (filter.cfg_.has_attribute(
"location_id")) {
470 std::set<map_location> matching_locs;
471 for(
const auto&
id :
utils::split(filter.cfg_[
"location_id"])) {
472 map_location test_loc = filter.fc_->get_disp_context().map().special_location(
id);
473 if(test_loc.
valid()) {
474 matching_locs.insert(test_loc);
477 filter_final(src, dest, filter, f1, f2, [matching_locs](
const map_location& loc) {
return matching_locs.count(loc) > 0; });
480 filter_final(src, dest, filter, f1, f2,
no_filter());
484 template<
typename T,
typename F1>
487 if (filter.cfg_.has_attribute(
"area")) {
488 const std::set<map_location>& area = filter.fc_->get_tod_man().get_area_by_id(filter.cfg_[
"area"]);
489 filter_special_loc(src, dest, filter, f1, [&area](
const map_location& loc) {
return area.find(loc) != area.end(); });
492 filter_special_loc(src, dest, filter, f1,
no_filter());
499 if (filter.cfg_.has_attribute(
"x") || filter.cfg_.has_attribute(
"y")) {
500 std::vector<map_location> xy_vector = filter.fc_->get_disp_context().map().parse_location_range(filter.cfg_[
"x"], filter.cfg_[
"y"], with_border);
501 filter_area(src, dest, filter, [&xy_vector](
const map_location& loc) {
return std::find(xy_vector.begin(), xy_vector.end(), loc) != xy_vector.end(); });
504 filter_area(src, dest, filter,
no_filter());
516 std::unique_ptr<scoped_wml_variable> ref_unit_var;
518 if(fc_->get_disp_context().map().on_board(ref_unit->
get_location())) {
525 std::set<map_location> match_set;
528 with_border = cfg_[
"include_borders"].to_bool(with_border);
530 if (cfg_.has_attribute(
"find_in")) {
532 if (
const game_data * gd = fc_->get_game_data()) {
535 auto ar = gd->get_variable_access_read(cfg_[
"find_in"]).as_array();
544 else if (cfg_.has_attribute(
"x") || cfg_.has_attribute(
"y")) {
545 std::vector<map_location> xy_vector = fc_->get_disp_context().map().parse_location_range(cfg_[
"x"], cfg_[
"y"], with_border);
548 else if (cfg_.has_attribute(
"area")) {
549 const std::set<map_location>& area = fc_->get_tod_man().get_area_by_id(cfg_[
"area"]);
552 else if (cfg_.has_attribute(
"location_id")) {
553 for(
const auto&
id :
utils::split(cfg_[
"location_id"])) {
554 map_location test_loc = fc_->get_disp_context().map().special_location(
id);
555 if(test_loc.
valid()) {
556 match_set.insert(test_loc);
560 else if (cfg_[
"gives_income"].to_bool()) {
561 auto ar = fc_->get_disp_context().map().villages();
566 int bs = fc_->get_disp_context().map().border_size();
567 int w = with_border ? fc_->get_disp_context().map().w() + bs : fc_->get_disp_context().map().w();
568 int h = with_border ? fc_->get_disp_context().map().h() + bs : fc_->get_disp_context().map().h();
569 for (
int x = with_border ? 0 - bs : 0; x <
w; ++x) {
570 for (
int y = with_border ? 0 - bs : 0; y <
h; ++y) {
577 if(cfg_.has_child(
"filter_adjacent_location")) {
578 if(cache_.adjacent_matches ==
nullptr) {
579 cache_.adjacent_matches.reset(
new std::vector<std::set<map_location>>());
582 for (
unsigned i = 0;
i < adj_cfgs.size(); ++
i) {
583 std::set<map_location> adj_set;
585 terrain_filter(adj_cfgs.at(
i), *
this).get_locations(adj_set, with_border);
586 cache_.adjacent_matches->push_back(adj_set);
587 if(
i >= max_loop_ &&
i+1 < adj_cfgs.size()) {
588 ERR_NG <<
"terrain_filter: loop count greater than " << max_loop_
595 while(loc_itor != match_set.end()) {
596 if(match_internal(*loc_itor, ref_unit,
true)) {
599 loc_itor = match_set.erase(loc_itor);
603 int ors_left = std::count_if(cfg_.ordered_begin(), cfg_.ordered_end(), [](
const auto& val) {
return val.first ==
"or"; });
606 for(
const auto& [key, filter] : cfg_.all_ordered()) {
608 if(match_set.empty() && ors_left <= 0) {
614 std::set<map_location> intersect_hexes;
615 terrain_filter(filter, *
this).get_locations(intersect_hexes, with_border);
617 while(intersect_itor != match_set.end()) {
618 if(intersect_hexes.find(*intersect_itor) == intersect_hexes.end()) {
619 match_set.erase(*intersect_itor++);
626 else if(key ==
"or") {
627 std::set<map_location> union_hexes;
628 terrain_filter(filter, *
this).get_locations(union_hexes, with_border);
631 while(insert_itor != union_hexes.end()) {
632 match_set.insert(*insert_itor++);
637 else if(key ==
"not") {
638 std::set<map_location> removal_hexes;
639 terrain_filter(filter, *
this).get_locations(removal_hexes, with_border);
641 while(erase_itor != removal_hexes.end()) {
642 match_set.erase(*erase_itor++);
646 if(match_set.empty()) {
651 std::size_t radius = cfg_[
"radius"].to_size_t(0);
652 if(radius > max_loop_) {
653 ERR_NG <<
"terrain_filter: radius greater than " << max_loop_
658 std::vector<map_location> xy_vector (match_set.begin(), match_set.end());
659 if(cfg_.has_child(
"filter_radius")) {
660 terrain_filter r_filter(cfg_.child(
"filter_radius"), *
this);
661 get_tiles_radius(fc_->get_disp_context().map(), xy_vector, radius, locs, with_border, r_filter);
663 get_tiles_radius(fc_->get_disp_context().map(), xy_vector, radius, locs, with_border);
666 locs.insert(match_set.begin(), match_set.end());
672 return cfg_.get_config();
bool operator()(const map_location &) const
std::function< int(lua_State *)> lua_function
bool empty() const
Tests for an attribute that either was never set or was set to "".
vconfig child(const std::string &key) const
Returns a child of *this whose key is key.
void get_adjacent_tiles(const map_location &a, map_location *res)
Function which, given a location, will place all adjacent locations in res.
This class represents a single unit of a specific type.
int main(int, char **argv)
Variant for storing WML attributes.
bool terrain_matches(const terrain_code &src, const terrain_code &dest)
Tests whether a specific terrain matches an expression, for matching rules see above.
static void filter_final(T &&src, location_set &dest, const terrain_filter &, const F1 &f1, const F2 &f2, const F3 &f3)
static lg::log_domain log_wml("wml")
map_location operator()(const config &cfg) const
int lawful_bonus
The % bonus lawful units receive.
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
bool in_ranges(const Cmp c, const std::vector< std::pair< Cmp, Cmp >> &ranges)
static void filter_special_loc(T &&src, location_set &dest, const terrain_filter &filter, const F1 &f1, const F2 &f2)
terrain_filter_cache cache_
int to_int(int def=0) const
static std::vector< DIRECTION > parse_directions(const std::string &str)
Parse_directions takes a comma-separated list, and filters out any invalid directions.
const std::size_t max_loop
The maximum number of hexes on a map and items in an array and also used as maximum in wml loops...
Definitions for the interface to Wesnoth Markup Language (WML).
bool match_internal(const map_location &loc, const unit *ref_unit, const bool ignore_xy) const
void get_locs_impl(std::set< map_location > &locs, const unit *ref_unit, bool with_border) const
const filter_context * fc_
Object which defines a time of day with associated bonuses, image, sounds etc.
This class stores all the data for a single 'side' (in game nomenclature).
std::vector< std::pair< int, int > > parse_ranges(const std::string &str)
std::vector< std::pair< int, int > > default_counts
static const std::vector< DIRECTION > & default_dirs()
Default list of directions.
const terrain_filter & filt_
filter_with_unit(const terrain_filter &filt, const unit &ref)
~terrain_filter()
Default implementation, but defined out-of-line for efficiency reasons.
Encapsulates the map of the game.
bool shrouded(const map_location &loc) const
bool operator()(const map_location &loc) const override
Game configuration data as global variables.
terrain_filter & operator=(const terrain_filter &other)
void get_tiles_radius(const map_location ¢er, 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...
bool matches_range(const std::string &xloc, const std::string &yloc) const
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.
Information on a WML variable.
std::vector< std::string > split(const config_attribute_value &val)
const map_location & get_location() const
The current map location this unit is at.
std::vector< int > get_teams() const
A variable-expanding proxy for the config class.
static lg::log_domain log_engine("engine")
Standard logging facilities (interface).
bool fogged(const map_location &loc) const
maybe_const_t< config::child_itors, V > as_array() const
If instantiated with vi_policy_const, the lifetime of the returned const attribute_value reference mi...
terrain_filter(const vconfig &cfg, const filter_context *fc, const bool flat_tod)
static void filter_area(T &&src, location_set &dest, const terrain_filter &filter, const F1 &f1)
bool match_impl(const map_location &loc, const unit *ref_unit) const
std::vector< vconfig > child_list
bool as_bool() const
Returns a boolean state of the variant value.
A config object defines a single node in a WML file, with access to child nodes.
This structure can be used for matching terrain strings.
static void filter_xy(T &&src, location_set &dest, const terrain_filter &filter, bool with_border)
std::string::const_iterator iterator
const std::size_t max_loop_
std::stringstream & log_to_chat()
Use this to show WML errors in the ingame chat.
std::set< map_location > location_set