40 #include "formula/callable_objects.hpp"
50 #define ERR_NG LOG_STREAM(err, log_engine)
53 #define ERR_WML LOG_STREAM(err, log_wml)
56 class temporary_facing
66 u_->set_facing(new_dir);
72 u_->set_facing(save_dir_);
149 const team& get_team(std::size_t side)
169 bool affects_side(
const config& cfg, std::size_t side, std::size_t other_side)
171 const team& side_team = get_team(side);
173 if(side == other_side)
174 return cfg[
"affect_allies"].to_bool(
true);
176 return cfg[
"affect_enemies"].to_bool();
178 return cfg[
"affect_allies"].to_bool();
192 const unit_map& units = get_unit_map();
195 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
197 if (it == units.
end() || it->incapacitated())
206 for (
const config &j : it->abilities_.child_range(tag_name)) {
229 const unit_map& units = get_unit_map();
232 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
234 if (it == units.
end() || it->incapacitated())
243 for(
const config& j : it->abilities_.child_range(tag_name)) {
266 std::vector<std::string> res;
269 std::string
id = cfg[
"id"];
271 res.push_back(std::move(
id));
284 bool add_ability_tooltip(
const config& ab,
unit_race::GENDER gender, std::vector<std::tuple<std::string, t_string,t_string,t_string>>& res,
bool active)
294 ab[
"description"].t_str() );
303 "female_name_inactive",
"name_inactive");
312 ab.
get_or(
"description_inactive",
"description").
t_str() );
323 std::vector<std::tuple<std::string, t_string,t_string,t_string>> res;
327 add_ability_tooltip(cfg,
gender_, res,
true);
335 std::vector<std::tuple<std::string, t_string,t_string,t_string>> res;
341 if (add_ability_tooltip(cfg,
gender_, res, active))
343 active_list.push_back(active);
354 void show_recursion_warning(
const unit&
unit,
const config& filter) {
360 static std::vector<std::tuple<std::string, std::string>> already_shown;
362 auto identifier = std::tuple<std::string, std::string>{
unit.
id(), filter.
debug()};
367 std::string_view filter_text_view = std::get<1>(identifier);
369 ERR_NG <<
"Looped recursion error for unit '" <<
unit.
id()
370 <<
"' while checking ability '" << filter_text_view <<
"'";
374 if(already_shown.size() > 100) {
375 already_shown.clear();
377 already_shown.push_back(std::move(identifier));
392 : parent(u.shared_from_this())
402 unit::recursion_guard::operator bool()
const {
408 assert(
this != &other);
417 assert(!parent->open_queries_.empty());
418 parent->open_queries_.pop_back();
426 show_recursion_warning(*
this, cfg);
434 bool illuminates = ability ==
"illuminates";
442 const unit_map& units = get_unit_map();
446 std::size_t count = 0;
448 ufilt.set_use_flat_tod(illuminates);
457 if (!ufilt(*
unit, *
this))
459 if((*this).id() == (*unit).id())
461 if (
i.has_attribute(
"is_enemy")) {
469 if (
i[
"count"].empty() && count != dirs.size()) {
479 std::size_t count = 0;
481 adj_filter.flatten(illuminates);
489 if(!adj_filter.match(adjacent[
static_cast<int>(
index)])) {
494 if (
i[
"count"].empty() && count != dirs.size()) {
506 bool illuminates = ability ==
"illuminates";
508 assert(dir >=0 && dir <= 5);
513 if (
i.has_attribute(
"adjacent")) {
515 if (
std::find(dirs.begin(), dirs.end(), direction) == dirs.end()) {
519 if((*this).id() == from.
id()){
522 auto filter =
i.optional_child(
"filter");
534 bool affect_self = cfg[
"affect_self"].to_bool(
true);
535 if (!filter || !affect_self)
return affect_self;
541 const std::string filter_tag_name = is_opp ?
"filter_second_weapon" :
"filter_weapon";
550 filter_lock = weapon->update_variables_recursion(cfg);
552 show_recursion_warning(*
this, cfg);
555 return weapon->matches_filter(filter);
567 auto ret =
std::find(image_list.begin(), image_list.end(), cfg[attribute_name].str());
568 if(ret == image_list.end()){
569 image_list.push_back(cfg[attribute_name].str());
575 std::vector<std::string> image_list;
583 if(!cfg[image_type +
"_image_self"].str().empty() &&
is_active){
588 const unit_map& units = get_unit_map();
593 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
595 if (it == units.
end() || it->incapacitated())
599 for(
const auto [key, cfg] : it->abilities_.all_children_view()) {
600 if(!cfg[image_type +
"_image"].str().empty() && affects_side(cfg,
side(), it->side()) && it->ability_active(key, cfg, adjacent[
i]) &&
ability_affects_adjacent(key, cfg,
i,
loc_, *it))
607 if(image_list.size() >= 2){
608 std::sort(image_list.begin(), image_list.end());
616 callable.
add(
"attacker",
wfl::variant(std::make_shared<wfl::unit_callable>(*att)));
619 callable.
add(
"defender",
wfl::variant(std::make_shared<wfl::unit_callable>(*def)));
626 template<
typename T,
typename TFuncFormula>
627 class get_ability_value_visitor
628 #ifdef USING_BOOST_VARIANT
629 :
public boost::static_visitor<T>
634 get_ability_value_visitor(T def,
const TFuncFormula& formula_handler) : def_(def), formula_handler_(formula_handler) {}
636 T operator()(
const utils::monostate&)
const {
return def_; }
637 T operator()(
bool)
const {
return def_; }
638 T operator()(
int i)
const {
return static_cast<T
>(
i); }
639 T operator()(
unsigned long long u)
const {
return static_cast<T
>(u); }
640 T operator()(
double d)
const {
return static_cast<T
>(
d); }
641 T operator()(
const t_string&)
const {
return def_; }
642 T operator()(
const std::string&
s)
const
644 if(
s.size() >= 2 &&
s[0] ==
'(') {
645 return formula_handler_(
s);
647 return lexical_cast_default<T>(
s, def_);
652 const TFuncFormula& formula_handler_;
655 template<
typename T,
typename TFuncFormula>
658 return v.
apply_visitor(get_ability_value_visitor(def, [&](
const std::string&
s) {
661 const unit_map& units = get_unit_map();
665 if(u_itor == units.
end()) {
670 att->add_formula_context(callable);
673 callable.add(
"student",
wfl::variant(std::make_shared<wfl::unit_callable>(*uptr)));
676 callable.add(
"other",
wfl::variant(std::make_shared<wfl::unit_callable>(*uptr)));
680 lg::log_to_chat() <<
"Formula error in ability or weapon special: " <<
e.type <<
" at " <<
e.filename <<
':' <<
e.line <<
")\n";
681 ERR_WML <<
"Formula error in ability or weapon special: " <<
e.type <<
" at " <<
e.filename <<
':' <<
e.line <<
")";
688 template<
typename TComp>
691 if ( cfgs_.empty() ) {
697 bool only_cumulative =
true;
704 return formula.
evaluate(callable).as_int();
707 if ((*
p.ability_cfg)[
"cumulative"].to_bool()) {
709 if (value < 0) value = -value;
710 if (only_cumulative && !comp(value, abs_max)) {
712 best_loc =
p.teacher_loc;
714 }
else if (only_cumulative || comp(flat, value)) {
715 only_cumulative =
false;
717 best_loc =
p.teacher_loc;
720 return std::pair(flat + stack, best_loc);
723 template std::pair<int, map_location> unit_ability_list::get_extremum<std::less<int>>(
const std::string& key,
int def,
const std::less<int>& comp)
const;
724 template std::pair<int, map_location> unit_ability_list::get_extremum<std::greater<int>>(
const std::string& key,
int def,
const std::greater<int>& comp)
const;
758 std::string tag_name;
774 bool get_special_children(std::vector<special_match>& tag_result,
775 std::vector<special_match>& id_result,
776 const config& parent,
const std::string&
id,
777 bool just_peeking=
false) {
780 if (just_peeking && (key ==
id || cfg[
"id"] ==
id)) {
785 special_match special = { key, &cfg };
786 tag_result.push_back(special);
788 if(cfg[
"id"] ==
id) {
789 special_match special = { key, &cfg };
790 id_result.push_back(special);
796 bool get_special_children_id(std::vector<special_match>& id_result,
797 const config& parent,
const std::string&
id,
798 bool just_peeking=
false) {
801 if (just_peeking && (cfg[
"id"] ==
id)) {
805 if(cfg[
"id"] ==
id) {
806 special_match special = { key, &cfg };
807 id_result.push_back(special);
813 bool get_special_children_tags(std::vector<special_match>& tag_result,
814 const config& parent,
const std::string&
id,
815 bool just_peeking=
false) {
818 if (just_peeking && (key ==
id)) {
823 special_match special = { key, &cfg };
824 tag_result.push_back(special);
841 std::vector<special_match> special_tag_matches;
842 std::vector<special_match> special_id_matches;
843 if(special_id && special_tags){
844 if ( get_special_children(special_tag_matches, special_id_matches, specials_, special, simple_check) ) {
847 }
else if(special_id && !special_tags){
848 if ( get_special_children_id(special_id_matches, specials_, special, simple_check) ) {
851 }
else if(!special_id && special_tags){
852 if ( get_special_children_tags(special_tag_matches, specials_, special, simple_check) ) {
860 for(
const special_match& entry : special_tag_matches) {
861 if ( special_active(*entry.cfg, AFFECT_SELF, entry.tag_name) ) {
867 for(
const special_match& entry : special_id_matches) {
868 if ( special_active(*entry.cfg, AFFECT_SELF, entry.tag_name) ) {
876 if ( simple_check || !other_attack_ ) {
880 std::vector<special_match> special_tag_matches;
881 std::vector<special_match> special_id_matches;
882 if(special_id && special_tags){
883 get_special_children(special_tag_matches, special_id_matches, other_attack_->specials_, special);
884 }
else if(special_id && !special_tags){
885 get_special_children_id(special_id_matches, other_attack_->specials_, special);
886 }
else if(!special_id && special_tags){
887 get_special_children_tags(special_tag_matches, other_attack_->specials_, special);
890 for(
const special_match& entry : special_tag_matches) {
891 if ( other_attack_->special_active(*entry.cfg, AFFECT_OTHER, entry.tag_name) ) {
897 for(
const special_match& entry : special_id_matches) {
898 if ( other_attack_->special_active(*entry.cfg, AFFECT_OTHER, entry.tag_name) ) {
916 for(
const config&
i : specials_.child_range(special)) {
917 if(special_active(
i, AFFECT_SELF, special)) {
926 for(
const config&
i : other_attack_->specials_.child_range(special)) {
927 if(other_attack_->special_active(
i, AFFECT_OTHER, special)) {
944 boost::dynamic_bitset<>* active_list)
const
947 std::vector<std::pair<t_string, t_string>> res;
949 active_list->clear();
951 for(
const auto [key, cfg] : specials_.all_children_view())
953 if ( !active_list || special_active(cfg, AFFECT_EITHER, key) ) {
956 res.emplace_back(
name, cfg[
"description"].t_str() );
958 active_list->push_back(
true);
963 res.emplace_back(
name, cfg.
get_or(
"description_inactive",
"description").
t_str() );
964 active_list->push_back(
false);
979 static void add_name(std::string& temp_string,
bool active,
const std::string&
name, std::set<std::string>& checking_name)
983 checking_name.insert(
name);
984 if (!temp_string.empty()) temp_string +=
", ";
1001 for(
const auto [key, cfg] : specials_.all_children_view())
1003 const bool active = special_active(cfg, AFFECT_EITHER, key);
1005 const std::string&
name =
1008 : cfg.
get_or(
"name_inactive",
"name").
str();
1021 std::string temp_string;
1022 std::set<std::string> checking_name;
1023 weapon_specials_impl_self(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name);
1024 weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name, {},
"affect_allies");
1025 if(!temp_string.empty() && !res.empty()) {
1026 temp_string =
", \n" + temp_string;
1028 }
else if (!temp_string.empty()){
1034 static void add_name_list(std::string& temp_string, std::string& weapon_abilities, std::set<std::string>& checking_name,
const std::string& from_str)
1036 if(!temp_string.empty()){
1037 temp_string = from_str.c_str() + temp_string;
1038 weapon_abilities += (!weapon_abilities.empty() && !temp_string.empty()) ?
"\n" :
"";
1039 weapon_abilities += temp_string;
1040 temp_string.clear();
1041 checking_name.clear();
1048 std::string temp_string, weapon_abilities;
1049 std::set<std::string> checking_name;
1050 for(
const auto [key, cfg] : specials_.all_children_view()) {
1052 const bool active = special_active(cfg, AFFECT_SELF, key);
1053 add_name(temp_string, active, cfg[
"name"].str(), checking_name);
1056 add_name_list(temp_string, weapon_abilities, checking_name,
"");
1058 weapon_specials_impl_self(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name,
checking_tags,
true);
1059 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Owned: "));
1061 weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name,
checking_tags,
"affect_allies",
true);
1063 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Taught: "));
1065 weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name,
checking_tags,
"affect_enemies",
true);
1067 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Taught: (by an enemy): "));
1071 for(
const auto [key, cfg] : other_attack_->specials_.all_children_view()) {
1073 const bool active = other_attack_->special_active(cfg, AFFECT_OTHER, key);
1074 add_name(temp_string, active, cfg[
"name"].str(), checking_name);
1078 weapon_specials_impl_self(temp_string, other_, other_attack_, shared_from_this(), other_loc_, AFFECT_OTHER, checking_name,
checking_tags);
1079 weapon_specials_impl_adj(temp_string, other_, other_attack_, shared_from_this(), other_loc_, AFFECT_OTHER, checking_name,
checking_tags);
1080 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Used by opponent: "));
1082 return weapon_abilities;
1086 std::string& temp_string,
1092 std::set<std::string>& checking_name,
1097 for(
const auto [key, cfg] : self->abilities().all_children_view()){
1099 const bool active = tag_checked && check_self_abilities_impl(self_attack, other_attack, cfg,
self, self_loc, whom, key, leader_bool);
1100 add_name(temp_string, active, cfg[
"name"].str(), checking_name);
1106 std::string& temp_string,
1112 std::set<std::string>& checking_name,
1114 const std::string& affect_adjacents,
1117 const unit_map& units = get_unit_map();
1120 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
1122 if (it == units.
end() || it->incapacitated())
1124 if(&*it ==
self.
get())
1126 for(
const auto [key, cfg] : it->abilities().all_children_view()) {
1128 bool default_bool = (affect_adjacents ==
"affect_allies") ?
true :
false;
1129 bool affect_allies = (!affect_adjacents.empty()) ? cfg[affect_adjacents].to_bool(default_bool) :
true;
1130 const bool active = tag_checked && check_adj_abilities_impl(self_attack, other_attack, cfg,
self, *it,
i, self_loc, whom, key, leader_bool) && affect_allies;
1131 add_name(temp_string, active, cfg[
"name"].str(), checking_name);
1157 : parent(weapon.shared_from_this())
1159 weapon.
self_ = std::move(
self);
1160 weapon.
other_ = std::move(other);
1177 : parent(weapon.shared_from_this())
1179 weapon.
self_ = std::move(
self);
1197 : parent(weapon.shared_from_this())
1209 : parent(weapon.shared_from_this())
1217 if(was_moved)
return;
1222 parent->is_attacker_ =
false;
1223 parent->other_attack_ =
nullptr;
1224 parent->is_for_listing_ =
false;
1228 : parent(other.parent)
1230 other.was_moved =
true;
1241 unsigned & max_attacks)
const
1246 if ( attacks_value < 0 ) {
1248 ERR_NG <<
"negative number of strikes after applying weapon specials";
1253 if ( !swarm_specials.
empty() ) {
1254 min_attacks = std::max<int>(0, swarm_specials.
highest(
"swarm_attacks_min").first);
1255 max_attacks = std::max<int>(0, swarm_specials.
highest(
"swarm_attacks_max", attacks_value).first);
1257 min_attacks = max_attacks = attacks_value;
1263 std::map<std::string, unsigned int> type_count;
1264 unsigned int max = 0;
1265 for(
auto&
i : damage_type_list) {
1267 if(
c.has_attribute(
"replacement_type")) {
1268 std::string
type =
c[
"replacement_type"].str();
1269 unsigned int count = ++type_count[
type];
1276 if (type_count.empty())
return "";
1278 std::vector<std::string> type_list;
1279 for(
auto&
i : type_count){
1280 if(
i.second == max){
1281 type_list.push_back(
i.first);
1285 if(type_list.empty())
return "";
1287 return type_list.front();
1292 std::map<std::string, int> type_res;
1293 int max_res = std::numeric_limits<int>::min();
1294 for(
auto&
i : damage_type_list) {
1296 if(
c.has_attribute(
"alternative_type")) {
1297 std::string
type =
c[
"alternative_type"].str();
1298 if(type_res.count(
type) == 0){
1300 max_res = std::max(max_res, type_res[
type]);
1305 if (type_res.empty())
return "";
1307 std::vector<std::string> type_list;
1308 for(
auto&
i : type_res){
1309 if(
i.second == max_res){
1310 type_list.push_back(
i.first);
1313 if(type_list.empty())
return "";
1315 return type_list.front();
1320 bool is_alternative = (key_name ==
"alternative_type");
1321 if(is_alternative &&
other_){
1323 }
else if(!is_alternative){
1338 if(damage_type_list.
empty()){
1339 return {
type(),
""};
1344 resistance_list = (*other_).get_abilities_weapons(
"resistance",
other_loc_,
other_attack_, shared_from_this());
1346 std::string replacement_type =
select_damage_type(damage_type_list,
"replacement_type", resistance_list);
1347 std::string alternative_type =
select_damage_type(damage_type_list,
"alternative_type", resistance_list);
1348 std::string type_damage = replacement_type.empty() ?
type() : replacement_type;
1349 if(!alternative_type.empty() && type_damage != alternative_type){
1350 return {type_damage, alternative_type};
1352 return {type_damage,
""};
1358 if(damage_type_list.
empty()){
1361 std::set<std::string> damage_types;
1362 for(
auto&
i : damage_type_list) {
1364 if(
c.has_attribute(
"alternative_type")){
1365 damage_types.insert(
c[
"alternative_type"].str());
1369 return damage_types;
1379 return damage_value;
1391 bool special_affects_opponent(
const config& special,
bool is_attacker)
1394 const std::string& apply_to = special[
"apply_to"];
1395 if ( apply_to.empty() )
1397 if ( apply_to ==
"both" )
1399 if ( apply_to ==
"opponent" )
1401 if ( is_attacker && apply_to ==
"defender" )
1403 if ( !is_attacker && apply_to ==
"attacker" )
1413 bool special_affects_self(
const config& special,
bool is_attacker)
1416 const std::string& apply_to = special[
"apply_to"];
1417 if ( apply_to.empty() )
1419 if ( apply_to ==
"both" )
1421 if ( apply_to ==
"self" )
1423 if ( is_attacker && apply_to ==
"attacker" )
1425 if ( !is_attacker && apply_to ==
"defender")
1440 static std::vector<std::tuple<std::string, std::string>> already_shown;
1442 auto identifier = std::tuple<std::string, std::string>{attack->id(), filter.
debug()};
1447 std::string_view filter_text_view = std::get<1>(identifier);
1449 ERR_NG <<
"Looped recursion error for weapon '" << attack->id()
1450 <<
"' while checking weapon special '" << filter_text_view <<
"'";
1454 if(already_shown.size() > 100) {
1455 already_shown.clear();
1457 already_shown.push_back(std::move(identifier));
1477 const bool for_listing,
1478 const std::string & child_tag,
const std::string& check_if_recursion)
1489 if (!filter[
"backstab"].
blank() && child_tag ==
"filter_opponent") {
1490 deprecated_message(
"backstab= in weapon specials",
DEP_LEVEL::INDEFINITE,
"",
"Use [filter_opponent] with a formula instead; the code can be found in data/core/macros/ in the WEAPON_SPECIAL_BACKSTAB macro.");
1493 if(filter[
"backstab"].to_bool() && child_tag ==
"filter_opponent"){
1494 const std::string& backstab_formula =
"enemy_of(self, flanker) and not flanker.petrified where flanker = unit_at(direction_from(loc, other.facing))";
1496 if(!filter.
has_child(
"filter_opponent")){
1497 filter_child[
"formula"] = backstab_formula;
1500 filter_opponent[
"formula"] = backstab_formula;
1501 filter_child.
add_child(
"and", filter_opponent);
1504 const config& filter_backstab = filter[
"backstab"].to_bool() ? cfg : filter;
1507 if ( !filter_child )
1523 filter_lock = weapon->update_variables_recursion(filter);
1525 show_recursion_warning(weapon, filter);
1530 if (
auto filter_weapon = filter_child->
optional_child(
"filter_weapon") ) {
1531 if ( !weapon || !weapon->matches_filter(*filter_weapon, check_if_recursion) )
1538 return ufilt.matches(*u,
loc);
1540 return ufilt.matches(*u,
loc, *u2);
1558 return special_active(*i.ability_cfg, AFFECT_SELF, ability,
"filter_student");
1564 return special_active_impl(other_attack_, shared_from_this(), *i.ability_cfg, AFFECT_OTHER, ability,
"filter_student");
1579 if(!abil_list.
empty() && !overwriters.
empty()){
1595 const std::string& apply_to = special[
"overwrite_specials"];
1596 return (apply_to ==
"one_side" || apply_to ==
"both_sides");
1607 if(overwriters.
empty()){
1612 if(overwriters.
size() >= 2){
1615 auto oi = (*
i.ability_cfg).optional_child(
"overwrite");
1617 if(oi && !oi[
"priority"].empty()){
1618 l = oi[
"priority"].to_double(0);
1620 auto oj = (*j.
ability_cfg).optional_child(
"overwrite");
1622 if(oj && !oj[
"priority"].empty()){
1623 r = oj[
"priority"].to_double(0);
1637 if(overwriters.
empty()){
1641 for(
const auto& j : overwriters) {
1643 bool affect_side = ((*j.ability_cfg)[
"overwrite_specials"] ==
"one_side");
1645 auto overwrite_specials = (*j.ability_cfg).optional_child(
"overwrite");
1646 double priority = overwrite_specials ? overwrite_specials[
"priority"].to_double(0) : 0.00;
1651 bool prior = (priority > 0) ? (has_overwrite_specials && has_overwrite_specials[
"priority"].to_double(0) >= priority) :
true;
1655 bool one_side_overwritable =
true;
1659 if(affect_side && is_overwritable){
1660 if(special_affects_self(*j.ability_cfg,
is_attacker_)){
1661 one_side_overwritable = special_affects_self(cfg,
is_attacker_);
1663 else if(special_affects_opponent(*j.ability_cfg, !
is_attacker_)){
1664 one_side_overwritable = special_affects_opponent(cfg, !
is_attacker_);
1669 bool special_matches =
true;
1670 if(overwrite_specials){
1671 auto overwrite_filter = (*overwrite_specials).optional_child(
"filter_specials");
1672 if(!overwrite_filter){
1673 overwrite_filter = (*overwrite_specials).optional_child(
"experimental_filter_specials");
1674 if(overwrite_filter){
1678 if(overwrite_filter && is_overwritable && one_side_overwritable){
1686 if(is_overwritable && one_side_overwritable && special_matches){
1705 std::vector<special_match>& id_result,
1706 const config& parent,
const std::string&
id,
1707 bool special_id=
true,
bool special_tags=
true) {
1708 if(special_id && special_tags){
1709 get_special_children(tag_result, id_result, parent,
id);
1710 }
else if(special_id && !special_tags){
1711 get_special_children_id(id_result, parent,
id);
1712 }
else if(!special_id && special_tags){
1713 get_special_children_tags(tag_result, parent,
id);
1721 show_recursion_warning(*
this, cfg);
1724 return (ability_active_impl(ability, cfg,
loc) && ability_affects_self(ability, cfg,
loc));
1731 show_recursion_warning(from, cfg);
1735 return (affects_side(cfg, side(), from.
side()) && from.
ability_active_impl(ability, cfg, adjacent[dir]) && ability_affects_adjacent(ability, cfg, dir,
loc, from));
1740 return (get_self_ability_bool(special, tag_name,
loc) && ability_affects_weapon(special, weapon,
false) && ability_affects_weapon(special, opp_weapon,
true));
1745 return (get_adj_ability_bool(special, tag_name, dir,
loc, from) && ability_affects_weapon(special, weapon,
false) && ability_affects_weapon(special, opp_weapon,
true));
1755 if(tag_name ==
"leadership" && leader_bool){
1756 if((*u).get_self_ability_bool_weapon(special, tag_name,
loc, self_attack, other_attack)) {
1760 if((*u).checking_tags().count(tag_name) != 0){
1761 if((*u).get_self_ability_bool(special, tag_name,
loc) &&
special_active_impl(self_attack, other_attack, special, whom, tag_name,
"filter_student")) {
1775 if(tag_name ==
"leadership" && leader_bool){
1776 if((*u).get_adj_ability_bool_weapon(special, tag_name, dir,
loc, from, self_attack, other_attack)) {
1780 if((*u).checking_tags().count(tag_name) != 0){
1781 if((*u).get_adj_ability_bool(special, tag_name, dir,
loc, from) &&
special_active_impl(self_attack, other_attack, special, whom, tag_name,
"filter_student")) {
1795 const unit_map& units = get_unit_map();
1797 std::vector<special_match> special_tag_matches_self;
1798 std::vector<special_match> special_id_matches_self;
1799 get_ability_children(special_tag_matches_self, special_id_matches_self, (*self_).abilities(), special, special_id , special_tags);
1801 for(
const special_match& entry : special_tag_matches_self) {
1808 for(
const special_match& entry : special_id_matches_self) {
1816 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
1818 if (it == units.
end() || it->incapacitated())
1820 if ( &*it ==
self_.get() )
1823 std::vector<special_match> special_tag_matches_adj;
1824 std::vector<special_match> special_id_matches_adj;
1825 get_ability_children(special_tag_matches_adj, special_id_matches_adj, it->abilities(), special, special_id , special_tags);
1827 for(
const special_match& entry : special_tag_matches_adj) {
1834 for(
const special_match& entry : special_id_matches_adj) {
1844 std::vector<special_match> special_tag_matches_other;
1845 std::vector<special_match> special_id_matches_other;
1846 get_ability_children(special_tag_matches_other, special_id_matches_other, (*other_).abilities(), special, special_id , special_tags);
1848 for(
const special_match& entry : special_tag_matches_other) {
1856 for(
const special_match& entry : special_id_matches_other) {
1864 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
1866 if (it == units.
end() || it->incapacitated())
1868 if ( &*it ==
other_.get() )
1871 std::vector<special_match> special_tag_matches_oadj;
1872 std::vector<special_match> special_id_matches_oadj;
1873 get_ability_children(special_tag_matches_oadj, special_id_matches_oadj, it->abilities(), special, special_id , special_tags);
1875 for(
const special_match& entry : special_tag_matches_oadj) {
1883 for(
const special_match& entry : special_id_matches_oadj) {
1899 if(
range().empty()){
1908 bool exclude_ability_attributes(
const std::string& tag_name,
const config & filter)
1911 bool abilities_check = abilities_list::ability_value_tags().count(tag_name) != 0 || abilities_list::ability_no_value_tags().count(tag_name) != 0;
1912 if(filter.
has_attribute(
"active_on") && tag_name !=
"resistance" && abilities_check)
1914 if(filter.
has_attribute(
"apply_to") && tag_name !=
"resistance" && abilities_check)
1917 if(filter.
has_attribute(
"overwrite_specials") && abilities_list::weapon_number_tags().count(tag_name) == 0)
1920 bool no_value_weapon_abilities_check = abilities_list::no_weapon_number_tags().count(tag_name) != 0 || abilities_list::ability_no_value_tags().count(tag_name) != 0;
1921 if(filter.
has_attribute(
"value") && no_value_weapon_abilities_check)
1923 if(filter.
has_attribute(
"add") && no_value_weapon_abilities_check)
1925 if(filter.
has_attribute(
"sub") && no_value_weapon_abilities_check)
1927 if(filter.
has_attribute(
"multiply") && no_value_weapon_abilities_check)
1929 if(filter.
has_attribute(
"divide") && no_value_weapon_abilities_check)
1932 bool all_engine = abilities_list::no_weapon_number_tags().count(tag_name) != 0 || abilities_list::weapon_number_tags().count(tag_name) != 0 || abilities_list::ability_value_tags().count(tag_name) != 0 || abilities_list::ability_no_value_tags().count(tag_name) != 0;
1933 if(filter.
has_attribute(
"replacement_type") && tag_name !=
"damage_type" && all_engine)
1935 if(filter.
has_attribute(
"alternative_type") && tag_name !=
"damage_type" && all_engine)
1937 if(filter.
has_attribute(
"type") && tag_name !=
"plague" && all_engine)
1943 bool matches_ability_filter(
const config & cfg,
const std::string& tag_name,
const config & filter)
1948 if(!exclude_ability_attributes(tag_name, filter))
1953 const std::vector<std::string> filter_type =
utils::split(filter[
"tag_name"]);
1954 if ( !filter_type.empty() &&
std::find(filter_type.begin(), filter_type.end(), tag_name) == filter_type.end() )
1962 if(!filter[
"affect_adjacent"].empty()){
1963 bool adjacent = cfg.
has_child(
"affect_adjacent");
1964 if(filter[
"affect_adjacent"].to_bool() != adjacent){
1996 if(!filter[
"value"].empty()){
1997 if(tag_name ==
"drains"){
2001 }
else if(tag_name ==
"berserk"){
2005 }
else if(tag_name ==
"heal_on_hit" || tag_name ==
"heals" || tag_name ==
"regenerate" || tag_name ==
"leadership"){
2031 if(tag_name ==
"resistance"){
2071 static bool common_matches_filter(
const config & cfg,
const std::string& tag_name,
const config & filter)
2074 bool matches = matches_ability_filter(cfg, tag_name, filter);
2081 matches = matches && common_matches_filter(cfg, tag_name, condition_cfg);
2084 else if ( key ==
"or" )
2085 matches = matches || common_matches_filter(cfg, tag_name, condition_cfg);
2088 else if ( key ==
"not" )
2089 matches = matches && !common_matches_filter(cfg, tag_name, condition_cfg);
2098 return common_matches_filter(cfg, tag_name, filter);
2103 return common_matches_filter(cfg, tag_name, filter);
2109 bool check_if_active = filter[
"active"].to_bool();
2110 for(
const auto [key, cfg] :
specials().all_children_view()) {
2112 if(!check_if_active){
2125 for(
const auto [key, cfg] :
other_attack_->specials().all_children_view()) {
2126 if(
other_attack_->special_matches_filter(cfg, key, filter)){
2138 if(!filter[
"active"].to_bool()){
2141 const unit_map& units = get_unit_map();
2143 for(
const auto [key, cfg] : (*self_).abilities().all_children_view()) {
2144 if(
self_->ability_matches_filter(cfg, key, filter)){
2152 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
2154 if (it == units.
end() || it->incapacitated())
2156 if ( &*it ==
self_.get() )
2159 for(
const auto [key, cfg] : it->abilities().all_children_view()) {
2168 for(
const auto [key, cfg] : (*other_).abilities().all_children_view()) {
2175 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
2177 if (it == units.
end() || it->incapacitated())
2179 if ( &*it ==
other_.get() )
2182 for(
const auto [key, cfg] : it->abilities().all_children_view()) {
2183 if(it->ability_matches_filter(cfg, key, filter) &&
check_adj_abilities_impl(
other_attack_, shared_from_this(), cfg,
other_, *it,
i,
other_loc_,
AFFECT_OTHER, key)){
2194 if(
range().empty()){
2201 const std::string& filter_self)
const
2221 const std::string& tag_name,
2222 const std::string& filter_self)
2224 assert(self_attack || other_attack);
2225 bool is_attacker = self_attack ? self_attack->is_attacker_ : !other_attack->is_attacker_;
2226 bool is_for_listing = self_attack ? self_attack->is_for_listing_ : other_attack->is_for_listing_;
2232 if ( !special_affects_self(special, is_attacker) )
2236 if ( !special_affects_opponent(special, is_attacker) )
2241 const std::string & active_on = special[
"active_on"];
2242 if ( !active_on.empty() ) {
2243 if ( is_attacker && active_on !=
"offense" )
2245 if ( !is_attacker && active_on !=
"defense" )
2250 const unit_map& units = get_unit_map();
2252 unit_const_ptr self = self_attack ? self_attack->self_ : other_attack->other_;
2253 unit_const_ptr other = self_attack ? self_attack->other_ : other_attack->self_;
2254 map_location self_loc = self_attack ? self_attack->self_loc_ : other_attack->other_loc_;
2255 map_location other_loc = self_attack ? self_attack->other_loc_ : other_attack->self_loc_;
2257 if(
self ==
nullptr) {
2263 if(other ==
nullptr) {
2272 temporary_facing other_facing(other, other_loc.
get_relative_dir(self_loc));
2276 bool whom_is_self = ((whom ==
AFFECT_SELF) || ((whom ==
AFFECT_EITHER) && special_affects_self(special, is_attacker)));
2278 map_location their_loc = whom_is_self ? other_loc : self_loc;
2280 if (tag_name ==
"drains" && them && them->get_state(
"undrainable")) {
2283 if (tag_name ==
"plague" && them &&
2284 (them->get_state(
"unplagueable") ||
2288 if (tag_name ==
"poison" && them &&
2292 if (tag_name ==
"slow" && them &&
2296 if (tag_name ==
"petrifies" && them &&
2297 them->get_state(
"unpetrifiable")) {
2305 const map_location & att_loc = is_attacker ? self_loc : other_loc;
2306 const map_location & def_loc = is_attacker ? other_loc : self_loc;
2307 const const_attack_ptr& att_weapon = is_attacker ? self_attack : other_attack;
2308 const const_attack_ptr& def_weapon = is_attacker ? other_attack : self_attack;
2312 if (tag_name ==
"firststrike") {
2313 bool whom_is_defender = whom_is_self ? !is_attacker : is_attacker;
2314 if (whom_is_defender && att_weapon && att_weapon->has_special_or_ability(
"firststrike"))
2323 bool applied_both = special[
"apply_to"] ==
"both";
2324 std::string self_check_if_recursion = (applied_both || whom_is_self) ? tag_name :
"";
2325 if (!special_unit_matches(
self, other, self_loc, self_attack, special, is_for_listing, filter_self, self_check_if_recursion))
2327 std::string opp_check_if_recursion = (applied_both || !whom_is_self) ? tag_name :
"";
2328 if (!special_unit_matches(other,
self, other_loc, other_attack, special, is_for_listing,
"filter_opponent", opp_check_if_recursion))
2332 bool applied_to_attacker = applied_both || (whom_is_self && is_attacker) || (!whom_is_self && !is_attacker);
2333 std::string att_check_if_recursion = applied_to_attacker ? tag_name :
"";
2334 if (!special_unit_matches(att, def, att_loc, att_weapon, special, is_for_listing,
"filter_attacker", att_check_if_recursion))
2336 bool applied_to_defender = applied_both || (whom_is_self && !is_attacker) || (!whom_is_self && is_attacker);
2337 std::string def_check_if_recursion= applied_to_defender ? tag_name :
"";
2338 if (!special_unit_matches(def, att, def_loc, def_weapon, special, is_for_listing,
"filter_defender", def_check_if_recursion))
2346 std::size_t count = 0;
2356 if (
i.has_attribute(
"is_enemy")) {
2364 if (
i[
"count"].empty() && count != dirs.size()) {
2375 std::size_t count = 0;
2382 if(!adj_filter.
match(adjacent[
static_cast<int>(
index)])) {
2387 if (
i[
"count"].empty() && count != dirs.size()) {
2413 if (
auto apply_filter = cfg.
optional_child(
"filter_base_value")) {
2420 return (cond_eq.
empty() || def == cond_eq.
to_int()) &&
2436 std::map<std::string,individual_effect> values_add;
2437 std::map<std::string,individual_effect> values_sub;
2438 std::map<std::string,individual_effect> values_mul;
2439 std::map<std::string,individual_effect> values_div;
2443 utils::optional<int> max_value = utils::nullopt;
2444 utils::optional<int> min_value = utils::nullopt;
2447 const config& cfg = *ability.ability_cfg;
2448 const std::string& effect_id = cfg[cfg[
"id"].
empty() ?
"name" :
"id"];
2456 callable.add(
"base_value", wfl::variant(def));
2457 return formula.evaluate(callable).as_int();
2460 int value_cum = cfg[
"cumulative"].to_bool() ? std::max(def, value) : value;
2463 set_effect_min.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2464 set_effect_max.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2467 if(value_cum > set_effect_max.
value) {
2468 set_effect_max.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2470 if(value_cum < set_effect_min.
value) {
2471 set_effect_min.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2479 max_value = max_value ? std::min(*max_value, cfg[
"max_value"].to_int()) : cfg[
"max_value"].to_int();
2482 min_value = min_value ? std::max(*min_value, cfg[
"min_value"].to_int()) : cfg[
"min_value"].to_int();
2488 callable.add(
"base_value", wfl::variant(def));
2489 return formula.evaluate(callable).as_int();
2492 if(add_effect == values_add.end() || add > add_effect->second.value) {
2493 values_add[effect_id].set(
ADD, add, ability.ability_cfg, ability.teacher_loc);
2498 callable.add(
"base_value", wfl::variant(def));
2499 return formula.evaluate(callable).as_int();
2502 if(sub_effect == values_sub.end() || sub < sub_effect->second.value) {
2503 values_sub[effect_id].set(
ADD, sub, ability.ability_cfg, ability.teacher_loc);
2508 callable.add(
"base_value", wfl::variant(def));
2509 return formula.evaluate(callable).as_decimal() / 1000.0 ;
2512 if(mul_effect == values_mul.end() || multiply > mul_effect->second.value) {
2513 values_mul[effect_id].set(
MUL, multiply, ability.ability_cfg, ability.teacher_loc);
2518 callable.add(
"base_value", wfl::variant(def));
2519 return formula.evaluate(callable).as_decimal() / 1000.0 ;
2523 ERR_NG <<
"division by zero with divide= in ability/weapon special " << effect_id;
2527 if(div_effect == values_div.end() || divide > div_effect->second.value) {
2528 values_div[effect_id].set(
DIV, divide, ability.ability_cfg, ability.teacher_loc);
2535 value_set = std::max(set_effect_max.
value, 0) + std::min(set_effect_min.
value, 0);
2536 if(set_effect_max.
value > def) {
2539 if(set_effect_min.
value < def) {
2552 double multiplier = 1.0;
2553 double divisor = 1.0;
2555 for(
const auto& val : values_mul) {
2556 multiplier *= val.second.value/100.0;
2560 for(
const auto& val : values_div) {
2561 divisor *= val.second.value/100.0;
2566 for(
const auto& val : values_add) {
2567 addition += val.second.value;
2573 int substraction = 0;
2574 for(
const auto& val : values_sub) {
2575 substraction += val.second.value;
2579 composite_value_ =
static_cast<int>((value_set + addition + substraction) * multiplier / divisor);
2581 if(max_value && min_value && *min_value < *max_value) {
2583 }
else if(max_value && !min_value) {
2585 }
else if(min_value && !max_value) {
static std::string select_replacement_type(const unit_ability_list &damage_type_list)
static void add_name(std::string &temp_string, bool active, const std::string &name, std::set< std::string > &checking_name)
static lg::log_domain log_engine("engine")
static void add_string_to_vector(std::vector< std::string > &image_list, const config &cfg, const std::string &attribute_name)
static void add_name_list(std::string &temp_string, std::string &weapon_abilities, std::set< std::string > &checking_name, const std::string &from_str)
static std::string select_alternative_type(const unit_ability_list &damage_type_list, const unit_ability_list &resistance_list, const unit &u)
static bool overwrite_special_affects(const config &special)
static lg::log_domain log_wml("wml")
static void get_ability_children(std::vector< special_match > &tag_result, std::vector< special_match > &id_result, const config &parent, const std::string &id, bool special_id=true, bool special_tags=true)
Gets the children of parent (which should be the abilities for an attack_type) and places the ones wh...
Helper similar to std::unique_lock for detecting when calculations such as has_special have entered i...
specials_context_t(const attack_type &weapon, bool attacking)
Initialize weapon specials context for listing.
const config & specials() const
std::string weapon_specials() const
Returns a comma-separated string of active names for the specials of *this.
bool check_self_abilities(const config &cfg, const std::string &special) const
check_self_abilities : return an boolean value for checking of activities of abilities used like weap...
bool has_special(const std::string &special, bool simple_check=false, bool special_id=true, bool special_tags=true) const
Returns whether or not *this has a special with a tag or id equal to special.
std::string weapon_specials_value(const std::set< std::string > &checking_tags) const
int composite_value(const unit_ability_list &abil_list, int base_value) const
Return the special weapon value, considering specials.
const_attack_ptr other_attack_
void add_formula_context(wfl::map_formula_callable &) const
const std::string & range() const
bool has_special_or_ability_with_filter(const config &filter) const
const std::string & type() const
static bool special_active_impl(const const_attack_ptr &self_attack, const const_attack_ptr &other_attack, const config &special, AFFECTS whom, const std::string &tag_name, const std::string &filter_self="filter_self")
Returns whether or not the given special is active for the specified unit, based on the current conte...
unit_ability_list get_weapon_ability(const std::string &ability) const
Returns list for weapon like abilities for each ability type.
std::string select_damage_type(const unit_ability_list &damage_type_list, const std::string &key_name, const unit_ability_list &resistance_list) const
Select best damage type based on frequency count for replacement_type and based on highest damage for...
bool has_ability_with_filter(const config &filter) const
unit_ability_list get_specials_and_abilities(const std::string &special) const
bool overwrite_special_checking(unit_ability_list &overwriters, const config &cfg, const std::string &tag_name) const
Check whether cfg would be overwritten by any element of overwriters.
bool check_adj_abilities(const config &cfg, const std::string &special, int dir, const unit &from) const
check_adj_abilities : return an boolean value for checking of activities of abilities used like weapo...
static void weapon_specials_impl_adj(std::string &temp_string, const unit_const_ptr &self, const const_attack_ptr &self_attack, const const_attack_ptr &other_attack, const map_location &self_loc, AFFECTS whom, std::set< std::string > &checking_name, const std::set< std::string > &checking_tags={}, const std::string &affect_adjacents="", bool leader_bool=false)
unit_ability_list overwrite_special_overwriter(unit_ability_list overwriters, const std::string &tag_name) const
Filter a list of abilities or weapon specials, removing any entries that don't own the overwrite_spec...
recursion_guard update_variables_recursion(const config &special) const
Tests which might otherwise cause infinite recursion should call this, check that the returned object...
std::vector< std::pair< t_string, t_string > > special_tooltips(boost::dynamic_bitset<> *active_list=nullptr) const
Returns a vector of names and descriptions for the specials of *this.
static void weapon_specials_impl_self(std::string &temp_string, const unit_const_ptr &self, const const_attack_ptr &self_attack, const const_attack_ptr &other_attack, const map_location &self_loc, AFFECTS whom, std::set< std::string > &checking_name, const std::set< std::string > &checking_tags={}, bool leader_bool=false)
weapon_specials_impl_self and weapon_specials_impl_adj : check if special name can be added.
std::set< std::string > alternative_damage_types() const
static bool check_self_abilities_impl(const const_attack_ptr &self_attack, const const_attack_ptr &other_attack, const config &special, const unit_const_ptr &u, const map_location &loc, AFFECTS whom, const std::string &tag_name, bool leader_bool=false)
check_self_abilities_impl : return an boolean value for checking of activities of abilities used like...
bool attack_empty() const
Returns true if this is a dummy attack_type, for example the placeholder that the unit_attack dialog ...
bool special_matches_filter(const config &cfg, const std::string &tag_name, const config &filter) const
Filter a list of abilities or weapon specials.
int modified_damage() const
Returns the damage per attack of this weapon, considering specials.
void modified_attacks(unsigned &min_attacks, unsigned &max_attacks) const
Calculates the number of attacks this weapon has, considering specials.
static bool check_adj_abilities_impl(const const_attack_ptr &self_attack, const const_attack_ptr &other_attack, const config &special, const unit_const_ptr &u, const unit &from, int dir, const map_location &loc, AFFECTS whom, const std::string &tag_name, bool leader_bool=false)
check_adj_abilities_impl : return an boolean value for checking of activities of abilities used like ...
bool has_special_with_filter(const config &filter) const
check if special matche
bool special_active(const config &special, AFFECTS whom, const std::string &tag_name, const std::string &filter_self="filter_self") const
unit_ability_list get_specials(const std::string &special) const
Returns the currently active specials as an ability list, given the current context (see set_specials...
bool has_special_or_ability(const std::string &special, bool special_id=true, bool special_tags=true) const
used for abilities used like weapon and true specials
bool has_weapon_ability(const std::string &special, bool special_id=true, bool special_tags=true) const
used for abilities used like weapon
std::pair< std::string, std::string > damage_type() const
return a modified damage type and/or add a secondary_type for hybrid use if special is active.
Variant for storing WML attributes.
std::string str(const std::string &fallback="") const
int to_int(int def=0) const
auto apply_visitor(const V &visitor) const
Visitor support: Applies a visitor to the underlying variant.
bool blank() const
Tests for an attribute that was never set.
bool empty() const
Tests for an attribute that either was never set or was set to "".
A config object defines a single node in a WML file, with access to child nodes.
const attribute_value & get_or(const config_key_type key, const config_key_type default_key) const
Chooses a value.
config & mandatory_child(config_key_type key, int n=0)
Returns the nth child with the given key, or throws an error if there is none.
bool matches(const config &filter) const
auto all_children_view() const
In-order iteration over all children.
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
bool has_attribute(config_key_type key) const
child_itors child_range(config_key_type key)
config & child_or_add(config_key_type key)
Returns a reference to the first child with the given key.
std::string debug() const
const attribute_value * get(config_key_type key) const
Returns a pointer to the attribute with the given key or nullptr if it does not exist.
optional_config_impl< config > optional_child(config_key_type key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
config & add_child(config_key_type key)
Abstract class for exposing game data that doesn't depend on the GUI, however which for historical re...
const team & get_team(int side) const
This getter takes a 1-based side number, not a 0-based team number.
virtual const unit_map & units() const =0
const display_context & context() const
static display * get_singleton()
Returns the display object if a display object exists.
virtual const display_context & get_disp_context() const =0
virtual const unit_map & units() const override
virtual const gamemap & map() const override
bool is_village(const map_location &loc) const
This class stores all the data for a single 'side' (in game nomenclature).
bool is_enemy(int n) const
bool match(const map_location &loc) const
Helper similar to std::unique_lock for detecting when calculations such as abilities have entered inf...
int get_composite_value() const
std::vector< individual_effect > effect_list_
void append(const unit_ability_list &other)
Appends the abilities from other to this, ignores other.loc()
std::pair< int, map_location > lowest(const std::string &key, int def=0) const
const map_location & loc() const
void emplace_back(T &&... args)
std::pair< int, map_location > highest(const std::string &key, int def=0) const
void append_if(const unit_ability_list &other, const Predicate &predicate)
Appends any abilities from other for which the given condition returns true to this,...
std::pair< int, map_location > get_extremum(const std::string &key, int def, const TComp &comp) const
bool matches(const unit &u, const map_location &loc) const
Determine if *this matches filter at a specified location.
unit_filter & set_use_flat_tod(bool value)
Container associating units to locations.
unit_ptr find_unit_ptr(const T &val)
unit_iterator find(std::size_t id)
A single unit type that the player may recruit.
This class represents a single unit of a specific type.
A variable-expanding proxy for the config class.
void swap(config &lhs, config &rhs)
Implement non-member swap function for std::swap (calls config::swap).
std::string deprecated_message(const std::string &elem_name, DEP_LEVEL level, const version_info &version, const std::string &detail)
map_display and display: classes which take care of displaying the map and game-data on the screen.
Interfaces for manipulating version numbers of engine, add-ons, etc.
static std::string _(const char *str)
bool ability_active(const std::string &ability, const config &cfg, const map_location &loc) const
Check if an ability is active.
bool ability_affects_self(const std::string &ability, const config &cfg, const map_location &loc) const
Check if an ability affects the owning unit.
std::vector< const config * > open_queries_
While processing a recursive match, all the filters that are currently being checked,...
bool get_ability_bool(const std::string &tag_name, const map_location &loc) const
Checks whether this unit currently possesses or is affected by a given ability.
bool ability_affects_adjacent(const std::string &ability, const config &cfg, int dir, const map_location &loc, const unit &from) const
Check if an ability affects adjacent units.
std::vector< std::tuple< std::string, t_string, t_string, t_string > > ability_tooltips() const
Gets the names and descriptions of this unit's abilities.
unit_ability_list get_abilities_weapons(const std::string &tag_name, const map_location &loc, const_attack_ptr weapon=nullptr, const_attack_ptr opp_weapon=nullptr) const
bool has_ability_type(const std::string &ability) const
Check if the unit has an ability of a specific type.
unit_ability_list get_abilities(const std::string &tag_name, const map_location &loc) const
Gets the unit's active abilities of a particular type if it were on a specified location.
unit_race::GENDER gender_
bool get_self_ability_bool_weapon(const config &special, const std::string &tag_name, const map_location &loc, const const_attack_ptr &weapon=nullptr, const const_attack_ptr &opp_weapon=nullptr) const
Checks whether this unit currently possesses a given ability of leadership type.
recursion_guard()
Construct an empty instance, only useful for extending the lifetime of a recursion_guard returned fro...
bool get_self_ability_bool(const config &cfg, const std::string &ability, const map_location &loc) const
Checks whether this unit currently possesses a given ability, and that that ability is active.
std::vector< std::string > get_ability_list() const
Get a list of all abilities by ID.
bool ability_active_impl(const std::string &ability, const config &cfg, const map_location &loc) const
Check if an ability is active.
bool get_adj_ability_bool_weapon(const config &special, const std::string &tag_name, int dir, const map_location &loc, const unit &from, const const_attack_ptr &weapon=nullptr, const const_attack_ptr &opp_weapon=nullptr) const
Checks whether this unit is affected by a given ability of leadership type.
recursion_guard update_variables_recursion(const config &ability) const
recursion_guard & operator=(recursion_guard &&)
const std::set< std::string > & checking_tags() const
bool get_adj_ability_bool(const config &cfg, const std::string &ability, int dir, const map_location &loc, const unit &from) const
Checks whether this unit is affected by a given ability, and that that ability is active.
bool ability_matches_filter(const config &cfg, const std::string &tag_name, const config &filter) const
Verify what abilities attributes match with filter.
bool ability_affects_weapon(const config &cfg, const const_attack_ptr &weapon, bool is_opp) const
filters the weapons that condition the use of abilities for combat ([resistance],[leadership] or abil...
const std::string & id() const
Gets this unit's id.
int side() const
The side this unit belongs to.
const t_string & name() const
Gets this unit's translatable display name.
@ STATE_POISONED
The unit is slowed - it moves slower and does less damage.
int resistance_value(unit_ability_list resistance_list, const std::string &damage_name) const
For the provided list of resistance abilities, determine the damage resistance based on which are act...
std::vector< std::string > halo_or_icon_abilities(const std::string &image_type) const
New lexcical_cast header.
void get_adjacent_tiles(const map_location &a, map_location *res)
Function which, given a location, will place all adjacent locations in res.
Standard logging facilities (interface).
void set(CURSOR_TYPE type)
Use the default parameter to reset cursors.
const color_t inactive_details_color
const color_t BUTTON_COLOR
static bool is_active(const widget *wgt)
std::stringstream & log_to_chat()
Use this to show WML errors in the ingame chat.
std::string span_color(const color_t &color, Args &&... data)
filter_context * filter_con
bool filter_base_matches(const config &cfg, int def)
std::size_t index(std::string_view str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Utility functions for implementing [filter], [filter_ability], [filter_weapon], etc.
bool int_matches_if_present(const config &filter, const config &cfg, const std::string &attribute, utils::optional< int > def=utils::nullopt)
bool set_includes_if_present(const config &filter, const config &cfg, const std::string &attribute)
filter[attribute] and cfg[attribute] are assumed to be comma-separated lists.
bool double_matches_if_present(const config &filter, const config &cfg, const std::string &attribute, utils::optional< double > def=utils::nullopt)
Checks whether the filter matches the value of cfg[attribute].
bool int_matches_if_present_or_negative(const config &filter, const config &cfg, const std::string &attribute, const std::string &opposite, utils::optional< int > def=utils::nullopt)
Supports filters using "add" and "sub" attributes, for example a filter add=1 matching a cfg containi...
bool string_matches_if_present(const config &filter, const config &cfg, const std::string &attribute, const std::string &def)
bool bool_or_empty(const config &filter, const config &cfg, const std::string &attribute)
bool bool_matches_if_present(const config &filter, const config &cfg, const std::string &attribute, bool def)
Checks whether the filter matches the value of cfg[attribute].
void trim(std::string_view &s)
bool contains(const Container &container, const Value &value)
Returns true iff value is found in container.
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 erase_if(Container &container, const Predicate &predicate)
Convenience wrapper for using std::remove_if on a container.
void sort_if(Container &container, const Predicate &predicate)
Convenience wrapper for using std::sort on a container.
std::vector< std::string > split(const config_attribute_value &val)
auto * find(Container &container, const Value &value)
Convenience wrapper for using find on a container without needing to comare to end()
std::string::const_iterator iterator
std::shared_ptr< const unit > unit_const_ptr
std::shared_ptr< const attack_type > const_attack_ptr
std::shared_ptr< unit > unit_ptr
const config::attribute_value & gender_value(const config &cfg, unit_race::GENDER gender, const std::string &male_key, const std::string &female_key, const std::string &default_key)
Chooses a value from the given config based on gender.
Encapsulates the map of the game.
static std::vector< direction > parse_directions(const std::string &str)
Parse_directions takes a comma-separated list, and filters out any invalid directions.
direction
Valid directions which can be moved in our hexagonal world.
direction get_relative_dir(const map_location &loc, map_location::RELATIVE_DIR_MODE mode) const
static const map_location & null_location()
void set(value_modifier t, int val, const config *abil, const map_location &l)
Data typedef for unit_ability_list.
map_location student_loc
Used by the formula in the ability.
const config * ability_cfg
The contents of the ability tag, never nullptr.
map_location teacher_loc
The location of the teacher, that is the unit who owns the ability tags (different from student becau...
pointer get_shared_ptr() const
This is exactly the same as operator-> but it's slightly more readable, and can replace &*iter syntax...
static map_location::direction s