40 #include "formula/callable_objects.hpp"
48 #define ERR_NG LOG_STREAM(err, log_engine)
51 #define ERR_WML LOG_STREAM(err, log_wml)
54 class temporary_facing
64 u_->set_facing(new_dir);
70 u_->set_facing(save_dir_);
147 const team& get_team(std::size_t side)
167 bool affects_side(
const config& cfg, std::size_t side, std::size_t other_side)
169 const team& side_team = get_team(side);
171 if(side == other_side)
172 return cfg[
"affect_allies"].to_bool(
true);
174 return cfg[
"affect_enemies"].to_bool();
176 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)) {
227 const unit_map& units = get_unit_map();
230 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
232 if (it == units.
end() || it->incapacitated())
241 for(
const config& j : it->abilities_.child_range(tag_name)) {
264 std::vector<std::string> res;
267 std::string
id = cfg[
"id"];
269 res.push_back(std::move(
id));
282 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)
292 ab[
"description"].t_str() );
301 "female_name_inactive",
"name_inactive");
310 ab.
get_or(
"description_inactive",
"description").
t_str() );
321 std::vector<std::tuple<std::string, t_string,t_string,t_string>> res;
325 add_ability_tooltip(cfg,
gender_, res,
true);
333 std::vector<std::tuple<std::string, t_string,t_string,t_string>> res;
339 if (add_ability_tooltip(cfg,
gender_, res, active))
341 active_list.push_back(active);
352 void show_recursion_warning(
const unit&
unit,
const config& filter) {
358 static std::vector<std::tuple<std::string, std::string>> already_shown;
360 auto identifier = std::tuple<std::string, std::string>{
unit.
id(), filter.
debug()};
365 std::string_view filter_text_view = std::get<1>(identifier);
367 ERR_NG <<
"Looped recursion error for unit '" <<
unit.
id()
368 <<
"' while checking ability '" << filter_text_view <<
"'";
372 if(already_shown.size() > 100) {
373 already_shown.clear();
375 already_shown.push_back(std::move(identifier));
390 : parent(u.shared_from_this())
400 unit::recursion_guard::operator bool()
const {
406 assert(
this != &other);
415 assert(!parent->open_queries_.empty());
416 parent->open_queries_.pop_back();
424 show_recursion_warning(*
this, cfg);
432 bool illuminates = ability ==
"illuminates";
435 if ( !
unit_filter(
vconfig(*afilter)).set_use_flat_tod(illuminates).matches(*
this, loc) )
440 const unit_map& units = get_unit_map();
444 std::size_t count = 0;
446 ufilt.set_use_flat_tod(illuminates);
455 if (!ufilt(*
unit, *
this))
457 if((*this).id() == (*unit).id())
459 if (
i.has_attribute(
"is_enemy")) {
467 if (
i[
"count"].empty() && count != dirs.size()) {
477 std::size_t count = 0;
479 adj_filter.flatten(illuminates);
487 if(!adj_filter.match(adjacent[
static_cast<int>(
index)])) {
492 if (
i[
"count"].empty() && count != dirs.size()) {
504 bool illuminates = ability ==
"illuminates";
506 assert(dir >=0 && dir <= 5);
511 if (
i.has_attribute(
"adjacent")) {
513 if (std::find(dirs.begin(), dirs.end(), direction) == dirs.end()) {
517 if((*this).id() == from.
id()){
520 auto filter =
i.optional_child(
"filter");
522 unit_filter(
vconfig(*filter)).set_use_flat_tod(illuminates).matches(*
this, loc, from) ) {
532 bool affect_self = cfg[
"affect_self"].to_bool(
true);
533 if (!filter || !affect_self)
return affect_self;
539 const std::string filter_tag_name = is_opp ?
"filter_second_weapon" :
"filter_weapon";
548 filter_lock = weapon->update_variables_recursion(cfg);
550 show_recursion_warning(*
this, cfg);
553 return weapon->matches_filter(filter);
565 auto ret = std::find(image_list.begin(), image_list.end(), cfg[attribute_name].str());
566 if(ret == image_list.end()){
567 image_list.push_back(cfg[attribute_name].str());
573 std::vector<std::string> image_list;
581 if(!cfg[image_type +
"_image_self"].str().empty() &&
is_active){
586 const unit_map& units = get_unit_map();
591 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
593 if (it == units.
end() || it->incapacitated())
597 for(
const auto [key, cfg] : it->abilities_.all_children_view()) {
598 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))
605 if(image_list.size() >= 2){
606 std::sort(image_list.begin(), image_list.end());
614 callable.
add(
"attacker",
wfl::variant(std::make_shared<wfl::unit_callable>(*att)));
617 callable.
add(
"defender",
wfl::variant(std::make_shared<wfl::unit_callable>(*def)));
624 template<
typename T,
typename TFuncFormula>
625 class get_ability_value_visitor
626 #ifdef USING_BOOST_VARIANT
627 :
public boost::static_visitor<T>
632 get_ability_value_visitor(T def,
const TFuncFormula& formula_handler) : def_(def), formula_handler_(formula_handler) {}
634 T operator()(
const utils::monostate&)
const {
return def_; }
635 T operator()(
bool)
const {
return def_; }
636 T operator()(
int i)
const {
return static_cast<T
>(
i); }
637 T operator()(
unsigned long long u)
const {
return static_cast<T
>(u); }
638 T operator()(
double d)
const {
return static_cast<T
>(
d); }
639 T operator()(
const t_string&)
const {
return def_; }
640 T operator()(
const std::string&
s)
const
642 if(
s.size() >= 2 &&
s[0] ==
'(') {
643 return formula_handler_(
s);
645 return lexical_cast_default<T>(
s, def_);
650 const TFuncFormula& formula_handler_;
653 template<
typename T,
typename TFuncFormula>
656 return v.
apply_visitor(get_ability_value_visitor(def, [&](
const std::string&
s) {
659 const unit_map& units = get_unit_map();
663 if(u_itor == units.
end()) {
668 att->add_formula_context(callable);
671 callable.add(
"student",
wfl::variant(std::make_shared<wfl::unit_callable>(*uptr)));
674 callable.add(
"other",
wfl::variant(std::make_shared<wfl::unit_callable>(*uptr)));
678 lg::log_to_chat() <<
"Formula error in ability or weapon special: " <<
e.type <<
" at " <<
e.filename <<
':' <<
e.line <<
")\n";
679 ERR_WML <<
"Formula error in ability or weapon special: " <<
e.type <<
" at " <<
e.filename <<
':' <<
e.line <<
")";
686 template<
typename TComp>
689 if ( cfgs_.empty() ) {
695 bool only_cumulative =
true;
702 return formula.
evaluate(callable).as_int();
705 if ((*
p.ability_cfg)[
"cumulative"].to_bool()) {
707 if (value < 0) value = -value;
708 if (only_cumulative && !comp(value, abs_max)) {
710 best_loc =
p.teacher_loc;
712 }
else if (only_cumulative || comp(flat, value)) {
713 only_cumulative =
false;
715 best_loc =
p.teacher_loc;
718 return std::pair(flat + stack, best_loc);
721 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;
722 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;
756 std::string tag_name;
772 bool get_special_children(std::vector<special_match>& tag_result,
773 std::vector<special_match>& id_result,
774 const config& parent,
const std::string&
id,
775 bool just_peeking=
false) {
778 if (just_peeking && (key ==
id || cfg[
"id"] ==
id)) {
783 special_match special = { key, &cfg };
784 tag_result.push_back(special);
786 if(cfg[
"id"] ==
id) {
787 special_match special = { key, &cfg };
788 id_result.push_back(special);
794 bool get_special_children_id(std::vector<special_match>& id_result,
795 const config& parent,
const std::string&
id,
796 bool just_peeking=
false) {
799 if (just_peeking && (cfg[
"id"] ==
id)) {
803 if(cfg[
"id"] ==
id) {
804 special_match special = { key, &cfg };
805 id_result.push_back(special);
811 bool get_special_children_tags(std::vector<special_match>& tag_result,
812 const config& parent,
const std::string&
id,
813 bool just_peeking=
false) {
816 if (just_peeking && (key ==
id)) {
821 special_match special = { key, &cfg };
822 tag_result.push_back(special);
839 std::vector<special_match> special_tag_matches;
840 std::vector<special_match> special_id_matches;
841 if(special_id && special_tags){
842 if ( get_special_children(special_tag_matches, special_id_matches, specials_, special, simple_check) ) {
845 }
else if(special_id && !special_tags){
846 if ( get_special_children_id(special_id_matches, specials_, special, simple_check) ) {
849 }
else if(!special_id && special_tags){
850 if ( get_special_children_tags(special_tag_matches, specials_, special, simple_check) ) {
858 for(
const special_match& entry : special_tag_matches) {
859 if ( special_active(*entry.cfg, AFFECT_SELF, entry.tag_name) ) {
865 for(
const special_match& entry : special_id_matches) {
866 if ( special_active(*entry.cfg, AFFECT_SELF, entry.tag_name) ) {
874 if ( simple_check || !other_attack_ ) {
878 std::vector<special_match> special_tag_matches;
879 std::vector<special_match> special_id_matches;
880 if(special_id && special_tags){
881 get_special_children(special_tag_matches, special_id_matches, other_attack_->specials_, special);
882 }
else if(special_id && !special_tags){
883 get_special_children_id(special_id_matches, other_attack_->specials_, special);
884 }
else if(!special_id && special_tags){
885 get_special_children_tags(special_tag_matches, other_attack_->specials_, special);
888 for(
const special_match& entry : special_tag_matches) {
889 if ( other_attack_->special_active(*entry.cfg, AFFECT_OTHER, entry.tag_name) ) {
895 for(
const special_match& entry : special_id_matches) {
896 if ( other_attack_->special_active(*entry.cfg, AFFECT_OTHER, entry.tag_name) ) {
911 const map_location loc = self_ ? self_->get_location() : self_loc_;
914 for(
const config&
i : specials_.child_range(special)) {
915 if(special_active(
i, AFFECT_SELF, special)) {
924 for(
const config&
i : other_attack_->specials_.child_range(special)) {
925 if(other_attack_->special_active(
i, AFFECT_OTHER, special)) {
942 boost::dynamic_bitset<>* active_list)
const
945 std::vector<std::pair<t_string, t_string>> res;
947 active_list->clear();
949 for(
const auto [key, cfg] : specials_.all_children_view())
951 if ( !active_list || special_active(cfg, AFFECT_EITHER, key) ) {
954 res.emplace_back(
name, cfg[
"description"].t_str() );
956 active_list->push_back(
true);
961 res.emplace_back(
name, cfg.
get_or(
"description_inactive",
"description").
t_str() );
962 active_list->push_back(
false);
977 static void add_name(std::string& temp_string,
bool active,
const std::string
name, std::set<std::string>& checking_name)
981 checking_name.insert(
name);
982 if (!temp_string.empty()) temp_string +=
", ";
999 for(
const auto [key, cfg] : specials_.all_children_view())
1001 const bool active = special_active(cfg, AFFECT_EITHER, key);
1003 const std::string&
name =
1006 : cfg.
get_or(
"name_inactive",
"name").
str();
1019 std::string temp_string;
1020 std::set<std::string> checking_name;
1021 weapon_specials_impl_self(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name);
1022 weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name, {},
"affect_allies");
1023 if(!temp_string.empty() && !res.empty()) {
1024 temp_string =
", \n" + temp_string;
1026 }
else if (!temp_string.empty()){
1032 static void add_name_list(std::string& temp_string, std::string& weapon_abilities, std::set<std::string>& checking_name,
const std::string from_str)
1034 if(!temp_string.empty()){
1035 temp_string = from_str.c_str() + temp_string;
1036 weapon_abilities += (!weapon_abilities.empty() && !temp_string.empty()) ?
"\n" :
"";
1037 weapon_abilities += temp_string;
1038 temp_string.clear();
1039 checking_name.clear();
1046 std::string temp_string, weapon_abilities;
1047 std::set<std::string> checking_name;
1048 for(
const auto [key, cfg] : specials_.all_children_view()) {
1050 const bool active = special_active(cfg, AFFECT_SELF, key);
1051 add_name(temp_string, active, cfg[
"name"].str(), checking_name);
1054 add_name_list(temp_string, weapon_abilities, checking_name,
"");
1056 weapon_specials_impl_self(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name,
checking_tags,
true);
1057 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Owned: "));
1059 weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name,
checking_tags,
"affect_allies",
true);
1061 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Taught: "));
1063 weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name,
checking_tags,
"affect_enemies",
true);
1065 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Taught: (by an enemy): "));
1069 for(
const auto [key, cfg] : other_attack_->specials_.all_children_view()) {
1071 const bool active = other_attack_->special_active(cfg, AFFECT_OTHER, key);
1072 add_name(temp_string, active, cfg[
"name"].str(), checking_name);
1076 weapon_specials_impl_self(temp_string, other_, other_attack_, shared_from_this(), other_loc_, AFFECT_OTHER, checking_name,
checking_tags);
1077 weapon_specials_impl_adj(temp_string, other_, other_attack_, shared_from_this(), other_loc_, AFFECT_OTHER, checking_name,
checking_tags);
1078 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Used by opponent: "));
1080 return weapon_abilities;
1084 std::string& temp_string,
1090 std::set<std::string>& checking_name,
1095 for(
const auto [key, cfg] : self->abilities().all_children_view()){
1097 const bool active = tag_checked && check_self_abilities_impl(self_attack, other_attack, cfg,
self, self_loc, whom, key, leader_bool);
1098 add_name(temp_string, active, cfg[
"name"].str(), checking_name);
1104 std::string& temp_string,
1110 std::set<std::string>& checking_name,
1112 const std::string& affect_adjacents,
1115 const unit_map& units = get_unit_map();
1118 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
1120 if (it == units.
end() || it->incapacitated())
1122 if(&*it ==
self.
get())
1124 for(
const auto [key, cfg] : it->abilities().all_children_view()) {
1126 bool default_bool = (affect_adjacents ==
"affect_allies") ?
true :
false;
1127 bool affect_allies = (!affect_adjacents.empty()) ? cfg[affect_adjacents].to_bool(default_bool) :
true;
1128 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;
1129 add_name(temp_string, active, cfg[
"name"].str(), checking_name);
1155 : parent(weapon.shared_from_this())
1157 weapon.
self_ =
self;
1175 : parent(weapon.shared_from_this())
1177 weapon.
self_ =
self;
1195 : parent(weapon.shared_from_this())
1207 : parent(weapon.shared_from_this())
1215 if(was_moved)
return;
1220 parent->is_attacker_ =
false;
1221 parent->other_attack_ =
nullptr;
1222 parent->is_for_listing_ =
false;
1226 : parent(other.parent)
1228 other.was_moved =
true;
1239 unsigned & max_attacks)
const
1244 if ( attacks_value < 0 ) {
1246 ERR_NG <<
"negative number of strikes after applying weapon specials";
1251 if ( !swarm_specials.
empty() ) {
1252 min_attacks = std::max<int>(0, swarm_specials.
highest(
"swarm_attacks_min").first);
1253 max_attacks = std::max<int>(0, swarm_specials.
highest(
"swarm_attacks_max", attacks_value).first);
1255 min_attacks = max_attacks = attacks_value;
1261 std::map<std::string, unsigned int> type_count;
1262 unsigned int max = 0;
1263 for(
auto&
i : damage_type_list) {
1265 if(
c.has_attribute(
"replacement_type")) {
1266 std::string
type =
c[
"replacement_type"].str();
1267 unsigned int count = ++type_count[
type];
1274 if (type_count.empty())
return "";
1276 std::vector<std::string> type_list;
1277 for(
auto&
i : type_count){
1278 if(
i.second == max){
1279 type_list.push_back(
i.first);
1283 if(type_list.empty())
return "";
1285 return type_list.front();
1290 std::map<std::string, int> type_res;
1291 int max_res = std::numeric_limits<int>::min();
1292 for(
auto&
i : damage_type_list) {
1294 if(
c.has_attribute(
"alternative_type")) {
1295 std::string
type =
c[
"alternative_type"].str();
1296 if(type_res.count(
type) == 0){
1298 max_res = std::max(max_res, type_res[
type]);
1303 if (type_res.empty())
return "";
1305 std::vector<std::string> type_list;
1306 for(
auto&
i : type_res){
1307 if(
i.second == max_res){
1308 type_list.push_back(
i.first);
1311 if(type_list.empty())
return "";
1313 return type_list.front();
1318 bool is_alternative = (key_name ==
"alternative_type");
1319 if(is_alternative &&
other_){
1321 }
else if(!is_alternative){
1336 if(damage_type_list.
empty()){
1337 return {
type(),
""};
1342 resistance_list = (*other_).get_abilities_weapons(
"resistance",
other_loc_,
other_attack_, shared_from_this());
1344 std::string replacement_type =
select_damage_type(damage_type_list,
"replacement_type", resistance_list);
1345 std::string alternative_type =
select_damage_type(damage_type_list,
"alternative_type", resistance_list);
1346 std::string type_damage = replacement_type.empty() ?
type() : replacement_type;
1347 if(!alternative_type.empty() && type_damage != alternative_type){
1348 return {type_damage, alternative_type};
1350 return {type_damage,
""};
1356 if(damage_type_list.
empty()){
1359 std::set<std::string> damage_types;
1360 for(
auto&
i : damage_type_list) {
1362 if(
c.has_attribute(
"alternative_type")){
1363 damage_types.insert(
c[
"alternative_type"].str());
1367 return damage_types;
1377 return damage_value;
1389 bool special_affects_opponent(
const config& special,
bool is_attacker)
1392 const std::string& apply_to = special[
"apply_to"];
1393 if ( apply_to.empty() )
1395 if ( apply_to ==
"both" )
1397 if ( apply_to ==
"opponent" )
1399 if ( is_attacker && apply_to ==
"defender" )
1401 if ( !is_attacker && apply_to ==
"attacker" )
1411 bool special_affects_self(
const config& special,
bool is_attacker)
1414 const std::string& apply_to = special[
"apply_to"];
1415 if ( apply_to.empty() )
1417 if ( apply_to ==
"both" )
1419 if ( apply_to ==
"self" )
1421 if ( is_attacker && apply_to ==
"attacker" )
1423 if ( !is_attacker && apply_to ==
"defender")
1438 static std::vector<std::tuple<std::string, std::string>> already_shown;
1440 auto identifier = std::tuple<std::string, std::string>{attack->id(), filter.
debug()};
1445 std::string_view filter_text_view = std::get<1>(identifier);
1447 ERR_NG <<
"Looped recursion error for weapon '" << attack->id()
1448 <<
"' while checking weapon special '" << filter_text_view <<
"'";
1452 if(already_shown.size() > 100) {
1453 already_shown.clear();
1455 already_shown.push_back(std::move(identifier));
1475 const bool for_listing,
1476 const std::string & child_tag,
const std::string& check_if_recursion)
1478 if (for_listing && !loc.
valid())
1487 if (!filter[
"backstab"].
blank() && child_tag ==
"filter_opponent") {
1488 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.");
1491 if(filter[
"backstab"].to_bool() && child_tag ==
"filter_opponent"){
1492 const std::string& backstab_formula =
"enemy_of(self, flanker) and not flanker.petrified where flanker = unit_at(direction_from(loc, other.facing))";
1494 if(!filter.
has_child(
"filter_opponent")){
1495 filter_child[
"formula"] = backstab_formula;
1498 filter_opponent[
"formula"] = backstab_formula;
1499 filter_child.
add_child(
"and", filter_opponent);
1502 const config& filter_backstab = filter[
"backstab"].to_bool() ? cfg : filter;
1505 if ( !filter_child )
1521 filter_lock = weapon->update_variables_recursion(filter);
1523 show_recursion_warning(weapon, filter);
1528 if (
auto filter_weapon = filter_child->
optional_child(
"filter_weapon") ) {
1529 if ( !weapon || !weapon->matches_filter(*filter_weapon, check_if_recursion) )
1536 return ufilt.matches(*u, loc);
1538 return ufilt.matches(*u, loc, *u2);
1556 return special_active(*i.ability_cfg, AFFECT_SELF, ability,
"filter_student");
1562 return special_active_impl(other_attack_, shared_from_this(), *i.ability_cfg, AFFECT_OTHER, ability,
"filter_student");
1577 if(!abil_list.
empty() && !overwriters.
empty()){
1593 const std::string& apply_to = special[
"overwrite_specials"];
1594 return (apply_to ==
"one_side" || apply_to ==
"both_sides");
1605 if(overwriters.
empty()){
1610 if(overwriters.
size() >= 2){
1613 auto oi = (*
i.ability_cfg).optional_child(
"overwrite");
1615 if(oi && !oi[
"priority"].empty()){
1616 l = oi[
"priority"].to_double(0);
1618 auto oj = (*j.
ability_cfg).optional_child(
"overwrite");
1620 if(oj && !oj[
"priority"].empty()){
1621 r = oj[
"priority"].to_double(0);
1635 if(overwriters.
empty()){
1639 for(
const auto& j : overwriters) {
1641 bool affect_side = ((*j.ability_cfg)[
"overwrite_specials"] ==
"one_side");
1643 auto overwrite_specials = (*j.ability_cfg).optional_child(
"overwrite");
1644 double priority = overwrite_specials ? overwrite_specials[
"priority"].to_double(0) : 0.00;
1649 bool prior = (priority > 0) ? (has_overwrite_specials && has_overwrite_specials[
"priority"].to_double(0) >= priority) :
true;
1653 bool one_side_overwritable =
true;
1657 if(affect_side && is_overwritable){
1658 if(special_affects_self(*j.ability_cfg,
is_attacker_)){
1659 one_side_overwritable = special_affects_self(cfg,
is_attacker_);
1661 else if(special_affects_opponent(*j.ability_cfg, !
is_attacker_)){
1662 one_side_overwritable = special_affects_opponent(cfg, !
is_attacker_);
1667 bool special_matches =
true;
1668 if(overwrite_specials){
1669 auto overwrite_filter = (*overwrite_specials).optional_child(
"filter_specials");
1670 if(!overwrite_filter){
1671 overwrite_filter = (*overwrite_specials).optional_child(
"experimental_filter_specials");
1672 if(overwrite_filter){
1676 if(overwrite_filter && is_overwritable && one_side_overwritable){
1684 if(is_overwritable && one_side_overwritable && special_matches){
1703 std::vector<special_match>& id_result,
1704 const config& parent,
const std::string&
id,
1705 bool special_id=
true,
bool special_tags=
true) {
1706 if(special_id && special_tags){
1707 get_special_children(tag_result, id_result, parent,
id);
1708 }
else if(special_id && !special_tags){
1709 get_special_children_id(id_result, parent,
id);
1710 }
else if(!special_id && special_tags){
1711 get_special_children_tags(tag_result, parent,
id);
1719 show_recursion_warning(*
this, cfg);
1722 return (ability_active_impl(ability, cfg, loc) && ability_affects_self(ability, cfg, loc));
1729 show_recursion_warning(from, cfg);
1733 return (affects_side(cfg, side(), from.
side()) && from.
ability_active_impl(ability, cfg, adjacent[dir]) && ability_affects_adjacent(ability, cfg, dir, loc, from));
1738 return (get_self_ability_bool(special, tag_name, loc) && ability_affects_weapon(special, weapon,
false) && ability_affects_weapon(special, opp_weapon,
true));
1743 return (get_adj_ability_bool(special, tag_name, dir, loc, from) && ability_affects_weapon(special, weapon,
false) && ability_affects_weapon(special, opp_weapon,
true));
1753 if(tag_name ==
"leadership" && leader_bool){
1754 if((*u).get_self_ability_bool_weapon(special, tag_name, loc, self_attack, other_attack)) {
1758 if((*u).checking_tags().count(tag_name) != 0){
1759 if((*u).get_self_ability_bool(special, tag_name, loc) &&
special_active_impl(self_attack, other_attack, special, whom, tag_name,
"filter_student")) {
1773 if(tag_name ==
"leadership" && leader_bool){
1774 if((*u).get_adj_ability_bool_weapon(special, tag_name, dir, loc, from, self_attack, other_attack)) {
1778 if((*u).checking_tags().count(tag_name) != 0){
1779 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")) {
1793 const unit_map& units = get_unit_map();
1795 std::vector<special_match> special_tag_matches_self;
1796 std::vector<special_match> special_id_matches_self;
1797 get_ability_children(special_tag_matches_self, special_id_matches_self, (*self_).abilities(), special, special_id , special_tags);
1799 for(
const special_match& entry : special_tag_matches_self) {
1806 for(
const special_match& entry : special_id_matches_self) {
1814 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
1816 if (it == units.
end() || it->incapacitated())
1818 if ( &*it ==
self_.get() )
1821 std::vector<special_match> special_tag_matches_adj;
1822 std::vector<special_match> special_id_matches_adj;
1823 get_ability_children(special_tag_matches_adj, special_id_matches_adj, it->abilities(), special, special_id , special_tags);
1825 for(
const special_match& entry : special_tag_matches_adj) {
1832 for(
const special_match& entry : special_id_matches_adj) {
1842 std::vector<special_match> special_tag_matches_other;
1843 std::vector<special_match> special_id_matches_other;
1844 get_ability_children(special_tag_matches_other, special_id_matches_other, (*other_).abilities(), special, special_id , special_tags);
1846 for(
const special_match& entry : special_tag_matches_other) {
1854 for(
const special_match& entry : special_id_matches_other) {
1862 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
1864 if (it == units.
end() || it->incapacitated())
1866 if ( &*it ==
other_.get() )
1869 std::vector<special_match> special_tag_matches_oadj;
1870 std::vector<special_match> special_id_matches_oadj;
1871 get_ability_children(special_tag_matches_oadj, special_id_matches_oadj, it->abilities(), special, special_id , special_tags);
1873 for(
const special_match& entry : special_tag_matches_oadj) {
1881 for(
const special_match& entry : special_id_matches_oadj) {
1897 if(
range().empty()){
1906 bool exclude_ability_attributes(
const std::string& tag_name,
const config & filter)
1909 bool abilities_check = abilities_list::ability_value_tags().count(tag_name) != 0 || abilities_list::ability_no_value_tags().count(tag_name) != 0;
1910 if(filter.
has_attribute(
"active_on") && tag_name !=
"resistance" && abilities_check)
1912 if(filter.
has_attribute(
"apply_to") && tag_name !=
"resistance" && abilities_check)
1915 if(filter.
has_attribute(
"overwrite_specials") && abilities_list::weapon_number_tags().count(tag_name) == 0)
1918 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;
1919 if(filter.
has_attribute(
"value") && no_value_weapon_abilities_check)
1921 if(filter.
has_attribute(
"add") && no_value_weapon_abilities_check)
1923 if(filter.
has_attribute(
"sub") && no_value_weapon_abilities_check)
1925 if(filter.
has_attribute(
"multiply") && no_value_weapon_abilities_check)
1927 if(filter.
has_attribute(
"divide") && no_value_weapon_abilities_check)
1930 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;
1931 if(filter.
has_attribute(
"replacement_type") && tag_name !=
"damage_type" && all_engine)
1933 if(filter.
has_attribute(
"alternative_type") && tag_name !=
"damage_type" && all_engine)
1935 if(filter.
has_attribute(
"type") && tag_name !=
"plague" && all_engine)
1941 bool matches_ability_filter(
const config & cfg,
const std::string& tag_name,
const config & filter)
1946 if(!exclude_ability_attributes(tag_name, filter))
1951 const std::vector<std::string> filter_type =
utils::split(filter[
"tag_name"]);
1952 if ( !filter_type.empty() && std::find(filter_type.begin(), filter_type.end(), tag_name) == filter_type.end() )
1960 if(!filter[
"affect_adjacent"].empty()){
1961 bool adjacent = cfg.
has_child(
"affect_adjacent");
1962 if(filter[
"affect_adjacent"].to_bool() != adjacent){
1994 if(!filter[
"value"].empty()){
1995 if(tag_name ==
"drains"){
1999 }
else if(tag_name ==
"berserk"){
2003 }
else if(tag_name ==
"heal_on_hit" || tag_name ==
"heals" || tag_name ==
"regenerate" || tag_name ==
"leadership"){
2029 if(tag_name ==
"resistance"){
2069 static bool common_matches_filter(
const config & cfg,
const std::string& tag_name,
const config & filter)
2072 bool matches = matches_ability_filter(cfg, tag_name, filter);
2079 matches = matches && common_matches_filter(cfg, tag_name, condition_cfg);
2082 else if ( key ==
"or" )
2083 matches = matches || common_matches_filter(cfg, tag_name, condition_cfg);
2086 else if ( key ==
"not" )
2087 matches = matches && !common_matches_filter(cfg, tag_name, condition_cfg);
2096 return common_matches_filter(cfg, tag_name, filter);
2101 return common_matches_filter(cfg, tag_name, filter);
2107 bool check_if_active = filter[
"active"].to_bool();
2108 for(
const auto [key, cfg] :
specials().all_children_view()) {
2110 if(!check_if_active){
2123 for(
const auto [key, cfg] :
other_attack_->specials().all_children_view()) {
2124 if(
other_attack_->special_matches_filter(cfg, key, filter)){
2136 if(!filter[
"active"].to_bool()){
2139 const unit_map& units = get_unit_map();
2141 for(
const auto [key, cfg] : (*self_).abilities().all_children_view()) {
2142 if(
self_->ability_matches_filter(cfg, key, filter)){
2150 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
2152 if (it == units.
end() || it->incapacitated())
2154 if ( &*it ==
self_.get() )
2157 for(
const auto [key, cfg] : it->abilities().all_children_view()) {
2166 for(
const auto [key, cfg] : (*other_).abilities().all_children_view()) {
2173 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
2175 if (it == units.
end() || it->incapacitated())
2177 if ( &*it ==
other_.get() )
2180 for(
const auto [key, cfg] : it->abilities().all_children_view()) {
2181 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)){
2192 if(
range().empty()){
2199 const std::string& filter_self)
const
2219 const std::string& tag_name,
2220 const std::string& filter_self)
2222 assert(self_attack || other_attack);
2223 bool is_attacker = self_attack ? self_attack->is_attacker_ : !other_attack->is_attacker_;
2224 bool is_for_listing = self_attack ? self_attack->is_for_listing_ : other_attack->is_for_listing_;
2230 if ( !special_affects_self(special, is_attacker) )
2234 if ( !special_affects_opponent(special, is_attacker) )
2239 const std::string & active_on = special[
"active_on"];
2240 if ( !active_on.empty() ) {
2241 if ( is_attacker && active_on !=
"offense" )
2243 if ( !is_attacker && active_on !=
"defense" )
2248 const unit_map& units = get_unit_map();
2250 unit_const_ptr self = self_attack ? self_attack->self_ : other_attack->other_;
2251 unit_const_ptr other = self_attack ? self_attack->other_ : other_attack->self_;
2252 map_location self_loc = self_attack ? self_attack->self_loc_ : other_attack->other_loc_;
2253 map_location other_loc = self_attack ? self_attack->other_loc_ : other_attack->self_loc_;
2255 if(
self ==
nullptr) {
2261 if(other ==
nullptr) {
2270 temporary_facing other_facing(other, other_loc.
get_relative_dir(self_loc));
2274 bool whom_is_self = ((whom ==
AFFECT_SELF) || ((whom ==
AFFECT_EITHER) && special_affects_self(special, is_attacker)));
2276 map_location their_loc = whom_is_self ? other_loc : self_loc;
2278 if (tag_name ==
"drains" && them && them->get_state(
"undrainable")) {
2281 if (tag_name ==
"plague" && them &&
2282 (them->get_state(
"unplagueable") ||
2286 if (tag_name ==
"poison" && them &&
2290 if (tag_name ==
"slow" && them &&
2294 if (tag_name ==
"petrifies" && them &&
2295 them->get_state(
"unpetrifiable")) {
2303 const map_location & att_loc = is_attacker ? self_loc : other_loc;
2304 const map_location & def_loc = is_attacker ? other_loc : self_loc;
2310 if (tag_name ==
"firststrike") {
2311 bool whom_is_defender = whom_is_self ? !is_attacker : is_attacker;
2312 if (whom_is_defender && att_weapon && att_weapon->has_special_or_ability(
"firststrike"))
2321 bool applied_both = special[
"apply_to"] ==
"both";
2322 std::string self_check_if_recursion = (applied_both || whom_is_self) ? tag_name :
"";
2323 if (!special_unit_matches(
self, other, self_loc, self_attack, special, is_for_listing, filter_self, self_check_if_recursion))
2325 std::string opp_check_if_recursion = (applied_both || !whom_is_self) ? tag_name :
"";
2326 if (!special_unit_matches(other,
self, other_loc, other_attack, special, is_for_listing,
"filter_opponent", opp_check_if_recursion))
2330 bool applied_to_attacker = applied_both || (whom_is_self && is_attacker) || (!whom_is_self && !is_attacker);
2331 std::string att_check_if_recursion = applied_to_attacker ? tag_name :
"";
2332 if (!special_unit_matches(att, def, att_loc, att_weapon, special, is_for_listing,
"filter_attacker", att_check_if_recursion))
2334 bool applied_to_defender = applied_both || (whom_is_self && !is_attacker) || (!whom_is_self && is_attacker);
2335 std::string def_check_if_recursion= applied_to_defender ? tag_name :
"";
2336 if (!special_unit_matches(def, att, def_loc, def_weapon, special, is_for_listing,
"filter_defender", def_check_if_recursion))
2344 std::size_t count = 0;
2354 if (
i.has_attribute(
"is_enemy")) {
2362 if (
i[
"count"].empty() && count != dirs.size()) {
2373 std::size_t count = 0;
2380 if(!adj_filter.
match(adjacent[
static_cast<int>(
index)])) {
2385 if (
i[
"count"].empty() && count != dirs.size()) {
2411 if (
auto apply_filter = cfg.
optional_child(
"filter_base_value")) {
2418 return (cond_eq.
empty() || def == cond_eq.
to_int()) &&
2434 std::map<std::string,individual_effect> values_add;
2435 std::map<std::string,individual_effect> values_sub;
2436 std::map<std::string,individual_effect> values_mul;
2437 std::map<std::string,individual_effect> values_div;
2441 utils::optional<int> max_value = utils::nullopt;
2442 utils::optional<int> min_value = utils::nullopt;
2445 const config& cfg = *ability.ability_cfg;
2446 const std::string& effect_id = cfg[cfg[
"id"].
empty() ?
"name" :
"id"];
2454 callable.add(
"base_value", wfl::variant(def));
2455 return formula.evaluate(callable).as_int();
2458 int value_cum = cfg[
"cumulative"].to_bool() ? std::max(def, value) : value;
2461 set_effect_min.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2462 set_effect_max.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2465 if(value_cum > set_effect_max.
value) {
2466 set_effect_max.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2468 if(value_cum < set_effect_min.
value) {
2469 set_effect_min.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2477 max_value = max_value ? std::min(*max_value, cfg[
"max_value"].to_int()) : cfg[
"max_value"].to_int();
2480 min_value = min_value ? std::max(*min_value, cfg[
"min_value"].to_int()) : cfg[
"min_value"].to_int();
2486 callable.add(
"base_value", wfl::variant(def));
2487 return formula.evaluate(callable).as_int();
2490 if(add_effect == values_add.end() || add > add_effect->second.value) {
2491 values_add[effect_id].set(
ADD, add, ability.ability_cfg, ability.teacher_loc);
2496 callable.add(
"base_value", wfl::variant(def));
2497 return formula.evaluate(callable).as_int();
2500 if(sub_effect == values_sub.end() || sub < sub_effect->second.value) {
2501 values_sub[effect_id].set(
ADD, sub, ability.ability_cfg, ability.teacher_loc);
2506 callable.add(
"base_value", wfl::variant(def));
2507 return formula.evaluate(callable).as_decimal() / 1000.0 ;
2510 if(mul_effect == values_mul.end() || multiply > mul_effect->second.value) {
2511 values_mul[effect_id].set(
MUL, multiply, ability.ability_cfg, ability.teacher_loc);
2516 callable.add(
"base_value", wfl::variant(def));
2517 return formula.evaluate(callable).as_decimal() / 1000.0 ;
2521 ERR_NG <<
"division by zero with divide= in ability/weapon special " << effect_id;
2525 if(div_effect == values_div.end() || divide > div_effect->second.value) {
2526 values_div[effect_id].set(
DIV, divide, ability.ability_cfg, ability.teacher_loc);
2533 value_set = std::max(set_effect_max.
value, 0) + std::min(set_effect_min.
value, 0);
2534 if(set_effect_max.
value > def) {
2537 if(set_effect_min.
value < def) {
2550 double multiplier = 1.0;
2551 double divisor = 1.0;
2553 for(
const auto& val : values_mul) {
2554 multiplier *= val.second.value/100.0;
2558 for(
const auto& val : values_div) {
2559 divisor *= val.second.value/100.0;
2564 for(
const auto& val : values_add) {
2565 addition += val.second.value;
2571 int substraction = 0;
2572 for(
const auto& val : values_sub) {
2573 substraction += val.second.value;
2577 composite_value_ =
static_cast<int>((value_set + addition + substraction) * multiplier / divisor);
2579 if(max_value && min_value && *min_value < *max_value) {
2581 }
else if(max_value && !min_value) {
2583 }
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...
Helper similar to std::unique_lock for detecting when calculations such as has_special have entered i...
specials_context_t(const attack_type &weapon, bool attacking)
Initialize weapon specials context for listing.
const config & specials() const
std::string weapon_specials() const
Returns a comma-separated string of active names for the specials of *this.
bool check_self_abilities(const config &cfg, const std::string &special) const
check_self_abilities : return an boolean value for checking of activities of abilities used like weap...
bool has_special(const std::string &special, bool simple_check=false, bool special_id=true, bool special_tags=true) const
Returns whether or not *this has a special with a tag or id equal to special.
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
bool has_special_or_ability_with_filter(const config &filter) 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.
bool has_ability_with_filter(const config &filter) const
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...
recursion_guard update_variables_recursion(const config &special) const
Tests which might otherwise cause infinite recursion should call this, check that the returned object...
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 has_special_with_filter(const config &filter) const
check if special matche
bool special_active(const config &special, AFFECTS whom, const std::string &tag_name, const std::string &filter_self="filter_self") const
unit_ability_list get_specials(const std::string &special) const
Returns the currently active specials as an ability list, given the current context (see set_specials...
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.
std::string str(const std::string &fallback="") const
int to_int(int def=0) const
auto apply_visitor(const V &visitor) const
Visitor support: Applies a visitor to the underlying variant.
bool blank() const
Tests for an attribute that was never set.
bool empty() const
Tests for an attribute that either was never set or was set to "".
A config object defines a single node in a WML file, with access to child nodes.
const attribute_value & get_or(const config_key_type key, const config_key_type default_key) const
Chooses a value.
config & mandatory_child(config_key_type key, int n=0)
Returns the nth child with the given key, or throws an error if there is none.
bool matches(const config &filter) const
auto all_children_view() const
In-order iteration over all children.
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
bool has_attribute(config_key_type key) const
child_itors child_range(config_key_type key)
config & child_or_add(config_key_type key)
Returns a reference to the first child with the given key.
std::string debug() const
const attribute_value * get(config_key_type key) const
Returns a pointer to the attribute with the given key or nullptr if it does not exist.
optional_config_impl< config > optional_child(config_key_type key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
config & add_child(config_key_type key)
Abstract class for exposing game data that doesn't depend on the GUI, however which for historical re...
const team & get_team(int side) const
This getter takes a 1-based side number, not a 0-based team number.
virtual const unit_map & units() const =0
const display_context & context() const
static display * get_singleton()
Returns the display object if a display object exists.
virtual const display_context & get_disp_context() const =0
virtual const unit_map & units() const override
virtual const gamemap & map() const override
bool is_village(const map_location &loc) const
This class stores all the data for a single 'side' (in game nomenclature).
bool is_enemy(int n) const
bool match(const map_location &loc) const
Helper similar to std::unique_lock for detecting when calculations such as abilities have entered inf...
int get_composite_value() const
std::vector< individual_effect > effect_list_
void append(const unit_ability_list &other)
Appends the abilities from other to this, ignores other.loc()
std::pair< int, map_location > lowest(const std::string &key, int def=0) const
const map_location & loc() const
void emplace_back(T &&... args)
std::pair< int, map_location > highest(const std::string &key, int def=0) const
void append_if(const unit_ability_list &other, const Predicate &predicate)
Appends any abilities from other for which the given condition returns true to this,...
std::pair< int, map_location > get_extremum(const std::string &key, int def, const TComp &comp) const
bool matches(const unit &u, const map_location &loc) const
Determine if *this matches filter at a specified location.
unit_filter & set_use_flat_tod(bool value)
Container associating units to locations.
unit_ptr find_unit_ptr(const T &val)
unit_iterator find(std::size_t id)
A single unit type that the player may recruit.
This class represents a single unit of a specific type.
A variable-expanding proxy for the config class.
void swap(config &lhs, config &rhs)
Implement non-member swap function for std::swap (calls config::swap).
std::string deprecated_message(const std::string &elem_name, DEP_LEVEL level, const version_info &version, const std::string &detail)
map_display and display: classes which take care of displaying the map and game-data on the screen.
Interfaces for manipulating version numbers of engine, add-ons, etc.
static std::string _(const char *str)
bool 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.
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 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.
unit_race::GENDER gender_
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.
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.
const std::string & id() const
Gets this unit's id.
int side() const
The side this unit belongs to.
const t_string & name() const
Gets this unit's translatable display name.
@ STATE_POISONED
The unit is slowed - it moves slower and does less damage.
int resistance_value(unit_ability_list resistance_list, const std::string &damage_name) const
For the provided list of resistance abilities, determine the damage resistance based on which are act...
std::vector< std::string > halo_or_icon_abilities(const std::string &image_type) const
New lexcical_cast header.
void get_adjacent_tiles(const map_location &a, map_location *res)
Function which, given a location, will place all adjacent locations in res.
Standard logging facilities (interface).
void set(CURSOR_TYPE type)
Use the default parameter to reset cursors.
const color_t inactive_details_color
const color_t BUTTON_COLOR
static bool is_active(const widget *wgt)
std::stringstream & log_to_chat()
Use this to show WML errors in the ingame chat.
std::string span_color(const color_t &color, Args &&... data)
filter_context * filter_con
bool filter_base_matches(const config &cfg, int def)
std::size_t index(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.
Encapsulates the map of the game.
static std::vector< direction > parse_directions(const std::string &str)
Parse_directions takes a comma-separated list, and filters out any invalid directions.
direction
Valid directions which can be moved in our hexagonal world.
direction get_relative_dir(const map_location &loc, map_location::RELATIVE_DIR_MODE mode) const
static const map_location & null_location()
void set(value_modifier t, int val, const config *abil, const map_location &l)
Data typedef for unit_ability_list.
map_location student_loc
Used by the formula in the ability.
const config * ability_cfg
The contents of the ability tag, never nullptr.
map_location teacher_loc
The location of the teacher, that is the unit who owns the ability tags (different from student becau...
pointer get_shared_ptr() const
This is exactly the same as operator-> but it's slightly more readable, and can replace &*iter syntax...
static map_location::direction s