26 #include "formula/callable_objects.hpp"
54 #define ERR_NG LOG_STREAM(err, log_engine)
57 #define ERR_WML LOG_STREAM(err, log_wml)
60 class temporary_facing
70 u_->set_facing(new_dir);
76 u_->set_facing(save_dir_);
153 const team& get_team(std::size_t side)
173 bool affects_side(
const config& cfg, std::size_t side, std::size_t other_side)
175 const team& side_team = get_team(side);
177 if(side == other_side)
178 return cfg[
"affect_allies"].to_bool(
true);
180 return cfg[
"affect_enemies"].to_bool();
182 return cfg[
"affect_allies"].to_bool();
196 const unit_map& units = get_unit_map();
199 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
201 if (it == units.
end() || it->incapacitated())
210 for (
const config &j : it->abilities_.child_range(tag_name)) {
233 const unit_map& units = get_unit_map();
236 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
238 if (it == units.
end() || it->incapacitated())
247 for(
const config& j : it->abilities_.child_range(tag_name)) {
269 std::vector<std::string> res;
272 std::string
id = cfg[
"id"];
274 res.push_back(std::move(
id));
287 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)
297 ab[
"description"].t_str() );
304 "female_name_inactive",
"name_inactive");
313 ab.
get_or(
"description_inactive",
"description").
t_str() );
324 std::vector<std::tuple<std::string, t_string,t_string,t_string>> res;
328 add_ability_tooltip(cfg,
gender_, res,
true);
336 std::vector<std::tuple<std::string, t_string,t_string,t_string>> res;
342 if (add_ability_tooltip(cfg,
gender_, res, active))
344 active_list.push_back(active);
361 static std::vector<std::tuple<std::string, std::string>> already_shown;
363 auto identifier = std::tuple<std::string, std::string>{
unit.
id(),
filter.debug()};
368 std::string_view filter_text_view = std::get<1>(identifier);
370 ERR_NG <<
"Looped recursion error for unit '" <<
unit.
id()
371 <<
"' while checking ability '" << filter_text_view <<
"'";
375 if(already_shown.size() > 100) {
376 already_shown.clear();
378 already_shown.push_back(std::move(identifier));
393 : parent(u.shared_from_this())
403 unit::recursion_guard::operator bool()
const {
409 assert(
this != &other);
418 assert(!parent->open_queries_.empty());
419 parent->open_queries_.pop_back();
427 show_recursion_warning(*
this, cfg);
435 bool illuminates = ability ==
"illuminates";
443 const unit_map& units = get_unit_map();
447 std::size_t count = 0;
449 ufilt.set_use_flat_tod(illuminates);
456 if (!ufilt(*
unit, *
this))
458 if((*this).id() == (*unit).id())
460 if (
i.has_attribute(
"is_enemy")) {
477 std::size_t count = 0;
479 adj_filter.flatten(illuminates);
484 if(!adj_filter.match(adjacent[
static_cast<int>(
index)])) {
500 bool illuminates = ability ==
"illuminates";
502 assert(dir >=0 && dir <= 5);
508 if (
i.has_attribute(
"adjacent")) {
510 if (
std::find(dirs.begin(), dirs.end(), direction) == dirs.end()) {
514 if((*this).id() == from.
id()){
517 auto filter =
i.optional_child(
"filter");
529 bool affect_self = cfg[
"affect_self"].to_bool(
true);
530 if (!
filter || !affect_self)
return affect_self;
536 const std::string filter_tag_name = is_opp ?
"filter_second_weapon" :
"filter_weapon";
545 filter_lock = weapon->update_variables_recursion(cfg);
547 show_recursion_warning(*
this, cfg);
550 return weapon->matches_filter(
filter);
562 auto ret =
std::find(image_list.begin(), image_list.end(), cfg[attribute_name].str());
563 if(ret == image_list.end()){
564 image_list.push_back(cfg[attribute_name].str());
570 std::vector<std::string> image_list;
578 if(!cfg[image_type +
"_image_self"].str().empty() &&
is_active){
583 const unit_map& units = get_unit_map();
588 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
590 if (it == units.
end() || it->incapacitated())
594 for(
const auto [key, cfg] : it->abilities_.all_children_view()) {
595 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))
602 if(image_list.size() >= 2){
603 std::sort(image_list.begin(), image_list.end());
611 callable.
add(
"attacker",
wfl::variant(std::make_shared<wfl::unit_callable>(*att)));
614 callable.
add(
"defender",
wfl::variant(std::make_shared<wfl::unit_callable>(*def)));
621 template<
typename T,
typename TFuncFormula>
622 class get_ability_value_visitor
623 #ifdef USING_BOOST_VARIANT
624 :
public boost::static_visitor<T>
629 get_ability_value_visitor(T def,
const TFuncFormula& formula_handler) : def_(def), formula_handler_(formula_handler) {}
631 T operator()(
const utils::monostate&)
const {
return def_; }
632 T operator()(
bool)
const {
return def_; }
633 T operator()(
int i)
const {
return static_cast<T
>(
i); }
634 T operator()(
unsigned long long u)
const {
return static_cast<T
>(u); }
635 T operator()(
double d)
const {
return static_cast<T
>(
d); }
636 T operator()(
const t_string&)
const {
return def_; }
637 T operator()(
const std::string&
s)
const
639 if(
s.size() >= 2 &&
s[0] ==
'(') {
640 return formula_handler_(
s);
642 return lexical_cast_default<T>(
s, def_);
647 const TFuncFormula& formula_handler_;
650 template<
typename T,
typename TFuncFormula>
653 return v.
apply_visitor(get_ability_value_visitor(def, [&](
const std::string&
s) {
656 const unit_map& units = get_unit_map();
660 if(u_itor == units.
end()) {
665 att->add_formula_context(callable);
668 callable.add(
"student",
wfl::variant(std::make_shared<wfl::unit_callable>(*uptr)));
671 callable.add(
"other",
wfl::variant(std::make_shared<wfl::unit_callable>(*uptr)));
675 lg::log_to_chat() <<
"Formula error in ability or weapon special: " <<
e.type <<
" at " <<
e.filename <<
':' <<
e.line <<
")\n";
676 ERR_WML <<
"Formula error in ability or weapon special: " <<
e.type <<
" at " <<
e.filename <<
':' <<
e.line <<
")";
683 template<
typename TComp>
686 if ( cfgs_.empty() ) {
692 bool only_cumulative =
true;
702 if ((*
p.ability_cfg)[
"cumulative"].to_bool()) {
704 if (value < 0) value = -value;
705 if (only_cumulative && !comp(value, abs_max)) {
707 best_loc =
p.teacher_loc;
709 }
else if (only_cumulative || comp(flat, value)) {
710 only_cumulative =
false;
712 best_loc =
p.teacher_loc;
715 return std::pair(flat + stack, best_loc);
718 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;
719 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;
753 std::string tag_name;
769 bool get_special_children(std::vector<special_match>& tag_result,
770 std::vector<special_match>& id_result,
771 const config& parent,
const std::string&
id,
772 bool just_peeking=
false) {
775 if (just_peeking && (key ==
id || cfg[
"id"] ==
id)) {
780 special_match special = { key, &cfg };
781 tag_result.push_back(special);
783 if(cfg[
"id"] ==
id) {
784 special_match special = { key, &cfg };
785 id_result.push_back(special);
791 bool get_special_children_id(std::vector<special_match>& id_result,
792 const config& parent,
const std::string&
id,
793 bool just_peeking=
false) {
796 if (just_peeking && (cfg[
"id"] ==
id)) {
800 if(cfg[
"id"] ==
id) {
801 special_match special = { key, &cfg };
802 id_result.push_back(special);
808 bool get_special_children_tags(std::vector<special_match>& tag_result,
809 const config& parent,
const std::string&
id,
810 bool just_peeking=
false) {
813 if (just_peeking && (key ==
id)) {
818 special_match special = { key, &cfg };
819 tag_result.push_back(special);
836 std::vector<special_match> special_tag_matches;
837 std::vector<special_match> special_id_matches;
838 if(special_id && special_tags){
839 if ( get_special_children(special_tag_matches, special_id_matches, specials_, special, simple_check) ) {
842 }
else if(special_id && !special_tags){
843 if ( get_special_children_id(special_id_matches, specials_, special, simple_check) ) {
846 }
else if(!special_id && special_tags){
847 if ( get_special_children_tags(special_tag_matches, specials_, special, simple_check) ) {
855 for(
const special_match& entry : special_tag_matches) {
856 if ( special_active(*entry.cfg, AFFECT_SELF, entry.tag_name) ) {
862 for(
const special_match& entry : special_id_matches) {
863 if ( special_active(*entry.cfg, AFFECT_SELF, entry.tag_name) ) {
871 if ( simple_check || !other_attack_ ) {
875 std::vector<special_match> special_tag_matches;
876 std::vector<special_match> special_id_matches;
877 if(special_id && special_tags){
878 get_special_children(special_tag_matches, special_id_matches, other_attack_->specials_, special);
879 }
else if(special_id && !special_tags){
880 get_special_children_id(special_id_matches, other_attack_->specials_, special);
881 }
else if(!special_id && special_tags){
882 get_special_children_tags(special_tag_matches, other_attack_->specials_, special);
885 for(
const special_match& entry : special_tag_matches) {
886 if ( other_attack_->special_active(*entry.cfg, AFFECT_OTHER, entry.tag_name) ) {
892 for(
const special_match& entry : special_id_matches) {
893 if ( other_attack_->special_active(*entry.cfg, AFFECT_OTHER, entry.tag_name) ) {
911 for(
const config&
i : specials_.child_range(special)) {
912 if(special_active(
i, AFFECT_SELF, special)) {
921 for(
const config&
i : other_attack_->specials_.child_range(special)) {
922 if(other_attack_->special_active(
i, AFFECT_OTHER, special)) {
939 boost::dynamic_bitset<>* active_list)
const
942 std::vector<std::pair<t_string, t_string>> res;
944 active_list->clear();
947 for(
const auto [key, cfg] : specials_.all_children_view()) {
948 if(!active_list || special_active(cfg, AFFECT_EITHER, key)) {
951 if(key ==
"plague") {
957 res.emplace_back(
name, cfg[
"description"].t_str());
961 active_list->push_back(
true);
967 res.emplace_back(
name, cfg.
get_or(
"description_inactive",
"description").
t_str() );
968 active_list->push_back(
false);
983 static void add_name(std::string& temp_string,
bool active,
const std::string&
name, std::set<std::string>& checking_name)
987 checking_name.insert(
name);
988 if (!temp_string.empty()) temp_string +=
", ";
1005 for(
const auto [key, cfg] : specials_.all_children_view())
1007 const bool active = special_active(cfg, AFFECT_EITHER, key);
1009 const std::string&
name =
1012 : cfg.
get_or(
"name_inactive",
"name").
str();
1025 std::string temp_string;
1026 std::set<std::string> checking_name;
1027 weapon_specials_impl_self(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name);
1028 weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name, {},
"affect_allies");
1029 if(!temp_string.empty() && !res.empty()) {
1030 temp_string =
", \n" + temp_string;
1032 }
else if (!temp_string.empty()){
1038 static void add_name_list(std::string& temp_string, std::string& weapon_abilities, std::set<std::string>& checking_name,
const std::string& from_str)
1040 if(!temp_string.empty()){
1041 temp_string = from_str.c_str() + temp_string;
1042 weapon_abilities += (!weapon_abilities.empty() && !temp_string.empty()) ?
"\n" :
"";
1043 weapon_abilities += temp_string;
1044 temp_string.clear();
1045 checking_name.clear();
1052 std::string temp_string, weapon_abilities;
1053 std::set<std::string> checking_name;
1054 for(
const auto [key, cfg] : specials_.all_children_view()) {
1056 const bool active = special_active(cfg, AFFECT_SELF, key);
1057 add_name(temp_string, active, cfg[
"name"].str(), checking_name);
1060 add_name_list(temp_string, weapon_abilities, checking_name,
"");
1062 weapon_specials_impl_self(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name,
checking_tags,
true);
1063 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Owned: "));
1065 weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name,
checking_tags,
"affect_allies",
true);
1067 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Taught: "));
1069 weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name,
checking_tags,
"affect_enemies",
true);
1071 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Taught: (by an enemy): "));
1075 for(
const auto [key, cfg] : other_attack_->specials_.all_children_view()) {
1077 const bool active = other_attack_->special_active(cfg, AFFECT_OTHER, key);
1078 add_name(temp_string, active, cfg[
"name"].str(), checking_name);
1082 weapon_specials_impl_self(temp_string, other_, other_attack_, shared_from_this(), other_loc_, AFFECT_OTHER, checking_name,
checking_tags);
1083 weapon_specials_impl_adj(temp_string, other_, other_attack_, shared_from_this(), other_loc_, AFFECT_OTHER, checking_name,
checking_tags);
1084 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Used by opponent: "));
1086 return weapon_abilities;
1090 std::string& temp_string,
1096 std::set<std::string>& checking_name,
1101 for(
const auto [key, cfg] : self->abilities().all_children_view()){
1103 const bool active = tag_checked && check_self_abilities_impl(self_attack, other_attack, cfg,
self, self_loc, whom, key, leader_bool);
1104 add_name(temp_string, active, cfg[
"name"].str(), checking_name);
1110 std::string& temp_string,
1116 std::set<std::string>& checking_name,
1118 const std::string& affect_adjacents,
1121 const unit_map& units = get_unit_map();
1124 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
1126 if (it == units.
end() || it->incapacitated())
1128 if(&*it ==
self.
get())
1130 for(
const auto [key, cfg] : it->abilities().all_children_view()) {
1132 bool default_bool = (affect_adjacents ==
"affect_allies") ?
true :
false;
1133 bool affect_allies = (!affect_adjacents.empty()) ? cfg[affect_adjacents].to_bool(default_bool) :
true;
1134 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;
1135 add_name(temp_string, active, cfg[
"name"].str(), checking_name);
1161 : parent(weapon.shared_from_this())
1163 weapon.
self_ = std::move(
self);
1164 weapon.
other_ = std::move(other);
1181 : parent(weapon.shared_from_this())
1183 weapon.
self_ = std::move(
self);
1201 : parent(weapon.shared_from_this())
1213 : parent(weapon.shared_from_this())
1221 if(was_moved)
return;
1226 parent->is_attacker_ =
false;
1227 parent->other_attack_ =
nullptr;
1228 parent->is_for_listing_ =
false;
1232 : parent(std::move(other.parent))
1234 other.was_moved =
true;
1245 unsigned & max_attacks)
const
1250 if ( attacks_value < 0 ) {
1252 ERR_NG <<
"negative number of strikes after applying weapon specials";
1257 if ( !swarm_specials.
empty() ) {
1258 min_attacks = std::max<int>(0, swarm_specials.
highest(
"swarm_attacks_min").first);
1259 max_attacks = std::max<int>(0, swarm_specials.
highest(
"swarm_attacks_max", attacks_value).first);
1261 min_attacks = max_attacks = attacks_value;
1267 std::map<std::string, unsigned int> type_count;
1268 unsigned int max = 0;
1269 for(
auto&
i : damage_type_list) {
1271 if(
c.has_attribute(
"replacement_type")) {
1272 std::string
type =
c[
"replacement_type"].str();
1273 unsigned int count = ++type_count[
type];
1280 if (type_count.empty())
return type();
1282 std::vector<std::string> type_list;
1283 for(
auto&
i : type_count){
1284 if(
i.second == max){
1285 type_list.push_back(
i.first);
1289 if(type_list.empty())
return type();
1291 return type_list.front();
1296 std::map<std::string, int> type_res;
1297 int max_res = INT_MIN;
1299 for(
auto&
i : damage_type_list) {
1301 if(
c.has_attribute(
"alternative_type")) {
1302 std::string
type =
c[
"alternative_type"].str();
1303 if(type_res.count(
type) == 0){
1304 type_res[
type] = (*other_).resistance_value(resistance_list,
type);
1305 max_res = std::max(max_res, type_res[
type]);
1311 if (type_res.empty())
return {
"", INT_MIN};
1313 std::vector<std::string> type_list;
1314 for(
auto&
i : type_res){
1315 if(
i.second == max_res){
1316 type_list.push_back(
i.first);
1319 if(type_list.empty())
return {
"", INT_MIN};
1321 return {type_list.front(), max_res};
1334 resistance_list = (*other_).get_abilities_weapons(
"resistance",
other_loc_,
other_attack_, shared_from_this());
1336 return (!((*
i.ability_cfg)[
"active_on"].empty() || (!
is_attacker_ && (*
i.ability_cfg)[
"active_on"] ==
"offense") || (
is_attacker_ && (*
i.ability_cfg)[
"active_on"] ==
"defense")));
1340 int res =
other_ ? (*other_).resistance_value(resistance_list,
type()) : 100;
1341 if(damage_type_list.
empty()){
1342 return {
type(), res};
1348 res = replacement_type !=
type() ? (*other_).resistance_value(resistance_list, replacement_type) : res;
1349 replacement_type = alternative_type.second > res ? alternative_type.first : replacement_type;
1350 res = std::max(res, alternative_type.second);
1352 return {replacement_type, res};
1361 std::set<std::string> alternative_damage_types;
1362 if(damage_type_list.
empty()){
1363 return {
type(), alternative_damage_types};
1366 for(
auto&
i : damage_type_list) {
1368 if(
c.has_attribute(
"alternative_type")){
1369 alternative_damage_types.insert(
c[
"alternative_type"].str());
1373 return {replacement_type, alternative_damage_types};
1382 return damage_value;
1394 bool special_affects_opponent(
const config& special,
bool is_attacker)
1397 const std::string& apply_to = special[
"apply_to"];
1398 if ( apply_to.empty() )
1400 if ( apply_to ==
"both" )
1402 if ( apply_to ==
"opponent" )
1404 if ( is_attacker && apply_to ==
"defender" )
1406 if ( !is_attacker && apply_to ==
"attacker" )
1416 bool special_affects_self(
const config& special,
bool is_attacker)
1419 const std::string& apply_to = special[
"apply_to"];
1420 if ( apply_to.empty() )
1422 if ( apply_to ==
"both" )
1424 if ( apply_to ==
"self" )
1426 if ( is_attacker && apply_to ==
"attacker" )
1428 if ( !is_attacker && apply_to ==
"defender")
1443 static std::vector<std::tuple<std::string, std::string>> already_shown;
1445 auto identifier = std::tuple<std::string, std::string>{attack->id(),
filter.debug()};
1450 std::string_view filter_text_view = std::get<1>(identifier);
1452 ERR_NG <<
"Looped recursion error for weapon '" << attack->id()
1453 <<
"' while checking weapon special '" << filter_text_view <<
"'";
1457 if(already_shown.size() > 100) {
1458 already_shown.clear();
1460 already_shown.push_back(std::move(identifier));
1480 const bool for_listing,
1481 const std::string & child_tag,
const std::string& check_if_recursion)
1492 if (!
filter[
"backstab"].
blank() && child_tag ==
"filter_opponent") {
1493 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.");
1496 if(
filter[
"backstab"].to_bool() && child_tag ==
"filter_opponent"){
1497 const std::string& backstab_formula =
"enemy_of(self, flanker) and not flanker.petrified where flanker = unit_at(direction_from(loc, other.facing))";
1499 if(!
filter.has_child(
"filter_opponent")){
1500 filter_child[
"formula"] = backstab_formula;
1503 filter_opponent[
"formula"] = backstab_formula;
1504 filter_child.
add_child(
"and", filter_opponent);
1510 if ( !filter_child )
1526 filter_lock = weapon->update_variables_recursion(
filter);
1528 show_recursion_warning(weapon,
filter);
1533 if (
auto filter_weapon = filter_child->
optional_child(
"filter_weapon") ) {
1534 if ( !weapon || !weapon->matches_filter(*filter_weapon, check_if_recursion) )
1541 return ufilt.matches(*u,
loc);
1543 return ufilt.matches(*u,
loc, *u2);
1561 return special_active(*i.ability_cfg, AFFECT_SELF, ability, true);
1567 return special_active_impl(other_attack_, shared_from_this(), *i.ability_cfg, AFFECT_OTHER, ability, true);
1582 if(!abil_list.
empty() && !overwriters.
empty()){
1598 const std::string& apply_to = special[
"overwrite_specials"];
1599 return (apply_to ==
"one_side" || apply_to ==
"both_sides");
1610 if(overwriters.
empty()){
1615 if(overwriters.
size() >= 2){
1618 auto oi = (*
i.ability_cfg).optional_child(
"overwrite");
1620 if(oi && !oi[
"priority"].empty()){
1621 l = oi[
"priority"].to_double(0);
1623 auto oj = (*j.
ability_cfg).optional_child(
"overwrite");
1625 if(oj && !oj[
"priority"].empty()){
1626 r = oj[
"priority"].to_double(0);
1640 if(overwriters.
empty()){
1644 for(
const auto& j : overwriters) {
1646 bool affect_side = ((*j.ability_cfg)[
"overwrite_specials"] ==
"one_side");
1648 auto overwrite_specials = (*j.ability_cfg).optional_child(
"overwrite");
1649 double priority = overwrite_specials ? overwrite_specials[
"priority"].to_double(0) : 0.00;
1654 bool prior = (priority > 0) ? (has_overwrite_specials && has_overwrite_specials[
"priority"].to_double(0) >= priority) :
true;
1658 bool one_side_overwritable =
true;
1662 if(affect_side && is_overwritable){
1663 if(special_affects_self(*j.ability_cfg,
is_attacker_)){
1664 one_side_overwritable = special_affects_self(cfg,
is_attacker_);
1666 else if(special_affects_opponent(*j.ability_cfg, !
is_attacker_)){
1667 one_side_overwritable = special_affects_opponent(cfg, !
is_attacker_);
1672 bool special_matches =
true;
1673 if(overwrite_specials){
1674 auto overwrite_filter = (*overwrite_specials).optional_child(
"filter_specials");
1675 if(!overwrite_filter){
1676 overwrite_filter = (*overwrite_specials).optional_child(
"experimental_filter_specials");
1677 if(overwrite_filter){
1681 if(overwrite_filter && is_overwritable && one_side_overwritable){
1689 if(is_overwritable && one_side_overwritable && special_matches){
1708 std::vector<special_match>& id_result,
1709 const config& parent,
const std::string&
id,
1710 bool special_id=
true,
bool special_tags=
true) {
1711 if(special_id && special_tags){
1712 get_special_children(tag_result, id_result, parent,
id);
1713 }
else if(special_id && !special_tags){
1714 get_special_children_id(id_result, parent,
id);
1715 }
else if(!special_id && special_tags){
1716 get_special_children_tags(tag_result, parent,
id);
1724 show_recursion_warning(*
this, cfg);
1727 return (ability_active_impl(ability, cfg,
loc) && ability_affects_self(ability, cfg,
loc));
1734 show_recursion_warning(from, cfg);
1738 return (affects_side(cfg, side(), from.
side()) && from.
ability_active_impl(ability, cfg, adjacent[dir]) && ability_affects_adjacent(ability, cfg, dir,
loc, from));
1743 return (get_self_ability_bool(special, tag_name,
loc) && ability_affects_weapon(special, weapon,
false) && ability_affects_weapon(special, opp_weapon,
true));
1748 return (get_adj_ability_bool(special, tag_name, dir,
loc, from) && ability_affects_weapon(special, weapon,
false) && ability_affects_weapon(special, opp_weapon,
true));
1758 if(tag_name ==
"leadership" && leader_bool){
1759 if((*u).get_self_ability_bool_weapon(special, tag_name,
loc, self_attack, other_attack)) {
1763 if((*u).checking_tags().count(tag_name) != 0){
1764 if((*u).get_self_ability_bool(special, tag_name,
loc) &&
special_active_impl(self_attack, other_attack, special, whom, tag_name,
true)) {
1778 if(tag_name ==
"leadership" && leader_bool){
1779 if((*u).get_adj_ability_bool_weapon(special, tag_name, dir,
loc, from, self_attack, other_attack)) {
1783 if((*u).checking_tags().count(tag_name) != 0){
1784 if((*u).get_adj_ability_bool(special, tag_name, dir,
loc, from) &&
special_active_impl(self_attack, other_attack, special, whom, tag_name,
true)) {
1798 const unit_map& units = get_unit_map();
1800 std::vector<special_match> special_tag_matches_self;
1801 std::vector<special_match> special_id_matches_self;
1802 get_ability_children(special_tag_matches_self, special_id_matches_self, (*self_).abilities(), special, special_id , special_tags);
1804 for(
const special_match& entry : special_tag_matches_self) {
1811 for(
const special_match& entry : special_id_matches_self) {
1819 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
1821 if (it == units.
end() || it->incapacitated())
1823 if ( &*it ==
self_.get() )
1826 std::vector<special_match> special_tag_matches_adj;
1827 std::vector<special_match> special_id_matches_adj;
1828 get_ability_children(special_tag_matches_adj, special_id_matches_adj, it->abilities(), special, special_id , special_tags);
1830 for(
const special_match& entry : special_tag_matches_adj) {
1837 for(
const special_match& entry : special_id_matches_adj) {
1847 std::vector<special_match> special_tag_matches_other;
1848 std::vector<special_match> special_id_matches_other;
1849 get_ability_children(special_tag_matches_other, special_id_matches_other, (*other_).abilities(), special, special_id , special_tags);
1851 for(
const special_match& entry : special_tag_matches_other) {
1859 for(
const special_match& entry : special_id_matches_other) {
1867 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
1869 if (it == units.
end() || it->incapacitated())
1871 if ( &*it ==
other_.get() )
1874 std::vector<special_match> special_tag_matches_oadj;
1875 std::vector<special_match> special_id_matches_oadj;
1876 get_ability_children(special_tag_matches_oadj, special_id_matches_oadj, it->abilities(), special, special_id , special_tags);
1878 for(
const special_match& entry : special_tag_matches_oadj) {
1886 for(
const special_match& entry : special_id_matches_oadj) {
1902 if(
range().empty()){
1911 bool exclude_ability_attributes(
const std::string& tag_name,
const config &
filter)
1914 bool abilities_check = abilities_list::ability_value_tags().count(tag_name) != 0 || abilities_list::ability_no_value_tags().count(tag_name) != 0;
1915 if(
filter.has_attribute(
"active_on") && tag_name !=
"resistance" && abilities_check)
1917 if(
filter.has_attribute(
"apply_to") && tag_name !=
"resistance" && abilities_check)
1920 if(
filter.has_attribute(
"overwrite_specials") && abilities_list::weapon_number_tags().count(tag_name) == 0)
1923 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;
1924 if(
filter.has_attribute(
"value") && no_value_weapon_abilities_check)
1926 if(
filter.has_attribute(
"add") && no_value_weapon_abilities_check)
1928 if(
filter.has_attribute(
"sub") && no_value_weapon_abilities_check)
1930 if(
filter.has_attribute(
"multiply") && no_value_weapon_abilities_check)
1932 if(
filter.has_attribute(
"divide") && no_value_weapon_abilities_check)
1935 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;
1936 if(
filter.has_attribute(
"replacement_type") && tag_name !=
"damage_type" && all_engine)
1938 if(
filter.has_attribute(
"alternative_type") && tag_name !=
"damage_type" && all_engine)
1940 if(
filter.has_attribute(
"type") && tag_name !=
"plague" && all_engine)
1946 bool matches_ability_filter(
const config & cfg,
const std::string& tag_name,
const config &
filter)
1951 if(!exclude_ability_attributes(tag_name,
filter))
1957 if ( !filter_type.empty() &&
std::find(filter_type.begin(), filter_type.end(), tag_name) == filter_type.end() )
1965 if(!
filter[
"affect_adjacent"].empty()){
1966 bool adjacent = cfg.
has_child(
"affect_adjacent");
1967 if(
filter[
"affect_adjacent"].to_bool() != adjacent){
1999 if(!
filter[
"value"].empty()){
2000 if(tag_name ==
"drains"){
2004 }
else if(tag_name ==
"berserk"){
2008 }
else if(tag_name ==
"heal_on_hit" || tag_name ==
"heals" || tag_name ==
"regenerate" || tag_name ==
"leadership"){
2034 if(tag_name ==
"resistance"){
2063 auto fwml =
filter.optional_child(
"filter_wml");
2074 static bool common_matches_filter(
const config & cfg,
const std::string& tag_name,
const config &
filter)
2077 bool matches = matches_ability_filter(cfg, tag_name,
filter);
2080 for(
const auto [key, condition_cfg] :
filter.all_children_view() )
2084 matches = matches && common_matches_filter(cfg, tag_name, condition_cfg);
2087 else if ( key ==
"or" )
2088 matches = matches || common_matches_filter(cfg, tag_name, condition_cfg);
2091 else if ( key ==
"not" )
2092 matches = matches && !common_matches_filter(cfg, tag_name, condition_cfg);
2101 return common_matches_filter(cfg, tag_name,
filter);
2106 return common_matches_filter(cfg, tag_name,
filter);
2112 bool check_if_active =
filter[
"active"].to_bool();
2113 for(
const auto [key, cfg] :
specials().all_children_view()) {
2115 if(!check_if_active){
2128 for(
const auto [key, cfg] :
other_attack_->specials().all_children_view()) {
2141 if(!
filter[
"active"].to_bool()){
2144 const unit_map& units = get_unit_map();
2146 for(
const auto [key, cfg] : (*self_).abilities().all_children_view()) {
2147 if(
self_->ability_matches_filter(cfg, key,
filter)){
2155 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
2157 if (it == units.
end() || it->incapacitated())
2159 if ( &*it ==
self_.get() )
2162 for(
const auto [key, cfg] : it->abilities().all_children_view()) {
2171 for(
const auto [key, cfg] : (*other_).abilities().all_children_view()) {
2178 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
2180 if (it == units.
end() || it->incapacitated())
2182 if ( &*it ==
other_.get() )
2185 for(
const auto [key, cfg] : it->abilities().all_children_view()) {
2186 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)){
2197 if(
range().empty()){
2204 bool in_abilities_tag)
const
2224 const std::string& tag_name,
2225 bool in_abilities_tag)
2227 assert(self_attack || other_attack);
2228 bool is_attacker = self_attack ? self_attack->is_attacker_ : !other_attack->is_attacker_;
2229 bool is_for_listing = self_attack ? self_attack->is_for_listing_ : other_attack->is_for_listing_;
2235 if ( !special_affects_self(special, is_attacker) )
2239 if ( !special_affects_opponent(special, is_attacker) )
2244 const std::string & active_on = special[
"active_on"];
2245 if ( !active_on.empty() ) {
2246 if ( is_attacker && active_on !=
"offense" )
2248 if ( !is_attacker && active_on !=
"defense" )
2253 const unit_map& units = get_unit_map();
2255 unit_const_ptr self = self_attack ? self_attack->self_ : other_attack->other_;
2256 unit_const_ptr other = self_attack ? self_attack->other_ : other_attack->self_;
2257 map_location self_loc = self_attack ? self_attack->self_loc_ : other_attack->other_loc_;
2258 map_location other_loc = self_attack ? self_attack->other_loc_ : other_attack->self_loc_;
2260 if(
self ==
nullptr) {
2266 if(other ==
nullptr) {
2275 temporary_facing other_facing(other, other_loc.
get_relative_dir(self_loc));
2279 bool whom_is_self = ((whom ==
AFFECT_SELF) || ((whom ==
AFFECT_EITHER) && special_affects_self(special, is_attacker)));
2281 map_location their_loc = whom_is_self ? other_loc : self_loc;
2283 if (tag_name ==
"drains" && them && them->get_state(
"undrainable")) {
2286 if (tag_name ==
"plague" && them &&
2287 (them->get_state(
"unplagueable") ||
2291 if (tag_name ==
"poison" && them &&
2295 if (tag_name ==
"slow" && them &&
2299 if (tag_name ==
"petrifies" && them &&
2300 them->get_state(
"unpetrifiable")) {
2308 const map_location & att_loc = is_attacker ? self_loc : other_loc;
2309 const map_location & def_loc = is_attacker ? other_loc : self_loc;
2310 const const_attack_ptr& att_weapon = is_attacker ? self_attack : other_attack;
2311 const const_attack_ptr& def_weapon = is_attacker ? other_attack : self_attack;
2315 if (tag_name ==
"firststrike") {
2316 bool whom_is_defender = whom_is_self ? !is_attacker : is_attacker;
2317 if (whom_is_defender && att_weapon && att_weapon->has_special_or_ability(
"firststrike"))
2326 bool applied_both = special[
"apply_to"] ==
"both";
2327 const std::string& filter_self = in_abilities_tag ?
"filter_student" :
"filter_self";
2328 std::string self_check_if_recursion = (applied_both || whom_is_self) ? tag_name :
"";
2329 if (!special_unit_matches(
self, other, self_loc, self_attack, special, is_for_listing, filter_self, self_check_if_recursion))
2331 std::string opp_check_if_recursion = (applied_both || !whom_is_self) ? tag_name :
"";
2332 if (!special_unit_matches(other,
self, other_loc, other_attack, special, is_for_listing,
"filter_opponent", opp_check_if_recursion))
2336 bool applied_to_attacker = applied_both || (whom_is_self && is_attacker) || (!whom_is_self && !is_attacker);
2337 std::string att_check_if_recursion = applied_to_attacker ? tag_name :
"";
2338 if (!special_unit_matches(att, def, att_loc, att_weapon, special, is_for_listing,
"filter_attacker", att_check_if_recursion))
2340 bool applied_to_defender = applied_both || (whom_is_self && !is_attacker) || (!whom_is_self && is_attacker);
2341 std::string def_check_if_recursion= applied_to_defender ? tag_name :
"";
2342 if (!special_unit_matches(def, att, def_loc, def_weapon, special, is_for_listing,
"filter_defender", def_check_if_recursion))
2348 const std::string& filter_adjacent = in_abilities_tag ?
"filter_adjacent_student" :
"filter_adjacent";
2349 const std::string& filter_adjacent_location = in_abilities_tag ?
"filter_adjacent_student_location" :
"filter_adjacent_location";
2356 std::size_t count = 0;
2364 if (
i.has_attribute(
"is_enemy")) {
2382 std::size_t count = 0;
2387 if(!adj_filter.
match(adjacent[
static_cast<int>(
index)])) {
2417 if (
auto apply_filter = cfg.
optional_child(
"filter_base_value")) {
2424 return (cond_eq.
empty() || def == cond_eq.
to_int()) &&
2440 std::map<std::string,individual_effect> values_add;
2441 std::map<std::string,individual_effect> values_sub;
2442 std::map<std::string,individual_effect> values_mul;
2443 std::map<std::string,individual_effect> values_div;
2447 utils::optional<int> max_value = utils::nullopt;
2448 utils::optional<int> min_value = utils::nullopt;
2451 const config& cfg = *ability.ability_cfg;
2452 const std::string& effect_id = cfg[cfg[
"id"].
empty() ?
"name" :
"id"];
2460 callable.add(
"base_value", wfl::variant(def));
2461 return std::round(formula.evaluate(callable).as_int());
2464 int value_cum = cfg[
"cumulative"].to_bool() ? std::max(def, value) : value;
2467 set_effect_min.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2468 set_effect_max.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2471 if(value_cum > set_effect_max.
value) {
2472 set_effect_max.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2474 if(value_cum < set_effect_min.
value) {
2475 set_effect_min.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2483 max_value = max_value ? std::min(*max_value, cfg[
"max_value"].to_int()) : cfg[
"max_value"].to_int();
2486 min_value = min_value ? std::max(*min_value, cfg[
"min_value"].to_int()) : cfg[
"min_value"].to_int();
2492 callable.add(
"base_value", wfl::variant(def));
2493 return std::round(formula.evaluate(callable).as_int());
2496 if(add_effect == values_add.end() || add > add_effect->second.value) {
2497 values_add[effect_id].set(
ADD, add, ability.ability_cfg, ability.teacher_loc);
2502 callable.add(
"base_value", wfl::variant(def));
2503 return std::round(formula.evaluate(callable).as_int());
2506 if(sub_effect == values_sub.end() || sub < sub_effect->second.value) {
2507 values_sub[effect_id].set(
ADD, sub, ability.ability_cfg, ability.teacher_loc);
2512 callable.add(
"base_value", wfl::variant(def));
2513 return formula.evaluate(callable).as_decimal() / 1000.0 ;
2516 if(mul_effect == values_mul.end() || multiply > mul_effect->second.value) {
2517 values_mul[effect_id].set(
MUL, multiply, ability.ability_cfg, ability.teacher_loc);
2522 callable.add(
"base_value", wfl::variant(def));
2523 return formula.evaluate(callable).as_decimal() / 1000.0 ;
2527 ERR_NG <<
"division by zero with divide= in ability/weapon special " << effect_id;
2531 if(div_effect == values_div.end() || divide > div_effect->second.value) {
2532 values_div[effect_id].set(
DIV, divide, ability.ability_cfg, ability.teacher_loc);
2539 value_set = std::max(set_effect_max.
value, 0) + std::min(set_effect_min.
value, 0);
2540 if(set_effect_max.
value > def) {
2543 if(set_effect_min.
value < def) {
2556 double multiplier = 1.0;
2557 double divisor = 1.0;
2559 for(
const auto& val : values_mul) {
2560 multiplier *= val.second.value/100.0;
2564 for(
const auto& val : values_div) {
2565 divisor *= val.second.value/100.0;
2570 for(
const auto& val : values_add) {
2571 addition += val.second.value;
2577 int substraction = 0;
2578 for(
const auto& val : values_sub) {
2579 substraction += val.second.value;
2585 if(max_value && min_value && *min_value < *max_value) {
2587 }
else if(max_value && !min_value) {
2589 }
else if(min_value && !max_value) {
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 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
bool special_active(const config &special, AFFECTS whom, const std::string &tag_name, bool in_abilities_tag=false) 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
std::string select_replacement_type(const unit_ability_list &damage_type_list) const
Select best damage type based on frequency count for replacement_type.
unit_ability_list get_weapon_ability(const std::string &ability) const
Returns list for weapon like abilities for each ability type.
bool has_ability_with_filter(const config &filter) const
double modified_damage() const
Returns the damage per attack of this weapon, considering specials.
unit_ability_list get_specials_and_abilities(const std::string &special) 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, bool in_abilities_tag=false)
Returns whether or not the given special is active for the specified unit, based on the current conte...
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::pair< std::string, int > select_alternative_type(const unit_ability_list &damage_type_list, const unit_ability_list &resistance_list) const
Select best damage type based on highest damage for alternative_type.
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::pair< std::string, std::set< std::string > > damage_types() const
Return a type()/replacement_type and a list of alternative_types that should be displayed in the sele...
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.
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
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, int > effective_damage_type() const
The type of attack used and the resistance value that does the most damage.
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.
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_
double get_composite_double_value() const
double composite_double_value_
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)
const unit_type_map & types() const
A single unit type that the player may recruit.
const t_string & type_name() const
The name of the unit in the current language setting.
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 unit_type & type() const
This unit's type, accounting for gender and variation.
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.
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 TITLE_COLOR
const color_t INACTIVE_COLOR
std::vector< std::pair< int, int > > default_counts
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)
Applies Pango markup to the input specifying its display color.
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.
static std::vector< direction > all_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
unit_type_data unit_types