39 #include "formula/callable_objects.hpp"
47 #define ERR_NG LOG_STREAM(err, log_engine)
50 #define ERR_WML LOG_STREAM(err, log_wml)
53 class temporary_facing
63 u_->set_facing(new_dir);
69 u_->set_facing(save_dir_);
146 const team& get_team(std::size_t side)
166 bool affects_side(
const config& cfg, std::size_t side, std::size_t other_side)
168 const team& side_team = get_team(side);
170 if(side == other_side)
171 return cfg[
"affect_allies"].to_bool(
true);
173 return cfg[
"affect_enemies"].to_bool();
175 return cfg[
"affect_allies"].to_bool();
190 const unit_map& units = get_unit_map();
193 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
195 if (it == units.
end() || it->incapacitated())
204 for (
const config &j : it->abilities_.child_range(tag_name)) {
205 if (affects_side(j,
side(), it->side()) &&
206 it->ability_active(tag_name, j, adjacent[
i]) &&
230 const unit_map& units = get_unit_map();
233 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
235 if (it == units.
end() || it->incapacitated())
244 for(
const config& j : it->abilities_.child_range(tag_name)) {
245 if(affects_side(j,
side(), it->side())
246 && it->ability_active(tag_name, j, adjacent[
i])
269 std::vector<std::string> res;
272 std::string
id = ab.cfg[
"id"];
274 res.push_back(std::move(
id));
295 ab.
cfg[
"name"].t_str(),
297 ab.
cfg[
"description"].t_str() );
306 "female_name_inactive",
"name_inactive");
326 std::vector<std::tuple<std::string, t_string,t_string,t_string>> res;
330 add_ability_tooltip(ab,
gender_, res,
true);
338 std::vector<std::tuple<std::string, t_string,t_string,t_string>> res;
344 if (add_ability_tooltip(ab,
gender_, res, active))
346 active_list.push_back(active);
361 constexpr
unsigned int UNIT_RECURSION_LIMIT = 3;
369 void show_recursion_warning(
const unit&
unit,
const config& filter) {
375 static std::vector<std::tuple<std::string, std::string>> already_shown;
382 std::string_view filter_text_view = std::get<1>(identifier);
384 ERR_NG <<
"Looped recursion error for unit '" <<
unit.
id()
385 <<
"' while checking ability '" << filter_text_view <<
"'";
389 if(already_shown.size() > 100) {
390 already_shown.clear();
392 already_shown.push_back(std::move(identifier));
407 : parent(u.shared_from_this())
417 unit::recursion_guard::operator bool()
const {
423 assert(
this != &other);
432 assert(parent->num_recursion_ > 0);
433 parent->num_recursion_--;
442 show_recursion_warning(*
this, cfg);
445 bool illuminates = ability ==
"illuminates";
448 if ( !
unit_filter(
vconfig(*afilter)).set_use_flat_tod(illuminates).matches(*
this, loc) )
453 const unit_map& units = get_unit_map();
457 std::size_t count = 0;
459 ufilt.set_use_flat_tod(illuminates);
468 if (!ufilt(*
unit, *
this))
470 if((*this).id() == (*unit).id())
472 if (
i.has_attribute(
"is_enemy")) {
480 if (
i[
"count"].empty() && count != dirs.size()) {
490 std::size_t count = 0;
492 adj_filter.flatten(illuminates);
500 if(!adj_filter.match(adjacent[
index])) {
505 if (
i[
"count"].empty() && count != dirs.size()) {
521 show_recursion_warning(*
this, cfg);
525 bool illuminates = ability ==
"illuminates";
527 assert(dir >=0 && dir <= 5);
532 if (
i.has_attribute(
"adjacent")) {
534 if (std::find(dirs.begin(), dirs.end(), direction) == dirs.end()) {
538 if((*this).id() == from.
id()){
541 auto filter =
i.optional_child(
"filter");
543 unit_filter(
vconfig(*filter)).set_use_flat_tod(illuminates).matches(*
this, loc, from) ) {
557 show_recursion_warning(*
this, cfg);
561 bool affect_self = cfg[
"affect_self"].to_bool(
true);
562 if (!filter || !affect_self)
return affect_self;
568 const std::string filter_tag_name = is_opp ?
"filter_second_weapon" :
"filter_weapon";
576 return weapon->matches_filter(filter);
588 auto ret = std::find(image_list.begin(), image_list.end(), cfg[attribute_name].str());
589 if(ret == image_list.end()){
590 image_list.push_back(cfg[attribute_name].str());
596 std::vector<std::string> image_list;
604 if(!(
sp.cfg)[image_type +
"_image_self"].str().empty() &&
is_active){
609 const unit_map& units = get_unit_map();
614 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
616 if (it == units.
end() || it->incapacitated())
621 if(!(j.cfg)[image_type +
"_image"].str().empty() && affects_side(j.cfg,
side(), it->side()) && it->ability_active(j.key, j.cfg, adjacent[
i]) &&
ability_affects_adjacent(j.key, j.cfg,
i,
loc_, *it))
628 if(image_list.size() >= 2){
629 std::sort(image_list.begin(), image_list.end());
637 callable.
add(
"attacker",
wfl::variant(std::make_shared<wfl::unit_callable>(*att)));
640 callable.
add(
"defender",
wfl::variant(std::make_shared<wfl::unit_callable>(*def)));
647 template<
typename T,
typename TFuncFormula>
648 class get_ability_value_visitor
649 #ifdef USING_BOOST_VARIANT
650 :
public boost::static_visitor<T>
655 get_ability_value_visitor(T def,
const TFuncFormula& formula_handler) : def_(def), formula_handler_(formula_handler) {}
657 T operator()(
const utils::monostate&)
const {
return def_; }
658 T operator()(
bool)
const {
return def_; }
659 T operator()(
int i)
const {
return static_cast<T
>(
i); }
660 T operator()(
unsigned long long u)
const {
return static_cast<T
>(u); }
661 T operator()(
double d)
const {
return static_cast<T
>(
d); }
662 T operator()(
const t_string&)
const {
return def_; }
663 T operator()(
const std::string&
s)
const
665 if(
s.size() >= 2 &&
s[0] ==
'(') {
666 return formula_handler_(
s);
668 return lexical_cast_default<T>(
s, def_);
673 const TFuncFormula& formula_handler_;
676 template<
typename T,
typename TFuncFormula>
679 return v.
apply_visitor(get_ability_value_visitor(def, [&](
const std::string&
s) {
682 const unit_map& units = get_unit_map();
686 if(u_itor == units.
end()) {
691 att->add_formula_context(callable);
694 callable.add(
"student",
wfl::variant(std::make_shared<wfl::unit_callable>(*uptr)));
697 callable.add(
"other",
wfl::variant(std::make_shared<wfl::unit_callable>(*uptr)));
701 lg::log_to_chat() <<
"Formula error in ability or weapon special: " <<
e.type <<
" at " <<
e.filename <<
':' <<
e.line <<
")\n";
702 ERR_WML <<
"Formula error in ability or weapon special: " <<
e.type <<
" at " <<
e.filename <<
':' <<
e.line <<
")";
709 template<
typename TComp>
712 if ( cfgs_.empty() ) {
718 bool only_cumulative =
true;
725 return formula.
evaluate(callable).as_int();
728 if ((*
p.ability_cfg)[
"cumulative"].to_bool()) {
730 if (value < 0) value = -value;
731 if (only_cumulative && !comp(value, abs_max)) {
733 best_loc =
p.teacher_loc;
735 }
else if (only_cumulative || comp(flat, value)) {
736 only_cumulative =
false;
738 best_loc =
p.teacher_loc;
741 return std::pair(flat + stack, best_loc);
744 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;
745 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;
779 std::string tag_name;
795 bool get_special_children(std::vector<special_match>& tag_result,
796 std::vector<special_match>& id_result,
797 const config& parent,
const std::string&
id,
798 bool just_peeking=
false) {
801 if (just_peeking && (
sp.key ==
id ||
sp.cfg[
"id"] ==
id)) {
806 special_match special = {
sp.key, &
sp.cfg };
807 tag_result.push_back(special);
809 if(
sp.cfg[
"id"] ==
id) {
810 special_match special = {
sp.key, &
sp.cfg };
811 id_result.push_back(special);
817 bool get_special_children_id(std::vector<special_match>& id_result,
818 const config& parent,
const std::string&
id,
819 bool just_peeking=
false) {
822 if (just_peeking && (
sp.cfg[
"id"] ==
id)) {
826 if(
sp.cfg[
"id"] ==
id) {
827 special_match special = {
sp.key, &
sp.cfg };
828 id_result.push_back(special);
834 bool get_special_children_tags(std::vector<special_match>& tag_result,
835 const config& parent,
const std::string&
id,
836 bool just_peeking=
false) {
839 if (just_peeking && (
sp.key ==
id)) {
844 special_match special = {
sp.key, &
sp.cfg };
845 tag_result.push_back(special);
862 std::vector<special_match> special_tag_matches;
863 std::vector<special_match> special_id_matches;
864 if(special_id && special_tags){
865 if ( get_special_children(special_tag_matches, special_id_matches, specials_, special, simple_check) ) {
868 }
else if(special_id && !special_tags){
869 if ( get_special_children_id(special_id_matches, specials_, special, simple_check) ) {
872 }
else if(!special_id && special_tags){
873 if ( get_special_children_tags(special_tag_matches, specials_, special, simple_check) ) {
881 for(
const special_match& entry : special_tag_matches) {
882 if ( special_active(*entry.cfg, AFFECT_SELF, entry.tag_name) ) {
888 for(
const special_match& entry : special_id_matches) {
889 if ( special_active(*entry.cfg, AFFECT_SELF, entry.tag_name) ) {
897 if ( simple_check || !other_attack_ ) {
901 std::vector<special_match> special_tag_matches;
902 std::vector<special_match> special_id_matches;
903 if(special_id && special_tags){
904 get_special_children(special_tag_matches, special_id_matches, other_attack_->specials_, special);
905 }
else if(special_id && !special_tags){
906 get_special_children_id(special_id_matches, other_attack_->specials_, special);
907 }
else if(!special_id && special_tags){
908 get_special_children_tags(special_tag_matches, other_attack_->specials_, special);
911 for(
const special_match& entry : special_tag_matches) {
912 if ( other_attack_->special_active(*entry.cfg, AFFECT_OTHER, entry.tag_name) ) {
918 for(
const special_match& entry : special_id_matches) {
919 if ( other_attack_->special_active(*entry.cfg, AFFECT_OTHER, entry.tag_name) ) {
934 const map_location loc = self_ ? self_->get_location() : self_loc_;
937 for(
const config&
i : specials_.child_range(special)) {
938 if(special_active(
i, AFFECT_SELF, special)) {
947 for(
const config&
i : other_attack_->specials_.child_range(special)) {
948 if(other_attack_->special_active(
i, AFFECT_OTHER, special)) {
965 boost::dynamic_bitset<>* active_list)
const
968 std::vector<std::pair<t_string, t_string>> res;
970 active_list->clear();
974 if ( !active_list || special_active(
sp.cfg, AFFECT_EITHER,
sp.key) ) {
977 res.emplace_back(
name,
sp.cfg[
"description"].t_str() );
979 active_list->push_back(
true);
982 const t_string&
name =
sp.cfg.get_or(
"name_inactive",
"name").t_str();
984 res.emplace_back(
name,
sp.cfg.get_or(
"description_inactive",
"description").t_str() );
985 active_list->push_back(
false);
1000 static void add_name(std::string& temp_string,
bool active,
const std::string
name, std::set<std::string>& checking_name)
1004 checking_name.insert(
name);
1005 if (!temp_string.empty()) temp_string +=
", ";
1024 const bool active = special_active(
sp.cfg, AFFECT_EITHER,
sp.key);
1026 const std::string&
name =
1028 ?
sp.cfg[
"name"].str()
1029 :
sp.cfg.get_or(
"name_inactive",
"name").str();
1031 if (!res.empty()) res +=
", ";
1034 if (!active) res +=
"</span>";
1037 std::string temp_string;
1038 std::set<std::string> checking_name;
1039 weapon_specials_impl_self(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name);
1040 weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name, {},
"affect_allies");
1041 if(!temp_string.empty() && !res.empty()) {
1042 temp_string =
", \n" + temp_string;
1044 }
else if (!temp_string.empty()){
1050 static void add_name_list(std::string& temp_string, std::string& weapon_abilities, std::set<std::string>& checking_name,
const std::string from_str)
1052 if(!temp_string.empty()){
1053 temp_string = from_str.c_str() + temp_string;
1054 weapon_abilities += (!weapon_abilities.empty() && !temp_string.empty()) ?
"\n" :
"";
1055 weapon_abilities += temp_string;
1056 temp_string.clear();
1057 checking_name.clear();
1064 std::string temp_string, weapon_abilities;
1065 std::set<std::string> checking_name;
1068 const bool active = special_active(
sp.cfg, AFFECT_SELF,
sp.key);
1069 add_name(temp_string, active,
sp.cfg[
"name"].str(), checking_name);
1072 add_name_list(temp_string, weapon_abilities, checking_name,
"");
1074 weapon_specials_impl_self(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name,
checking_tags,
true);
1075 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Owned: "));
1077 weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name,
checking_tags,
"affect_allies",
true);
1079 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Taught: "));
1081 weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name,
checking_tags,
"affect_enemies",
true);
1083 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Taught: (by an enemy): "));
1089 const bool active = other_attack_->special_active(
sp.cfg, AFFECT_OTHER,
sp.key);
1090 add_name(temp_string, active,
sp.cfg[
"name"].str(), checking_name);
1094 weapon_specials_impl_self(temp_string, other_, other_attack_, shared_from_this(), other_loc_, AFFECT_OTHER, checking_name,
checking_tags);
1095 weapon_specials_impl_adj(temp_string, other_, other_attack_, shared_from_this(), other_loc_, AFFECT_OTHER, checking_name,
checking_tags);
1096 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Used by opponent: "));
1098 return weapon_abilities;
1102 std::string& temp_string,
1108 std::set<std::string>& checking_name,
1115 const bool active = tag_checked && check_self_abilities_impl(self_attack, other_attack,
sp.cfg,
self, self_loc, whom,
sp.key, leader_bool);
1116 add_name(temp_string, active,
sp.cfg[
"name"].str(), checking_name);
1122 std::string& temp_string,
1128 std::set<std::string>& checking_name,
1130 const std::string& affect_adjacents,
1133 const unit_map& units = get_unit_map();
1136 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
1138 if (it == units.
end() || it->incapacitated())
1140 if(&*it ==
self.
get())
1144 bool default_bool = (affect_adjacents ==
"affect_allies") ?
true :
false;
1145 bool affect_allies = (!affect_adjacents.empty()) ?
sp.cfg[affect_adjacents].to_bool(default_bool) :
true;
1146 const bool active = tag_checked && check_adj_abilities_impl(self_attack, other_attack,
sp.cfg,
self, *it,
i, self_loc, whom,
sp.key, leader_bool) && affect_allies;
1147 add_name(temp_string, active,
sp.cfg[
"name"].str(), checking_name);
1173 : parent(weapon.shared_from_this())
1175 weapon.
self_ =
self;
1193 : parent(weapon.shared_from_this())
1195 weapon.
self_ =
self;
1213 : parent(weapon.shared_from_this())
1225 : parent(weapon.shared_from_this())
1233 if(was_moved)
return;
1238 parent->is_attacker_ =
false;
1239 parent->other_attack_ =
nullptr;
1240 parent->is_for_listing_ =
false;
1244 : parent(other.parent)
1246 other.was_moved =
true;
1257 unsigned & max_attacks)
const
1262 if ( attacks_value < 0 ) {
1264 ERR_NG <<
"negative number of strikes after applying weapon specials";
1269 if ( !swarm_specials.
empty() ) {
1270 min_attacks = std::max<int>(0, swarm_specials.
highest(
"swarm_attacks_min").first);
1271 max_attacks = std::max<int>(0, swarm_specials.
highest(
"swarm_attacks_max", attacks_value).first);
1273 min_attacks = max_attacks = attacks_value;
1279 std::map<std::string, unsigned int> type_count;
1280 unsigned int max = 0;
1281 for(
auto&
i : damage_type_list) {
1283 if(
c.has_attribute(
"replacement_type")) {
1284 std::string
type =
c[
"replacement_type"].str();
1285 unsigned int count = ++type_count[
type];
1292 if (type_count.empty())
return "";
1294 std::vector<std::string> type_list;
1295 for(
auto&
i : type_count){
1296 if(
i.second == max){
1297 type_list.push_back(
i.first);
1301 if(type_list.empty())
return "";
1303 return type_list.front();
1308 std::map<std::string, int> type_res;
1309 int max_res = std::numeric_limits<int>::min();
1310 for(
auto&
i : damage_type_list) {
1312 if(
c.has_attribute(
"alternative_type")) {
1313 std::string
type =
c[
"alternative_type"].str();
1314 if(type_res.count(
type) == 0){
1316 max_res = std::max(max_res, type_res[
type]);
1321 if (type_res.empty())
return "";
1323 std::vector<std::string> type_list;
1324 for(
auto&
i : type_res){
1325 if(
i.second == max_res){
1326 type_list.push_back(
i.first);
1329 if(type_list.empty())
return "";
1331 return type_list.front();
1336 bool is_alternative = (key_name ==
"alternative_type");
1337 if(is_alternative &&
other_){
1339 }
else if(!is_alternative){
1354 if(damage_type_list.
empty()){
1355 return {
type(),
""};
1360 resistance_list = (*other_).get_abilities_weapons(
"resistance",
other_loc_,
other_attack_, shared_from_this());
1362 std::string replacement_type =
select_damage_type(damage_type_list,
"replacement_type", resistance_list);
1363 std::string alternative_type =
select_damage_type(damage_type_list,
"alternative_type", resistance_list);
1364 std::string type_damage = replacement_type.empty() ?
type() : replacement_type;
1365 if(!alternative_type.empty() && type_damage != alternative_type){
1366 return {type_damage, alternative_type};
1368 return {type_damage,
""};
1374 if(damage_type_list.
empty()){
1377 std::set<std::string> damage_types;
1378 for(
auto&
i : damage_type_list) {
1380 if(
c.has_attribute(
"alternative_type")){
1381 damage_types.insert(
c[
"alternative_type"].str());
1385 return damage_types;
1395 return damage_value;
1407 bool special_affects_opponent(
const config& special,
bool is_attacker)
1410 const std::string& apply_to = special[
"apply_to"];
1411 if ( apply_to.empty() )
1413 if ( apply_to ==
"both" )
1415 if ( apply_to ==
"opponent" )
1417 if ( is_attacker && apply_to ==
"defender" )
1419 if ( !is_attacker && apply_to ==
"attacker" )
1429 bool special_affects_self(
const config& special,
bool is_attacker)
1432 const std::string& apply_to = special[
"apply_to"];
1433 if ( apply_to.empty() )
1435 if ( apply_to ==
"both" )
1437 if ( apply_to ==
"self" )
1439 if ( is_attacker && apply_to ==
"attacker" )
1441 if ( !is_attacker && apply_to ==
"defender")
1463 const bool for_listing,
1464 const std::string & child_tag,
const std::string& check_if_recursion)
1466 if (for_listing && !loc.
valid())
1475 if ( !filter_child )
1490 if (
auto filter_weapon = filter_child->optional_child(
"filter_weapon") ) {
1491 if ( !weapon || !weapon->matches_filter(*filter_weapon, check_if_recursion) )
1498 return ufilt.matches(*u, loc);
1500 return ufilt.matches(*u, loc, *u2);
1518 return special_active(*i.ability_cfg, AFFECT_SELF, ability,
"filter_student");
1524 return special_active_impl(other_attack_, shared_from_this(), *i.ability_cfg, AFFECT_OTHER, ability,
"filter_student");
1539 if(!abil_list.
empty() && !overwriters.
empty()){
1555 const std::string& apply_to = special[
"overwrite_specials"];
1556 return (apply_to ==
"one_side" || apply_to ==
"both_sides");
1567 if(overwriters.
empty()){
1572 if(overwriters.
size() >= 2){
1575 auto oi = (*
i.ability_cfg).optional_child(
"overwrite");
1577 if(oi && !oi[
"priority"].empty()){
1578 l = oi[
"priority"].to_double(0);
1580 auto oj = (*j.
ability_cfg).optional_child(
"overwrite");
1582 if(oj && !oj[
"priority"].empty()){
1583 r = oj[
"priority"].to_double(0);
1597 if(overwriters.
empty()){
1601 for(
const auto& j : overwriters) {
1603 bool affect_side = ((*j.ability_cfg)[
"overwrite_specials"] ==
"one_side");
1605 auto overwrite_specials = (*j.ability_cfg).optional_child(
"overwrite");
1606 double priority = overwrite_specials ? overwrite_specials[
"priority"].to_double(0) : 0.00;
1611 bool prior = (priority > 0) ? (has_overwrite_specials && has_overwrite_specials[
"priority"].to_double(0) >= priority) :
true;
1615 bool one_side_overwritable =
true;
1619 if(affect_side && is_overwritable){
1620 if(special_affects_self(*j.ability_cfg,
is_attacker_)){
1621 one_side_overwritable = special_affects_self(cfg,
is_attacker_);
1623 else if(special_affects_opponent(*j.ability_cfg, !
is_attacker_)){
1624 one_side_overwritable = special_affects_opponent(cfg, !
is_attacker_);
1629 bool special_matches =
true;
1630 if(overwrite_specials){
1631 auto overwrite_filter = (*overwrite_specials).optional_child(
"experimental_filter_specials");
1632 if(overwrite_filter && is_overwritable && one_side_overwritable){
1640 if(is_overwritable && one_side_overwritable && special_matches){
1659 std::vector<special_match>& id_result,
1660 const config& parent,
const std::string&
id,
1661 bool special_id=
true,
bool special_tags=
true) {
1662 if(special_id && special_tags){
1663 get_special_children(tag_result, id_result, parent,
id);
1664 }
else if(special_id && !special_tags){
1665 get_special_children_id(id_result, parent,
id);
1666 }
else if(!special_id && special_tags){
1667 get_special_children_tags(tag_result, parent,
id);
1673 return (ability_active(tag_name, special, loc) && ability_affects_self(tag_name, special, loc));
1679 return (affects_side(special, side(), from.
side()) && from.
ability_active(tag_name, special, adjacent[dir]) && ability_affects_adjacent(tag_name, special, dir, loc, from));
1684 return (get_self_ability_bool(special, tag_name, loc) && ability_affects_weapon(special, weapon,
false) && ability_affects_weapon(special, opp_weapon,
true));
1689 return (get_adj_ability_bool(special, tag_name, dir, loc, from) && ability_affects_weapon(special, weapon,
false) && ability_affects_weapon(special, opp_weapon,
true));
1699 if(tag_name ==
"leadership" && leader_bool){
1700 if((*u).get_self_ability_bool_weapon(special, tag_name, loc, self_attack, other_attack)) {
1704 if((*u).checking_tags().count(tag_name) != 0){
1705 if((*u).get_self_ability_bool(special, tag_name, loc) &&
special_active_impl(self_attack, other_attack, special, whom, tag_name,
"filter_student")) {
1719 if(tag_name ==
"leadership" && leader_bool){
1720 if((*u).get_adj_ability_bool_weapon(special, tag_name, dir, loc, from, self_attack, other_attack)) {
1724 if((*u).checking_tags().count(tag_name) != 0){
1725 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")) {
1739 const unit_map& units = get_unit_map();
1741 std::vector<special_match> special_tag_matches_self;
1742 std::vector<special_match> special_id_matches_self;
1743 get_ability_children(special_tag_matches_self, special_id_matches_self, (*self_).abilities(), special, special_id , special_tags);
1745 for(
const special_match& entry : special_tag_matches_self) {
1752 for(
const special_match& entry : special_id_matches_self) {
1760 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
1762 if (it == units.
end() || it->incapacitated())
1764 if ( &*it ==
self_.get() )
1767 std::vector<special_match> special_tag_matches_adj;
1768 std::vector<special_match> special_id_matches_adj;
1769 get_ability_children(special_tag_matches_adj, special_id_matches_adj, it->abilities(), special, special_id , special_tags);
1771 for(
const special_match& entry : special_tag_matches_adj) {
1778 for(
const special_match& entry : special_id_matches_adj) {
1788 std::vector<special_match> special_tag_matches_other;
1789 std::vector<special_match> special_id_matches_other;
1790 get_ability_children(special_tag_matches_other, special_id_matches_other, (*other_).abilities(), special, special_id , special_tags);
1792 for(
const special_match& entry : special_tag_matches_other) {
1800 for(
const special_match& entry : special_id_matches_other) {
1808 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
1810 if (it == units.
end() || it->incapacitated())
1812 if ( &*it ==
other_.get() )
1815 std::vector<special_match> special_tag_matches_oadj;
1816 std::vector<special_match> special_id_matches_oadj;
1817 get_ability_children(special_tag_matches_oadj, special_id_matches_oadj, it->abilities(), special, special_id , special_tags);
1819 for(
const special_match& entry : special_tag_matches_oadj) {
1827 for(
const special_match& entry : special_id_matches_oadj) {
1843 if(
range().empty()){
1852 bool matches_ability_filter(
const config & cfg,
const std::string& tag_name,
const config & filter)
1856 if(!filter[
"affect_adjacent"].empty()){
1857 bool adjacent = cfg.
has_child(
"affect_adjacent");
1858 if(filter[
"affect_adjacent"].to_bool() != adjacent){
1875 const std::vector<std::string> filter_type =
utils::split(filter[
"tag_name"]);
1876 if ( !filter_type.empty() && std::find(filter_type.begin(), filter_type.end(), tag_name) == filter_type.end() )
1882 if(tag_name ==
"resistance"){
1909 if(!filter[
"value"].empty()){
1910 if(tag_name ==
"drains"){
1914 }
else if(tag_name ==
"berserk"){
1918 }
else if(tag_name ==
"heal_on_hit" || tag_name ==
"heals" || tag_name ==
"regenerate" || tag_name ==
"leadership"){
1955 static bool common_matches_filter(
const config & cfg,
const std::string& tag_name,
const config & filter)
1958 bool matches = matches_ability_filter(cfg, tag_name, filter);
1964 if ( condition.key ==
"and" )
1965 matches = matches && common_matches_filter(cfg, tag_name, condition.cfg);
1968 else if ( condition.key ==
"or" )
1969 matches = matches || common_matches_filter(cfg, tag_name, condition.cfg);
1972 else if ( condition.key ==
"not" )
1973 matches = matches && !common_matches_filter(cfg, tag_name, condition.cfg);
1982 return common_matches_filter(cfg, tag_name, filter);
1987 return common_matches_filter(cfg, tag_name, filter);
1991 const std::string& filter_self)
const
2011 const std::string& tag_name,
2012 const std::string& filter_self)
2014 assert(self_attack || other_attack);
2015 bool is_attacker = self_attack ? self_attack->is_attacker_ : !other_attack->is_attacker_;
2016 bool is_for_listing = self_attack ? self_attack->is_for_listing_ : other_attack->is_for_listing_;
2022 if ( !special_affects_self(special, is_attacker) )
2026 if ( !special_affects_opponent(special, is_attacker) )
2031 const std::string & active_on = special[
"active_on"];
2032 if ( !active_on.empty() ) {
2033 if ( is_attacker && active_on !=
"offense" )
2035 if ( !is_attacker && active_on !=
"defense" )
2040 const unit_map& units = get_unit_map();
2042 unit_const_ptr self = self_attack ? self_attack->self_ : other_attack->other_;
2043 unit_const_ptr other = self_attack ? self_attack->other_ : other_attack->self_;
2044 map_location self_loc = self_attack ? self_attack->self_loc_ : other_attack->other_loc_;
2045 map_location other_loc = self_attack ? self_attack->other_loc_ : other_attack->self_loc_;
2047 if(
self ==
nullptr) {
2053 if(other ==
nullptr) {
2062 temporary_facing other_facing(other, other_loc.
get_relative_dir(self_loc));
2066 bool whom_is_self = ((whom ==
AFFECT_SELF) || ((whom ==
AFFECT_EITHER) && special_affects_self(special, is_attacker)));
2068 map_location their_loc = whom_is_self ? other_loc : self_loc;
2070 if (tag_name ==
"drains" && them && them->get_state(
"undrainable")) {
2073 if (tag_name ==
"plague" && them &&
2074 (them->get_state(
"unplagueable") ||
2078 if (tag_name ==
"poison" && them &&
2082 if (tag_name ==
"slow" && them &&
2086 if (tag_name ==
"petrifies" && them &&
2087 them->get_state(
"unpetrifiable")) {
2095 const map_location & att_loc = is_attacker ? self_loc : other_loc;
2096 const map_location & def_loc = is_attacker ? other_loc : self_loc;
2102 if (tag_name ==
"firststrike") {
2103 bool whom_is_defender = whom_is_self ? !is_attacker : is_attacker;
2104 if (whom_is_defender && att_weapon && att_weapon->has_special_or_ability(
"firststrike"))
2109 if (!special[
"backstab"].
blank()) {
2110 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.");
2113 if(special[
"backstab"].to_bool()){
2114 const std::string& backstab_formula =
"enemy_of(self, flanker) and not flanker.petrified where flanker = unit_at(direction_from(loc, other.facing))";
2116 if(!special.
has_child(
"filter_opponent")){
2117 filter_child[
"formula"] = backstab_formula;
2120 filter[
"formula"] = backstab_formula;
2124 const config& special_backstab = special[
"backstab"].to_bool() ? cfg : special;
2131 bool applied_both = special[
"apply_to"] ==
"both";
2132 std::string self_check_if_recursion = (applied_both || whom_is_self) ? tag_name :
"";
2133 if (!special_unit_matches(
self, other, self_loc, self_attack, special, is_for_listing, filter_self, self_check_if_recursion))
2135 std::string opp_check_if_recursion = (applied_both || !whom_is_self) ? tag_name :
"";
2136 if (!special_unit_matches(other,
self, other_loc, other_attack, special_backstab, is_for_listing,
"filter_opponent", opp_check_if_recursion))
2140 bool applied_to_attacker = applied_both || (whom_is_self && is_attacker) || (!whom_is_self && !is_attacker);
2141 std::string att_check_if_recursion = applied_to_attacker ? tag_name :
"";
2142 if (!special_unit_matches(att, def, att_loc, att_weapon, special, is_for_listing,
"filter_attacker", att_check_if_recursion))
2144 bool applied_to_defender = applied_both || (whom_is_self && !is_attacker) || (!whom_is_self && is_attacker);
2145 std::string def_check_if_recursion= applied_to_defender ? tag_name :
"";
2146 if (!special_unit_matches(def, att, def_loc, def_weapon, special, is_for_listing,
"filter_defender", def_check_if_recursion))
2154 std::size_t count = 0;
2164 if (
i.has_attribute(
"is_enemy")) {
2172 if (
i[
"count"].empty() && count != dirs.size()) {
2183 std::size_t count = 0;
2195 if (
i[
"count"].empty() && count != dirs.size()) {
2221 if (
auto apply_filter = cfg.
optional_child(
"filter_base_value")) {
2228 return (cond_eq.
empty() || def == cond_eq.
to_int()) &&
2244 std::map<std::string,individual_effect> values_add;
2245 std::map<std::string,individual_effect> values_sub;
2246 std::map<std::string,individual_effect> values_mul;
2247 std::map<std::string,individual_effect> values_div;
2251 utils::optional<int> max_value = utils::nullopt;
2252 utils::optional<int> min_value = utils::nullopt;
2255 const config& cfg = *ability.ability_cfg;
2256 const std::string& effect_id = cfg[cfg[
"id"].
empty() ?
"name" :
"id"];
2264 callable.add(
"base_value", wfl::variant(def));
2265 return formula.evaluate(callable).as_int();
2268 int value_cum = cfg[
"cumulative"].to_bool() ? std::max(def, value) : value;
2271 set_effect_min.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2272 set_effect_max.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2275 if(value_cum > set_effect_max.
value) {
2276 set_effect_max.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2278 if(value_cum < set_effect_min.
value) {
2279 set_effect_min.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2287 max_value = max_value ? std::min(*max_value, cfg[
"max_value"].to_int()) : cfg[
"max_value"].to_int();
2290 min_value = min_value ? std::max(*min_value, cfg[
"min_value"].to_int()) : cfg[
"min_value"].to_int();
2296 callable.add(
"base_value", wfl::variant(def));
2297 return formula.evaluate(callable).as_int();
2300 if(add_effect == values_add.end() || add > add_effect->second.value) {
2301 values_add[effect_id].set(
ADD, add, ability.ability_cfg, ability.teacher_loc);
2306 callable.add(
"base_value", wfl::variant(def));
2307 return formula.evaluate(callable).as_int();
2310 if(sub_effect == values_sub.end() || sub < sub_effect->second.value) {
2311 values_sub[effect_id].set(
ADD, sub, ability.ability_cfg, ability.teacher_loc);
2316 callable.add(
"base_value", wfl::variant(def));
2317 return formula.evaluate(callable).as_decimal() / 1000.0 ;
2320 if(mul_effect == values_mul.end() || multiply > mul_effect->second.value) {
2321 values_mul[effect_id].set(
MUL, multiply, ability.ability_cfg, ability.teacher_loc);
2326 callable.add(
"base_value", wfl::variant(def));
2327 return formula.evaluate(callable).as_decimal() / 1000.0 ;
2331 ERR_NG <<
"division by zero with divide= in ability/weapon special " << effect_id;
2335 if(div_effect == values_div.end() || divide > div_effect->second.value) {
2336 values_div[effect_id].set(
DIV, divide, ability.ability_cfg, ability.teacher_loc);
2343 value_set = std::max(set_effect_max.
value, 0) + std::min(set_effect_min.
value, 0);
2344 if(set_effect_max.
value > def) {
2347 if(set_effect_min.
value < def) {
2360 double multiplier = 1.0;
2361 double divisor = 1.0;
2363 for(
const auto& val : values_mul) {
2364 multiplier *= val.second.value/100.0;
2368 for(
const auto& val : values_div) {
2369 divisor *= val.second.value/100.0;
2374 for(
const auto& val : values_add) {
2375 addition += val.second.value;
2381 int substraction = 0;
2382 for(
const auto& val : values_sub) {
2383 substraction += val.second.value;
2387 composite_value_ =
static_cast<int>((value_set + addition + substraction) * multiplier / divisor);
2389 if(max_value && min_value && *min_value < *max_value) {
2391 }
else if(max_value && !min_value) {
2393 }
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 std::string select_alternative_type(const unit_ability_list &damage_type_list, unit_ability_list resistance_list, const unit &u)
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 bool overwrite_special_affects(const config &special)
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 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...
specials_context_t(const attack_type &weapon, bool attacking)
Initialize weapon specials context for listing.
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.
static bool special_active_impl(const_attack_ptr self_attack, 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...
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
const std::string & type() const
unit_ability_list get_weapon_ability(const std::string &ability) const
Returns list for weapon like abilities for each ability type.
std::string weapon_specials_value(const std::set< std::string > checking_tags) 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...
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...
static bool check_adj_abilities_impl(const_attack_ptr self_attack, const_attack_ptr other_attack, const config &special, 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 ...
static void weapon_specials_impl_adj(std::string &temp_string, unit_const_ptr self, const_attack_ptr self_attack, 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)
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, unit_const_ptr self, const_attack_ptr self_attack, 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
bool attack_empty() const
Returns true if this is a dummy attack_type, for example the placeholder that the unit_attack dialog ...
static bool check_self_abilities_impl(const_attack_ptr self_attack, const_attack_ptr other_attack, const config &special, 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 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.
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...
std::string select_damage_type(const unit_ability_list &damage_type_list, const std::string &key_name, 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_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.
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
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
const_all_children_itors all_children_range() const
In-order iteration over all children.
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.
const unit_map & get_units() const
const display_context & get_disp_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 get_self_ability_bool_weapon(const config &special, const std::string &tag_name, const map_location &loc, const_attack_ptr weapon=nullptr, const_attack_ptr opp_weapon=nullptr) const
Checks whether this unit currently possesses a given ability of leadership type.
bool get_adj_ability_bool_weapon(const config &special, const std::string &tag_name, int dir, const map_location &loc, const unit &from, const_attack_ptr weapon=nullptr, const_attack_ptr opp_weapon=nullptr) const
Checks whether this unit is affected by a given ability of leadership type.
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.
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.
unsigned int num_recursion_
Number of instances of recursion_guard that are currently allocated permission to recurse.
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 ability_affects_weapon(const config &cfg, const_attack_ptr weapon, bool is_opp) const
filters the weapons that condition the use of abilities for combat ([resistance],[leadership] or abil...
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.
bool get_adj_ability_bool(const config &special, const std::string &tag_name, int dir, const map_location &loc, const unit &from) const
Checks whether this unit is affected by a given ability used like weapon.
unit_race::GENDER gender_
recursion_guard()
Construct an empty instance, only useful for extending the lifetime of a recursion_guard returned fro...
std::vector< std::string > get_ability_list() const
Get a list of all abilities by ID.
recursion_guard update_variables_recursion() const
recursion_guard & operator=(recursion_guard &&)
const std::set< std::string > & checking_tags() const
bool get_self_ability_bool(const config &special, const std::string &tag_name, const map_location &loc) const
Checks whether this unit currently possesses a given ability used like weapon.
bool ability_matches_filter(const config &cfg, const std::string &tag_name, const config &filter) const
Verify what abilities attributes match with filter.
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...
const 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
std::string span_color(const color_t &color)
Returns a Pango formatting string using the provided color_t object.
static bool is_active(const widget *wgt)
std::stringstream & log_to_chat()
Use this to show WML errors in the ingame chat.
filter_context * filter_con
bool filter_base_matches(const config &cfg, int def)
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.
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)
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.
const child_map::key_type & key
Encapsulates the map of the game.
DIRECTION
Valid directions which can be moved in our hexagonal world.
static std::vector< DIRECTION > parse_directions(const std::string &str)
Parse_directions takes a comma-separated list, and filters out any invalid directions.
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