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();
192 for(std::size_t j = 0; j < adjacent.size(); ++j) {
193 bool adj_or_dist = distance != 1 ?
distance_between(adjacent[j],
loc) == (distance - 1) : adjacent[j] ==
loc;
216 const unit_map& units = get_unit_map();
222 for(
const unit& u : units) {
223 if(!u.has_ability_distant() || u.incapacitated() || &u ==
this || !u.affect_distant(tag_name)) {
228 if(distance > *u.affect_distant(tag_name)) {
231 int dir = find_direction(
loc, from_loc, distance);
232 for(
const config&
i : u.abilities_.child_range(tag_name)) {
258 const unit_map& units = get_unit_map();
264 for(
const unit& u : units) {
265 if(!u.has_ability_distant() || u.incapacitated() || &u ==
this || !u.affect_distant(tag_name)) {
270 if(distance > *u.affect_distant(tag_name)) {
273 int dir = find_direction(
loc, from_loc, distance);
274 for(
const config&
i : u.abilities_.child_range(tag_name)) {
296 std::vector<std::string> res;
299 std::string
id = cfg[
"id"];
301 res.push_back(std::move(
id));
314 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)
324 ab[
"description"].t_str() );
331 "female_name_inactive",
"name_inactive");
340 ab.
get_or(
"description_inactive",
"description").
t_str() );
351 std::vector<std::tuple<std::string, t_string,t_string,t_string>> res;
355 add_ability_tooltip(cfg,
gender_, res,
true);
363 std::vector<std::tuple<std::string, t_string,t_string,t_string>> res;
369 if (add_ability_tooltip(cfg,
gender_, res, active))
371 active_list.push_back(active);
388 static std::vector<std::tuple<std::string, std::string>> already_shown;
390 auto identifier = std::tuple<std::string, std::string>{
unit.
id(),
filter.debug()};
395 std::string_view filter_text_view = std::get<1>(identifier);
397 ERR_NG <<
"Looped recursion error for unit '" <<
unit.
id()
398 <<
"' while checking ability '" << filter_text_view <<
"'";
402 if(already_shown.size() > 100) {
403 already_shown.clear();
405 already_shown.push_back(std::move(identifier));
420 : parent(u.shared_from_this())
430 unit::recursion_guard::operator bool()
const {
436 assert(
this != &other);
445 assert(!parent->open_queries_.empty());
446 parent->open_queries_.pop_back();
454 show_recursion_warning(*
this, cfg);
462 bool illuminates = ability ==
"illuminates";
470 const unit_map& units = get_unit_map();
473 std::size_t radius =
i[
"radius"].to_int(1);
474 std::size_t count = 0;
476 ufilt.set_use_flat_tod(illuminates);
477 for(
const unit& u : units) {
480 if(distance > radius || !ufilt(u, *
this)) {
484 for(
unsigned j = 0; j < adjacent.size(); ++j) {
485 bool adj_or_dist = distance != 1 ?
distance_between(adjacent[j], from_loc) == (distance - 1) : adjacent[j] == from_loc;
491 assert(dir >= 0 && dir <= 5);
493 if(
i.has_attribute(
"adjacent")) {
498 if(
i.has_attribute(
"is_enemy")) {
507 if(
i[
"count"].empty() && count == 0) {
517 std::size_t count = 0;
519 adj_filter.flatten(illuminates);
524 if(!adj_filter.match(adjacent[
static_cast<int>(
index)])) {
544 bool illuminates = ability ==
"illuminates";
546 assert(dir >=0 && dir <= 5);
548 std::size_t radius = 1;
552 if(
i[
"radius"] !=
"all_map") {
553 radius =
i[
"radius"].to_int(1);
561 if (
i.has_attribute(
"adjacent")) {
563 if (
std::find(dirs.begin(), dirs.end(), direction) == dirs.end()) {
567 auto filter =
i.optional_child(
"filter");
579 bool affect_self = cfg[
"affect_self"].to_bool(
true);
580 if (!
filter || !affect_self)
return affect_self;
586 const std::string filter_tag_name = is_opp ?
"filter_second_weapon" :
"filter_weapon";
595 filter_lock = weapon->update_variables_recursion(cfg);
597 show_recursion_warning(*
this, cfg);
600 return weapon->matches_filter(
filter);
612 auto ret =
std::find(image_list.begin(), image_list.end(), cfg[attribute_name].str());
613 if(ret == image_list.end()){
614 image_list.push_back(cfg[attribute_name].str());
620 std::vector<std::string> image_list;
628 if(!cfg[image_type +
"_image_self"].str().empty() &&
is_active){
633 const unit_map& units = get_unit_map();
635 for(
const unit& u : units) {
636 if(!u.has_ability_distant_image() || u.incapacitated() || &u ==
this) {
641 if(distance > *u.has_ability_distant_image()) {
644 int dir = find_direction(
loc_, from_loc, distance);
645 for(
const auto [key, cfg] : u.abilities_.all_children_view()) {
653 if(image_list.size() >= 2){
654 std::sort(image_list.begin(), image_list.end());
662 callable.
add(
"attacker",
wfl::variant(std::make_shared<wfl::unit_callable>(*att)));
665 callable.
add(
"defender",
wfl::variant(std::make_shared<wfl::unit_callable>(*def)));
672 template<
typename T,
typename TFuncFormula>
673 class get_ability_value_visitor
674 #ifdef USING_BOOST_VARIANT
675 :
public boost::static_visitor<T>
680 get_ability_value_visitor(T def,
const TFuncFormula& formula_handler) : def_(def), formula_handler_(formula_handler) {}
682 T operator()(
const utils::monostate&)
const {
return def_; }
683 T operator()(
bool)
const {
return def_; }
684 T operator()(
int i)
const {
return static_cast<T
>(
i); }
685 T operator()(
unsigned long long u)
const {
return static_cast<T
>(u); }
686 T operator()(
double d)
const {
return static_cast<T
>(
d); }
687 T operator()(
const t_string&)
const {
return def_; }
688 T operator()(
const std::string&
s)
const
690 if(
s.size() >= 2 &&
s[0] ==
'(') {
691 return formula_handler_(
s);
693 return lexical_cast_default<T>(
s, def_);
698 const TFuncFormula& formula_handler_;
701 template<
typename T,
typename TFuncFormula>
704 return v.
apply_visitor(get_ability_value_visitor(def, [&](
const std::string&
s) {
707 const unit_map& units = get_unit_map();
711 if(u_itor == units.
end()) {
716 att->add_formula_context(callable);
719 callable.add(
"student",
wfl::variant(std::make_shared<wfl::unit_callable>(*uptr)));
722 callable.add(
"other",
wfl::variant(std::make_shared<wfl::unit_callable>(*uptr)));
726 lg::log_to_chat() <<
"Formula error in ability or weapon special: " <<
e.type <<
" at " <<
e.filename <<
':' <<
e.line <<
")\n";
727 ERR_WML <<
"Formula error in ability or weapon special: " <<
e.type <<
" at " <<
e.filename <<
':' <<
e.line <<
")";
734 template<
typename TComp>
737 if ( cfgs_.empty() ) {
743 bool only_cumulative =
true;
753 if ((*
p.ability_cfg)[
"cumulative"].to_bool()) {
755 if (value < 0) value = -value;
756 if (only_cumulative && !comp(value, abs_max)) {
758 best_loc =
p.teacher_loc;
760 }
else if (only_cumulative || comp(flat, value)) {
761 only_cumulative =
false;
763 best_loc =
p.teacher_loc;
766 return std::pair(flat + stack, best_loc);
769 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;
770 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;
809 if(simple_check && specials().has_child(special)) {
812 for(
const config &
i : specials().child_range(special)) {
813 if(special_active(
i, AFFECT_SELF, special)) {
819 if ( simple_check || !other_attack_ ) {
823 for(
const config &
i : other_attack_->specials().child_range(special)) {
824 if(other_attack_->special_active(
i, AFFECT_OTHER, special)) {
841 for(
const config&
i : specials_.child_range(special)) {
842 if(special_active(
i, AFFECT_SELF, special)) {
851 for(
const config&
i : other_attack_->specials_.child_range(special)) {
852 if(other_attack_->special_active(
i, AFFECT_OTHER, special)) {
869 boost::dynamic_bitset<>* active_list)
const
872 std::vector<std::pair<t_string, t_string>> res;
874 active_list->clear();
877 for(
const auto [key, cfg] : specials_.all_children_view()) {
878 if(!active_list || special_active(cfg, AFFECT_EITHER, key)) {
879 std::string
name = cfg[
"name"];
880 std::string desc = cfg[
"description"];
886 const auto iter = key ==
"plague"
899 res.emplace_back(
name, desc);
902 active_list->push_back(
true);
907 res.emplace_back(
name, cfg.
get_or(
"description_inactive",
"description").
t_str() );
908 active_list->push_back(
false);
916 boost::dynamic_bitset<>* active_list)
const
918 std::vector<std::pair<t_string, t_string>> res;
920 active_list->clear();
922 std::set<std::string> checking_name;
926 for(
const auto [key, cfg] : self_->abilities().all_children_view()) {
927 if(!active_list || check_self_abilities_impl(shared_from_this(), other_attack_, cfg, self_, self_loc_, AFFECT_SELF, key,
false)) {
928 const std::string
name = cfg[
"name_affected"];
929 const std::string desc = cfg[
"description_affected"];
934 res.emplace_back(
name, desc);
937 active_list->push_back(
true);
941 for(
const unit& u : get_unit_map()) {
942 if(!u.has_ability_distant() || u.incapacitated() || &u == self_.get()) {
947 if(distance > *u.has_ability_distant()) {
950 int dir = find_direction(self_loc_, from_loc, distance);
951 for(
const auto [key, cfg] : u.abilities().all_children_view()) {
952 if(!active_list || check_adj_abilities_impl(shared_from_this(), other_attack_, cfg, self_, u, distance, dir, self_loc_, from_loc, AFFECT_SELF, key,
false)) {
953 const std::string
name = cfg[
"name_affected"];
954 const std::string desc = cfg[
"description_affected"];
959 res.emplace_back(
name, desc);
962 active_list->push_back(
true);
978 static void add_name(std::string& temp_string,
bool active,
const std::string&
name, std::set<std::string>& checking_name)
982 checking_name.insert(
name);
983 if (!temp_string.empty()) temp_string +=
", ";
1000 for(
const auto [key, cfg] : specials_.all_children_view())
1002 const bool active = special_active(cfg, AFFECT_EITHER, key);
1004 const std::string&
name =
1007 : cfg.
get_or(
"name_inactive",
"name").
str();
1020 std::string temp_string;
1021 std::set<std::string> checking_name;
1022 weapon_specials_impl_self(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name);
1023 weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name, {},
"affect_allies");
1024 if(!temp_string.empty() && !res.empty()) {
1025 temp_string =
", \n" + temp_string;
1027 }
else if (!temp_string.empty()){
1033 static void add_name_list(std::string& temp_string, std::string& weapon_abilities, std::set<std::string>& checking_name,
const std::string& from_str)
1035 if(!temp_string.empty()){
1036 temp_string = from_str.c_str() + temp_string;
1037 weapon_abilities += (!weapon_abilities.empty() && !temp_string.empty()) ?
"\n" :
"";
1038 weapon_abilities += temp_string;
1039 temp_string.clear();
1040 checking_name.clear();
1047 std::string temp_string, weapon_abilities;
1048 std::set<std::string> checking_name;
1049 for(
const auto [key, cfg] : specials_.all_children_view()) {
1051 const bool active = special_active(cfg, AFFECT_SELF, key);
1052 add_name(temp_string, active, cfg.
get_or(
"name_affected",
"name").
str(), checking_name);
1055 add_name_list(temp_string, weapon_abilities, checking_name,
"");
1057 weapon_specials_impl_self(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name,
checking_tags,
true);
1058 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Owned: "));
1060 weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name,
checking_tags,
"affect_allies",
true);
1062 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Taught: "));
1064 weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name,
checking_tags,
"affect_enemies",
true);
1066 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Taught: (by an enemy): "));
1070 for(
const auto [key, cfg] : other_attack_->specials_.all_children_view()) {
1072 const bool active = other_attack_->special_active(cfg, AFFECT_OTHER, key);
1073 add_name(temp_string, active, cfg.
get_or(
"name_affected",
"name").
str(), checking_name);
1077 weapon_specials_impl_self(temp_string, other_, other_attack_, shared_from_this(), other_loc_, AFFECT_OTHER, checking_name,
checking_tags);
1078 weapon_specials_impl_adj(temp_string, other_, other_attack_, shared_from_this(), other_loc_, AFFECT_OTHER, checking_name,
checking_tags);
1079 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Used by opponent: "));
1081 return weapon_abilities;
1085 std::string& temp_string,
1091 std::set<std::string>& checking_name,
1096 for(
const auto [key, cfg] : self->abilities().all_children_view()){
1098 const bool active = tag_checked && check_self_abilities_impl(self_attack, other_attack, cfg,
self, self_loc, whom, key, leader_bool);
1099 add_name(temp_string, active, cfg.
get_or(
"name_affected",
"name").
str(), checking_name);
1105 std::string& temp_string,
1111 std::set<std::string>& checking_name,
1113 const std::string& affect_adjacents,
1116 const unit_map& units = get_unit_map();
1118 for(
const unit& u : units) {
1119 if(!u.has_ability_distant() || u.incapacitated() || &u ==
self.get()) {
1124 if(distance > *u.has_ability_distant()) {
1127 int dir = find_direction(self_loc, from_loc, distance);
1128 for(
const auto [key, cfg] : u.abilities().all_children_view()) {
1130 bool default_bool = affect_adjacents ==
"affect_allies" ? true :
false;
1131 bool affect_allies = !affect_adjacents.empty() ? cfg[affect_adjacents].to_bool(default_bool) :
true;
1132 const bool active = tag_checked && check_adj_abilities_impl(self_attack, other_attack, cfg,
self, u, distance, dir, self_loc, from_loc, whom, key, leader_bool) && affect_allies;
1133 add_name(temp_string, active, cfg.
get_or(
"name_affected",
"name").
str(), checking_name);
1159 : parent(weapon.shared_from_this())
1161 weapon.
self_ = std::move(
self);
1162 weapon.
other_ = std::move(other);
1179 : parent(weapon.shared_from_this())
1181 weapon.
self_ = std::move(
self);
1199 : parent(weapon.shared_from_this())
1211 : parent(weapon.shared_from_this())
1219 if(was_moved)
return;
1224 parent->is_attacker_ =
false;
1225 parent->other_attack_ =
nullptr;
1226 parent->is_for_listing_ =
false;
1230 : parent(std::move(other.parent))
1232 other.was_moved =
true;
1243 unsigned & max_attacks)
const
1248 if ( attacks_value < 0 ) {
1250 ERR_NG <<
"negative number of strikes after applying weapon specials";
1255 if ( !swarm_specials.
empty() ) {
1256 min_attacks = std::max<int>(0, swarm_specials.
highest(
"swarm_attacks_min").first);
1257 max_attacks = std::max<int>(0, swarm_specials.
highest(
"swarm_attacks_max", attacks_value).first);
1259 min_attacks = max_attacks = attacks_value;
1265 std::map<std::string, unsigned int> type_count;
1266 unsigned int max = 0;
1267 for(
auto&
i : damage_type_list) {
1269 if(
c.has_attribute(
"replacement_type")) {
1270 std::string
type =
c[
"replacement_type"].str();
1271 unsigned int count = ++type_count[
type];
1278 if (type_count.empty())
return type();
1280 std::vector<std::string> type_list;
1281 for(
auto&
i : type_count){
1282 if(
i.second == max){
1283 type_list.push_back(
i.first);
1287 if(type_list.empty())
return type();
1289 return type_list.front();
1294 std::map<std::string, int> type_res;
1295 int max_res = INT_MIN;
1297 for(
auto&
i : damage_type_list) {
1299 if(
c.has_attribute(
"alternative_type")) {
1300 std::string
type =
c[
"alternative_type"].str();
1301 if(type_res.count(
type) == 0){
1302 type_res[
type] = (*other_).resistance_value(resistance_list,
type);
1303 max_res = std::max(max_res, type_res[
type]);
1309 if (type_res.empty())
return {
"", INT_MIN};
1311 std::vector<std::string> type_list;
1312 for(
auto&
i : type_res){
1313 if(
i.second == max_res){
1314 type_list.push_back(
i.first);
1317 if(type_list.empty())
return {
"", INT_MIN};
1319 return {type_list.front(), max_res};
1332 resistance_list = (*other_).get_abilities_weapons(
"resistance",
other_loc_,
other_attack_, shared_from_this());
1334 return (!((*
i.ability_cfg)[
"active_on"].empty() || (!
is_attacker_ && (*
i.ability_cfg)[
"active_on"] ==
"offense") || (
is_attacker_ && (*
i.ability_cfg)[
"active_on"] ==
"defense")));
1338 int res =
other_ ? (*other_).resistance_value(resistance_list,
type()) : 100;
1339 if(damage_type_list.
empty()){
1340 return {
type(), res};
1346 res = replacement_type !=
type() ? (*other_).resistance_value(resistance_list, replacement_type) : res;
1347 replacement_type = alternative_type.second > res ? alternative_type.first : replacement_type;
1348 res = std::max(res, alternative_type.second);
1350 return {replacement_type, res};
1359 std::set<std::string> alternative_damage_types;
1360 if(damage_type_list.
empty()){
1361 return {
type(), alternative_damage_types};
1364 for(
auto&
i : damage_type_list) {
1366 if(
c.has_attribute(
"alternative_type")){
1367 alternative_damage_types.insert(
c[
"alternative_type"].str());
1371 return {replacement_type, alternative_damage_types};
1380 return damage_value;
1392 bool special_affects_opponent(
const config& special,
bool is_attacker)
1395 const std::string& apply_to = special[
"apply_to"];
1396 if ( apply_to.empty() )
1398 if ( apply_to ==
"both" )
1400 if ( apply_to ==
"opponent" )
1402 if ( is_attacker && apply_to ==
"defender" )
1404 if ( !is_attacker && apply_to ==
"attacker" )
1414 bool special_affects_self(
const config& special,
bool is_attacker)
1417 const std::string& apply_to = special[
"apply_to"];
1418 if ( apply_to.empty() )
1420 if ( apply_to ==
"both" )
1422 if ( apply_to ==
"self" )
1424 if ( is_attacker && apply_to ==
"attacker" )
1426 if ( !is_attacker && apply_to ==
"defender")
1441 static std::vector<std::tuple<std::string, std::string>> already_shown;
1443 auto identifier = std::tuple<std::string, std::string>{attack->id(),
filter.debug()};
1448 std::string_view filter_text_view = std::get<1>(identifier);
1450 ERR_NG <<
"Looped recursion error for weapon '" << attack->id()
1451 <<
"' while checking weapon special '" << filter_text_view <<
"'";
1455 if(already_shown.size() > 100) {
1456 already_shown.clear();
1458 already_shown.push_back(std::move(identifier));
1478 const bool for_listing,
1479 const std::string & child_tag,
const std::string& check_if_recursion)
1490 if (!
filter[
"backstab"].
blank() && child_tag ==
"filter_opponent") {
1491 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.");
1494 if(
filter[
"backstab"].to_bool() && child_tag ==
"filter_opponent"){
1495 const std::string& backstab_formula =
"enemy_of(self, flanker) and not flanker.petrified where flanker = unit_at(direction_from(loc, other.facing))";
1497 if(!
filter.has_child(
"filter_opponent")){
1498 filter_child[
"formula"] = backstab_formula;
1501 filter_opponent[
"formula"] = backstab_formula;
1502 filter_child.
add_child(
"and", filter_opponent);
1508 if ( !filter_child )
1524 filter_lock = weapon->update_variables_recursion(
filter);
1526 show_recursion_warning(weapon,
filter);
1531 if (
auto filter_weapon = filter_child->
optional_child(
"filter_weapon") ) {
1532 if ( !weapon || !weapon->matches_filter(*filter_weapon, check_if_recursion) )
1539 return ufilt.matches(*u,
loc);
1541 return ufilt.matches(*u,
loc, *u2);
1559 return special_active(*i.ability_cfg, AFFECT_SELF, ability, true);
1565 return special_active_impl(other_attack_, shared_from_this(), *i.ability_cfg, AFFECT_OTHER, ability, true);
1580 if(!abil_list.
empty() && !overwriters.
empty()){
1596 const std::string& apply_to = special[
"overwrite_specials"];
1597 return (apply_to ==
"one_side" || apply_to ==
"both_sides");
1608 if(overwriters.
empty()){
1613 if(overwriters.
size() >= 2){
1616 auto oi = (*
i.ability_cfg).optional_child(
"overwrite");
1618 if(oi && !oi[
"priority"].empty()){
1619 l = oi[
"priority"].to_double(0);
1621 auto oj = (*j.
ability_cfg).optional_child(
"overwrite");
1623 if(oj && !oj[
"priority"].empty()){
1624 r = oj[
"priority"].to_double(0);
1638 if(overwriters.
empty()){
1642 for(
const auto& j : overwriters) {
1644 bool affect_side = ((*j.ability_cfg)[
"overwrite_specials"] ==
"one_side");
1646 auto overwrite_specials = (*j.ability_cfg).optional_child(
"overwrite");
1647 double priority = overwrite_specials ? overwrite_specials[
"priority"].to_double(0) : 0.00;
1652 bool prior = (priority > 0) ? (has_overwrite_specials && has_overwrite_specials[
"priority"].to_double(0) >= priority) :
true;
1656 bool one_side_overwritable =
true;
1660 if(affect_side && is_overwritable){
1661 if(special_affects_self(*j.ability_cfg,
is_attacker_)){
1662 one_side_overwritable = special_affects_self(cfg,
is_attacker_);
1664 else if(special_affects_opponent(*j.ability_cfg, !
is_attacker_)){
1665 one_side_overwritable = special_affects_opponent(cfg, !
is_attacker_);
1670 bool special_matches =
true;
1671 if(overwrite_specials){
1672 auto overwrite_filter = (*overwrite_specials).optional_child(
"filter_specials");
1673 if(!overwrite_filter){
1674 overwrite_filter = (*overwrite_specials).optional_child(
"experimental_filter_specials");
1675 if(overwrite_filter){
1679 if(overwrite_filter && is_overwritable && one_side_overwritable){
1687 if(is_overwritable && one_side_overwritable && special_matches){
1698 show_recursion_warning(*
this, cfg);
1701 return (ability_active_impl(ability, cfg,
loc) && ability_affects_self(ability, cfg,
loc));
1708 show_recursion_warning(from, cfg);
1711 return (affects_side(cfg, side(), from.
side()) && from.
ability_active_impl(ability, cfg, from_loc) && ability_affects_adjacent(ability, cfg, dist, dir,
loc, from));
1716 return (get_self_ability_bool(special, tag_name,
loc) && ability_affects_weapon(special, weapon,
false) && ability_affects_weapon(special, opp_weapon,
true));
1721 return (get_adj_ability_bool(special, tag_name, dist, dir,
loc, from, from_loc) && ability_affects_weapon(special, weapon,
false) && ability_affects_weapon(special, opp_weapon,
true));
1731 if(tag_name ==
"leadership" && leader_bool){
1732 if(u->get_self_ability_bool_weapon(special, tag_name,
loc, self_attack, other_attack)) {
1736 if(u->checking_tags().count(tag_name) != 0){
1737 if(u->get_self_ability_bool(special, tag_name,
loc) &&
special_active_impl(self_attack, other_attack, special, whom, tag_name,
true)) {
1746 return check_adj_abilities_impl(shared_from_this(),
other_attack_, cfg,
self_, from, dist, dir,
self_loc_, from_loc,
AFFECT_SELF, special,
true);
1749 bool attack_type::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, std::size_t dist,
int dir,
const map_location&
loc,
const map_location& from_loc,
AFFECTS whom,
const std::string& tag_name,
bool leader_bool)
1751 if(tag_name ==
"leadership" && leader_bool) {
1752 if(u->get_adj_ability_bool_weapon(special, tag_name, dist, dir,
loc, from, from_loc, self_attack, other_attack)) {
1756 if(u->checking_tags().count(tag_name) != 0) {
1757 if(u->get_adj_ability_bool(special, tag_name, dist, dir,
loc, from, from_loc) &&
special_active_impl(self_attack, other_attack, special, whom, tag_name,
true)) {
1775 if(
range().empty()){
1782 const unit_map& units = get_unit_map();
1784 for(
const config &
i :
self_->abilities().child_range(special)) {
1790 for(
const unit& u : units) {
1791 if(!u.affect_distant(special) || u.incapacitated() || &u ==
self_.get()) {
1796 if(distance > *u.affect_distant(special)) {
1799 int dir = find_direction(
self_loc_, from_loc, distance);
1800 for(
const config &
i : u.abilities().child_range(special)) {
1809 for(
const config &
i :
other_->abilities().child_range(special)) {
1815 for(
const unit& u : units) {
1816 if(!u.affect_distant(special) || u.incapacitated() || &u ==
other_.get()) {
1821 if(distance > *u.affect_distant(special)) {
1824 int dir = find_direction(
other_loc_, from_loc, distance);
1825 for(
const config &
i : u.abilities().child_range(special)) {
1826 if(
check_adj_abilities_impl(
other_attack_, shared_from_this(),
i,
other_, u, distance, dir,
other_loc_, from_loc,
AFFECT_OTHER, special)) {
1838 bool special_checking(
const std::string& special_id,
const std::string& tag_name,
const std::set<std::string>& filter_special,
const std::set<std::string>& filter_special_id,
const std::set<std::string>& filter_special_type)
1840 if(!filter_special.empty() && filter_special.count(special_id) == 0 && filter_special.count(tag_name) == 0)
1843 if(!filter_special_id.empty() && filter_special_id.count(special_id) == 0)
1846 if(!filter_special_type.empty() && filter_special_type.count(tag_name) == 0)
1855 if(
range().empty()){
1862 for(
const auto [key, cfg] :
specials().all_children_view()) {
1863 if(special_checking(cfg[
"id"].str(), key, filter_special, filter_special_id, filter_special_type)) {
1873 for(
const auto [key, cfg] :
other_attack_->specials().all_children_view()) {
1874 if(special_checking(cfg[
"id"].str(), key, filter_special, filter_special_id, filter_special_type)) {
1886 const unit_map& units = get_unit_map();
1888 for(
const auto [key, cfg] :
self_->abilities().all_children_view()) {
1889 if(special_checking(cfg[
"id"].str(), key, filter_special, filter_special_id, filter_special_type)) {
1895 for(
const unit& u : units) {
1896 if(!u.has_ability_distant() || u.incapacitated() || &u ==
self_.get()) {
1901 if(distance > *u.has_ability_distant()) {
1904 int dir = find_direction(
self_loc_, from_loc, distance);
1906 for(
const auto [key, cfg] : u.abilities().all_children_view()) {
1907 if(special_checking(cfg[
"id"].str(), key, filter_special, filter_special_id, filter_special_type) &&
check_adj_abilities(cfg, key, distance, dir, u, from_loc)) {
1915 for(
const auto [key, cfg] :
other_->abilities().all_children_view()) {
1916 if(special_checking(cfg[
"id"].str(), key, filter_special, filter_special_id, filter_special_type) &&
check_self_abilities_impl(
other_attack_, shared_from_this(), cfg,
other_,
other_loc_,
AFFECT_OTHER, key)){
1921 for(
const unit& u : units) {
1922 if(!u.has_ability_distant() || u.incapacitated() || &u ==
other_.get()) {
1927 if(distance > *u.has_ability_distant()) {
1930 int dir = find_direction(
other_loc_, from_loc, distance);
1932 for(
const auto [key, cfg] : u.abilities().all_children_view()) {
1933 if(special_checking(cfg[
"id"].str(), key, filter_special, filter_special_id, filter_special_type) &&
check_adj_abilities_impl(
other_attack_, shared_from_this(), cfg,
other_, u, distance, dir,
other_loc_, from_loc,
AFFECT_OTHER, key)) {
1944 bool exclude_ability_attributes(
const std::string& tag_name,
const config &
filter)
1947 bool abilities_check = abilities_list::ability_value_tags().count(tag_name) != 0 || abilities_list::ability_no_value_tags().count(tag_name) != 0;
1948 if(
filter.has_attribute(
"active_on") && tag_name !=
"resistance" && abilities_check)
1950 if(
filter.has_attribute(
"apply_to") && tag_name !=
"resistance" && abilities_check)
1953 if(
filter.has_attribute(
"overwrite_specials") && abilities_list::weapon_number_tags().count(tag_name) == 0)
1956 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;
1957 if(
filter.has_attribute(
"value") && no_value_weapon_abilities_check)
1959 if(
filter.has_attribute(
"add") && no_value_weapon_abilities_check)
1961 if(
filter.has_attribute(
"sub") && no_value_weapon_abilities_check)
1963 if(
filter.has_attribute(
"multiply") && no_value_weapon_abilities_check)
1965 if(
filter.has_attribute(
"divide") && no_value_weapon_abilities_check)
1968 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;
1969 if(
filter.has_attribute(
"replacement_type") && tag_name !=
"damage_type" && all_engine)
1971 if(
filter.has_attribute(
"alternative_type") && tag_name !=
"damage_type" && all_engine)
1973 if(
filter.has_attribute(
"type") && tag_name !=
"plague" && all_engine)
1979 bool matches_ability_filter(
const config & cfg,
const std::string& tag_name,
const config &
filter)
1984 if(!exclude_ability_attributes(tag_name,
filter))
1990 if ( !filter_type.empty() &&
std::find(filter_type.begin(), filter_type.end(), tag_name) == filter_type.end() )
1998 if(!
filter[
"affect_adjacent"].empty()){
1999 bool adjacent = cfg.
has_child(
"affect_adjacent");
2000 if(
filter[
"affect_adjacent"].to_bool() != adjacent){
2032 if(!
filter[
"value"].empty()){
2033 if(tag_name ==
"drains"){
2037 }
else if(tag_name ==
"berserk"){
2041 }
else if(tag_name ==
"heal_on_hit" || tag_name ==
"heals" || tag_name ==
"regenerate" || tag_name ==
"leadership"){
2067 if(tag_name ==
"resistance"){
2096 auto fwml =
filter.optional_child(
"filter_wml");
2107 static bool common_matches_filter(
const config & cfg,
const std::string& tag_name,
const config &
filter)
2110 bool matches = matches_ability_filter(cfg, tag_name,
filter);
2113 for(
const auto [key, condition_cfg] :
filter.all_children_view() )
2117 matches = matches && common_matches_filter(cfg, tag_name, condition_cfg);
2120 else if ( key ==
"or" )
2121 matches = matches || common_matches_filter(cfg, tag_name, condition_cfg);
2124 else if ( key ==
"not" )
2125 matches = matches && !common_matches_filter(cfg, tag_name, condition_cfg);
2134 return common_matches_filter(cfg, tag_name,
filter);
2139 return common_matches_filter(cfg, tag_name,
filter);
2144 if(
range().empty()){
2148 bool check_if_active =
filter[
"active"].to_bool();
2149 for(
const auto [key, cfg] :
specials().all_children_view()) {
2151 if(!check_if_active) {
2161 for(
const auto [key, cfg] :
other_attack_->specials().all_children_view()) {
2169 if(!check_if_active){
2173 const unit_map& units = get_unit_map();
2174 bool check_adjacent =
filter[
"affect_adjacent"].to_bool(
true);
2176 for(
const auto [key, cfg] :
self_->abilities().all_children_view()) {
2177 if(
self_->ability_matches_filter(cfg, key,
filter)) {
2183 if(check_adjacent) {
2184 for(
const unit& u : units) {
2185 if(!u.has_ability_distant() || u.incapacitated() || &u ==
self_.get()) {
2190 if(distance > *u.has_ability_distant()) {
2193 int dir = find_direction(
self_loc_, from_loc, distance);
2195 for(
const auto [key, cfg] : u.abilities().all_children_view()) {
2205 for(
const auto [key, cfg] :
other_->abilities().all_children_view()) {
2211 if(check_adjacent) {
2212 for(
const unit& u : units) {
2213 if(!u.has_ability_distant() || u.incapacitated() || &u ==
other_.get()) {
2218 if(distance > *u.has_ability_distant()) {
2221 int dir = find_direction(
other_loc_, from_loc, distance);
2223 for(
const auto [key, cfg] : u.abilities().all_children_view()) {
2224 if(u.ability_matches_filter(cfg, key,
filter) &&
check_adj_abilities_impl(
other_attack_, shared_from_this(), cfg,
other_, u, distance, dir,
other_loc_, from_loc,
AFFECT_OTHER, key)) {
2235 bool in_abilities_tag)
const
2255 const std::string& tag_name,
2256 bool in_abilities_tag)
2258 assert(self_attack || other_attack);
2259 bool is_attacker = self_attack ? self_attack->is_attacker_ : !other_attack->is_attacker_;
2260 bool is_for_listing = self_attack ? self_attack->is_for_listing_ : other_attack->is_for_listing_;
2266 if ( !special_affects_self(special, is_attacker) )
2270 if ( !special_affects_opponent(special, is_attacker) )
2275 const std::string & active_on = special[
"active_on"];
2276 if ( !active_on.empty() ) {
2277 if ( is_attacker && active_on !=
"offense" )
2279 if ( !is_attacker && active_on !=
"defense" )
2284 const unit_map& units = get_unit_map();
2286 unit_const_ptr self = self_attack ? self_attack->self_ : other_attack->other_;
2287 unit_const_ptr other = self_attack ? self_attack->other_ : other_attack->self_;
2288 map_location self_loc = self_attack ? self_attack->self_loc_ : other_attack->other_loc_;
2289 map_location other_loc = self_attack ? self_attack->other_loc_ : other_attack->self_loc_;
2291 if(
self ==
nullptr) {
2297 if(other ==
nullptr) {
2306 temporary_facing other_facing(other, other_loc.
get_relative_dir(self_loc));
2310 bool whom_is_self = ((whom ==
AFFECT_SELF) || ((whom ==
AFFECT_EITHER) && special_affects_self(special, is_attacker)));
2312 map_location their_loc = whom_is_self ? other_loc : self_loc;
2314 if (tag_name ==
"drains" && them && them->get_state(
"undrainable")) {
2317 if (tag_name ==
"plague" && them &&
2318 (them->get_state(
"unplagueable") ||
2322 if (tag_name ==
"poison" && them &&
2326 if (tag_name ==
"slow" && them &&
2330 if (tag_name ==
"petrifies" && them &&
2331 them->get_state(
"unpetrifiable")) {
2339 const map_location & att_loc = is_attacker ? self_loc : other_loc;
2340 const map_location & def_loc = is_attacker ? other_loc : self_loc;
2341 const const_attack_ptr& att_weapon = is_attacker ? self_attack : other_attack;
2342 const const_attack_ptr& def_weapon = is_attacker ? other_attack : self_attack;
2346 if (tag_name ==
"firststrike") {
2347 bool whom_is_defender = whom_is_self ? !is_attacker : is_attacker;
2348 if (whom_is_defender && att_weapon && att_weapon->has_special_or_ability(
"firststrike"))
2357 bool applied_both = special[
"apply_to"] ==
"both";
2358 const std::string& filter_self = in_abilities_tag ?
"filter_student" :
"filter_self";
2359 std::string self_check_if_recursion = (applied_both || whom_is_self) ? tag_name :
"";
2360 if (!special_unit_matches(
self, other, self_loc, self_attack, special, is_for_listing, filter_self, self_check_if_recursion))
2362 std::string opp_check_if_recursion = (applied_both || !whom_is_self) ? tag_name :
"";
2363 if (!special_unit_matches(other,
self, other_loc, other_attack, special, is_for_listing,
"filter_opponent", opp_check_if_recursion))
2367 bool applied_to_attacker = applied_both || (whom_is_self && is_attacker) || (!whom_is_self && !is_attacker);
2368 std::string att_check_if_recursion = applied_to_attacker ? tag_name :
"";
2369 if (!special_unit_matches(att, def, att_loc, att_weapon, special, is_for_listing,
"filter_attacker", att_check_if_recursion))
2371 bool applied_to_defender = applied_both || (whom_is_self && !is_attacker) || (!whom_is_self && is_attacker);
2372 std::string def_check_if_recursion= applied_to_defender ? tag_name :
"";
2373 if (!special_unit_matches(def, att, def_loc, def_weapon, special, is_for_listing,
"filter_defender", def_check_if_recursion))
2379 const std::string& filter_adjacent = in_abilities_tag ?
"filter_adjacent_student" :
"filter_adjacent";
2380 const std::string& filter_adjacent_location = in_abilities_tag ?
"filter_adjacent_student_location" :
"filter_adjacent_location";
2386 std::size_t radius =
i[
"radius"].to_int(1);
2387 std::size_t count = 0;
2389 for(
const unit& u : units) {
2392 if(distance > radius || !ufilt(u, *
self)) {
2396 for(
unsigned j = 0; j < adjacent.size(); ++j) {
2397 bool adj_or_dist = distance != 1 ?
distance_between(adjacent[j], from_loc) == (distance - 1) : adjacent[j] == from_loc;
2403 assert(dir >=0 && dir <= 5);
2405 if(
i.has_attribute(
"adjacent")) {
2410 if(
i.has_attribute(
"is_enemy")) {
2419 if(
i[
"count"].empty() && count == 0) {
2430 std::size_t count = 0;
2435 if(!adj_filter.
match(adjacent[
static_cast<int>(
index)])) {
2465 if (
auto apply_filter = cfg.
optional_child(
"filter_base_value")) {
2472 return (cond_eq.
empty() || def == cond_eq.
to_int()) &&
2488 std::map<std::string,individual_effect> values_add;
2489 std::map<std::string,individual_effect> values_sub;
2490 std::map<std::string,individual_effect> values_mul;
2491 std::map<std::string,individual_effect> values_div;
2495 utils::optional<int> max_value = utils::nullopt;
2496 utils::optional<int> min_value = utils::nullopt;
2499 const config& cfg = *ability.ability_cfg;
2500 const std::string& effect_id = cfg[cfg[
"id"].
empty() ?
"name" :
"id"];
2508 callable.add(
"base_value", wfl::variant(def));
2509 return std::round(formula.evaluate(callable).as_int());
2512 int value_cum = cfg[
"cumulative"].to_bool() ? std::max(def, value) : value;
2515 set_effect_min.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2516 set_effect_max.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2519 if(value_cum > set_effect_max.
value) {
2520 set_effect_max.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2522 if(value_cum < set_effect_min.
value) {
2523 set_effect_min.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2531 max_value = max_value ? std::min(*max_value, cfg[
"max_value"].to_int()) : cfg[
"max_value"].to_int();
2534 min_value = min_value ? std::max(*min_value, cfg[
"min_value"].to_int()) : cfg[
"min_value"].to_int();
2540 callable.add(
"base_value", wfl::variant(def));
2541 return std::round(formula.evaluate(callable).as_int());
2544 if(add_effect == values_add.end() || add > add_effect->second.value) {
2545 values_add[effect_id].set(
ADD, add, ability.ability_cfg, ability.teacher_loc);
2550 callable.add(
"base_value", wfl::variant(def));
2551 return std::round(formula.evaluate(callable).as_int());
2554 if(sub_effect == values_sub.end() || sub < sub_effect->second.value) {
2555 values_sub[effect_id].set(
ADD, sub, ability.ability_cfg, ability.teacher_loc);
2560 callable.add(
"base_value", wfl::variant(def));
2561 return formula.evaluate(callable).as_decimal() / 1000.0 ;
2564 if(mul_effect == values_mul.end() || multiply > mul_effect->second.value) {
2565 values_mul[effect_id].set(
MUL, multiply, ability.ability_cfg, ability.teacher_loc);
2570 callable.add(
"base_value", wfl::variant(def));
2571 return formula.evaluate(callable).as_decimal() / 1000.0 ;
2575 ERR_NG <<
"division by zero with divide= in ability/weapon special " << effect_id;
2579 if(div_effect == values_div.end() || divide > div_effect->second.value) {
2580 values_div[effect_id].set(
DIV, divide, ability.ability_cfg, ability.teacher_loc);
2587 value_set = std::max(set_effect_max.
value, 0) + std::min(set_effect_min.
value, 0);
2588 if(set_effect_max.
value > def) {
2591 if(set_effect_min.
value < def) {
2604 double multiplier = 1.0;
2605 double divisor = 1.0;
2607 for(
const auto& val : values_mul) {
2608 multiplier *= val.second.value/100.0;
2612 for(
const auto& val : values_div) {
2613 divisor *= val.second.value/100.0;
2618 for(
const auto& val : values_add) {
2619 addition += val.second.value;
2625 int substraction = 0;
2626 for(
const auto& val : values_sub) {
2627 substraction += val.second.value;
2633 if(max_value && min_value && *min_value < *max_value) {
2635 }
else if(max_value && !min_value) {
2637 }
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")
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...
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
check if special matche
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.
bool has_special_or_ability(const std::string &special) const
used for abilities used like weapon and true specials
unit_ability_list get_weapon_ability(const std::string &ability) const
Returns list for weapon like abilities for each ability type.
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.
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.
bool has_filter_special_or_ability(const config &filter, bool simple_check=false) const
check if special matche
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...
std::vector< std::pair< t_string, t_string > > abilities_special_tooltips(boost::dynamic_bitset<> *active_list=nullptr) const
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, std::size_t dist, int dir, const map_location &loc, const map_location &from_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 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.
bool check_adj_abilities(const config &cfg, const std::string &special, std::size_t dist, int dir, const unit &from, const map_location &from_loc) const
check_adj_abilities : return an boolean value for checking of activities of abilities used like weapo...
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(const std::string &special, bool simple_check=false) const
Returns whether or not *this has a special with a tag or id equal to special.
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.
void insert(config_key_type key, T &&value)
Inserts an attribute into the config.
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_affects_adjacent(const std::string &ability, const config &cfg, std::size_t dist, int dir, const map_location &loc, const unit &from) const
Check if an ability affects distant units.
bool get_adj_ability_bool(const config &cfg, const std::string &ability, std::size_t dist, int dir, const map_location &loc, const unit &from, const map_location &from_loc) const
Checks whether this unit is affected by a given ability, and that that ability is active.
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 get_adj_ability_bool_weapon(const config &special, const std::string &tag_name, std::size_t dist, int dir, const map_location &loc, const unit &from, const map_location &from_loc, const const_attack_ptr &weapon, const const_attack_ptr &opp_weapon) const
Checks whether this unit is affected by a given ability of leadership type.
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.
recursion_guard & operator=(recursion_guard &&) noexcept
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
const std::set< std::string > & checking_tags() const
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.
std::size_t distance_between(const map_location &a, const map_location &b)
Function which gives the number of hexes between two tiles (i.e.
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)
std::string interpolate_variables_into_string(const std::string &str, const string_map *const symbols)
Function which will interpolate variables, starting with '$' in the string 'str' with the equivalent ...
std::set< std::string > split_set(std::string_view s, char sep, const int flags)
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.
std::map< std::string, t_string > string_map
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