25 #include "formula/callable_objects.hpp"
53 #define ERR_NG LOG_STREAM(err, log_engine)
56 #define ERR_WML LOG_STREAM(err, log_wml)
59 class temporary_facing
69 u_->set_facing(new_dir);
75 u_->set_facing(save_dir_);
152 const team& get_team(std::size_t side)
172 bool affects_side(
const config& cfg, std::size_t side, std::size_t other_side)
174 const team& side_team = get_team(side);
176 if(side == other_side)
177 return cfg[
"affect_allies"].to_bool(
true);
179 return cfg[
"affect_enemies"].to_bool();
181 return cfg[
"affect_allies"].to_bool();
195 const unit_map& units = get_unit_map();
198 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
200 if (it == units.
end() || it->incapacitated())
209 for (
const config &j : it->abilities_.child_range(tag_name)) {
232 const unit_map& units = get_unit_map();
235 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
237 if (it == units.
end() || it->incapacitated())
246 for(
const config& j : it->abilities_.child_range(tag_name)) {
268 std::vector<std::string> res;
271 std::string
id = cfg[
"id"];
273 res.push_back(std::move(
id));
286 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)
296 ab[
"description"].t_str() );
303 "female_name_inactive",
"name_inactive");
312 ab.
get_or(
"description_inactive",
"description").
t_str() );
323 std::vector<std::tuple<std::string, t_string,t_string,t_string>> res;
327 add_ability_tooltip(cfg,
gender_, res,
true);
335 std::vector<std::tuple<std::string, t_string,t_string,t_string>> res;
341 if (add_ability_tooltip(cfg,
gender_, res, active))
343 active_list.push_back(active);
360 static std::vector<std::tuple<std::string, std::string>> already_shown;
362 auto identifier = std::tuple<std::string, std::string>{
unit.
id(),
filter.debug()};
367 std::string_view filter_text_view = std::get<1>(identifier);
369 ERR_NG <<
"Looped recursion error for unit '" <<
unit.
id()
370 <<
"' while checking ability '" << filter_text_view <<
"'";
374 if(already_shown.size() > 100) {
375 already_shown.clear();
377 already_shown.push_back(std::move(identifier));
392 : parent(u.shared_from_this())
402 unit::recursion_guard::operator bool()
const {
408 assert(
this != &other);
417 assert(!parent->open_queries_.empty());
418 parent->open_queries_.pop_back();
426 show_recursion_warning(*
this, cfg);
434 bool illuminates = ability ==
"illuminates";
442 const unit_map& units = get_unit_map();
446 std::size_t count = 0;
448 ufilt.set_use_flat_tod(illuminates);
455 if (!ufilt(*
unit, *
this))
457 if((*this).id() == (*unit).id())
459 if (
i.has_attribute(
"is_enemy")) {
476 std::size_t count = 0;
478 adj_filter.flatten(illuminates);
483 if(!adj_filter.match(adjacent[
static_cast<int>(
index)])) {
499 bool illuminates = ability ==
"illuminates";
501 assert(dir >=0 && dir <= 5);
506 if (
i.has_attribute(
"adjacent")) {
508 if (
std::find(dirs.begin(), dirs.end(), direction) == dirs.end()) {
512 if((*this).id() == from.
id()){
515 auto filter =
i.optional_child(
"filter");
527 bool affect_self = cfg[
"affect_self"].to_bool(
true);
528 if (!
filter || !affect_self)
return affect_self;
534 const std::string filter_tag_name = is_opp ?
"filter_second_weapon" :
"filter_weapon";
543 filter_lock = weapon->update_variables_recursion(cfg);
545 show_recursion_warning(*
this, cfg);
548 return weapon->matches_filter(
filter);
560 auto ret =
std::find(image_list.begin(), image_list.end(), cfg[attribute_name].str());
561 if(ret == image_list.end()){
562 image_list.push_back(cfg[attribute_name].str());
568 std::vector<std::string> image_list;
576 if(!cfg[image_type +
"_image_self"].str().empty() &&
is_active){
581 const unit_map& units = get_unit_map();
586 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
588 if (it == units.
end() || it->incapacitated())
592 for(
const auto [key, cfg] : it->abilities_.all_children_view()) {
593 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))
600 if(image_list.size() >= 2){
601 std::sort(image_list.begin(), image_list.end());
609 callable.
add(
"attacker",
wfl::variant(std::make_shared<wfl::unit_callable>(*att)));
612 callable.
add(
"defender",
wfl::variant(std::make_shared<wfl::unit_callable>(*def)));
619 template<
typename T,
typename TFuncFormula>
620 class get_ability_value_visitor
621 #ifdef USING_BOOST_VARIANT
622 :
public boost::static_visitor<T>
627 get_ability_value_visitor(T def,
const TFuncFormula& formula_handler) : def_(def), formula_handler_(formula_handler) {}
629 T operator()(
const utils::monostate&)
const {
return def_; }
630 T operator()(
bool)
const {
return def_; }
631 T operator()(
int i)
const {
return static_cast<T
>(
i); }
632 T operator()(
unsigned long long u)
const {
return static_cast<T
>(u); }
633 T operator()(
double d)
const {
return static_cast<T
>(
d); }
634 T operator()(
const t_string&)
const {
return def_; }
635 T operator()(
const std::string&
s)
const
637 if(
s.size() >= 2 &&
s[0] ==
'(') {
638 return formula_handler_(
s);
640 return lexical_cast_default<T>(
s, def_);
645 const TFuncFormula& formula_handler_;
648 template<
typename T,
typename TFuncFormula>
651 return v.
apply_visitor(get_ability_value_visitor(def, [&](
const std::string&
s) {
654 const unit_map& units = get_unit_map();
658 if(u_itor == units.
end()) {
663 att->add_formula_context(callable);
666 callable.add(
"student",
wfl::variant(std::make_shared<wfl::unit_callable>(*uptr)));
669 callable.add(
"other",
wfl::variant(std::make_shared<wfl::unit_callable>(*uptr)));
673 lg::log_to_chat() <<
"Formula error in ability or weapon special: " <<
e.type <<
" at " <<
e.filename <<
':' <<
e.line <<
")\n";
674 ERR_WML <<
"Formula error in ability or weapon special: " <<
e.type <<
" at " <<
e.filename <<
':' <<
e.line <<
")";
681 template<
typename TComp>
684 if ( cfgs_.empty() ) {
690 bool only_cumulative =
true;
700 if ((*
p.ability_cfg)[
"cumulative"].to_bool()) {
702 if (value < 0) value = -value;
703 if (only_cumulative && !comp(value, abs_max)) {
705 best_loc =
p.teacher_loc;
707 }
else if (only_cumulative || comp(flat, value)) {
708 only_cumulative =
false;
710 best_loc =
p.teacher_loc;
713 return std::pair(flat + stack, best_loc);
716 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;
717 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;
751 std::string tag_name;
767 bool get_special_children(std::vector<special_match>& tag_result,
768 std::vector<special_match>& id_result,
769 const config& parent,
const std::string&
id,
770 bool just_peeking=
false) {
773 if (just_peeking && (key ==
id || cfg[
"id"] ==
id)) {
778 special_match special = { key, &cfg };
779 tag_result.push_back(special);
781 if(cfg[
"id"] ==
id) {
782 special_match special = { key, &cfg };
783 id_result.push_back(special);
789 bool get_special_children_id(std::vector<special_match>& id_result,
790 const config& parent,
const std::string&
id,
791 bool just_peeking=
false) {
794 if (just_peeking && (cfg[
"id"] ==
id)) {
798 if(cfg[
"id"] ==
id) {
799 special_match special = { key, &cfg };
800 id_result.push_back(special);
806 bool get_special_children_tags(std::vector<special_match>& tag_result,
807 const config& parent,
const std::string&
id,
808 bool just_peeking=
false) {
811 if (just_peeking && (key ==
id)) {
816 special_match special = { key, &cfg };
817 tag_result.push_back(special);
834 std::vector<special_match> special_tag_matches;
835 std::vector<special_match> special_id_matches;
836 if(special_id && special_tags){
837 if ( get_special_children(special_tag_matches, special_id_matches, specials_, special, simple_check) ) {
840 }
else if(special_id && !special_tags){
841 if ( get_special_children_id(special_id_matches, specials_, special, simple_check) ) {
844 }
else if(!special_id && special_tags){
845 if ( get_special_children_tags(special_tag_matches, specials_, special, simple_check) ) {
853 for(
const special_match& entry : special_tag_matches) {
854 if ( special_active(*entry.cfg, AFFECT_SELF, entry.tag_name) ) {
860 for(
const special_match& entry : special_id_matches) {
861 if ( special_active(*entry.cfg, AFFECT_SELF, entry.tag_name) ) {
869 if ( simple_check || !other_attack_ ) {
873 std::vector<special_match> special_tag_matches;
874 std::vector<special_match> special_id_matches;
875 if(special_id && special_tags){
876 get_special_children(special_tag_matches, special_id_matches, other_attack_->specials_, special);
877 }
else if(special_id && !special_tags){
878 get_special_children_id(special_id_matches, other_attack_->specials_, special);
879 }
else if(!special_id && special_tags){
880 get_special_children_tags(special_tag_matches, other_attack_->specials_, special);
883 for(
const special_match& entry : special_tag_matches) {
884 if ( other_attack_->special_active(*entry.cfg, AFFECT_OTHER, entry.tag_name) ) {
890 for(
const special_match& entry : special_id_matches) {
891 if ( other_attack_->special_active(*entry.cfg, AFFECT_OTHER, entry.tag_name) ) {
909 for(
const config&
i : specials_.child_range(special)) {
910 if(special_active(
i, AFFECT_SELF, special)) {
919 for(
const config&
i : other_attack_->specials_.child_range(special)) {
920 if(other_attack_->special_active(
i, AFFECT_OTHER, special)) {
937 boost::dynamic_bitset<>* active_list)
const
940 std::vector<std::pair<t_string, t_string>> res;
942 active_list->clear();
945 for(
const auto [key, cfg] : specials_.all_children_view()) {
946 if(!active_list || special_active(cfg, AFFECT_EITHER, key)) {
949 if(key ==
"plague") {
955 res.emplace_back(
name, cfg[
"description"].t_str());
959 active_list->push_back(
true);
965 res.emplace_back(
name, cfg.
get_or(
"description_inactive",
"description").
t_str() );
966 active_list->push_back(
false);
981 static void add_name(std::string& temp_string,
bool active,
const std::string&
name, std::set<std::string>& checking_name)
985 checking_name.insert(
name);
986 if (!temp_string.empty()) temp_string +=
", ";
1003 for(
const auto [key, cfg] : specials_.all_children_view())
1005 const bool active = special_active(cfg, AFFECT_EITHER, key);
1007 const std::string&
name =
1010 : cfg.
get_or(
"name_inactive",
"name").
str();
1023 std::string temp_string;
1024 std::set<std::string> checking_name;
1025 weapon_specials_impl_self(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name);
1026 weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name, {},
"affect_allies");
1027 if(!temp_string.empty() && !res.empty()) {
1028 temp_string =
", \n" + temp_string;
1030 }
else if (!temp_string.empty()){
1036 static void add_name_list(std::string& temp_string, std::string& weapon_abilities, std::set<std::string>& checking_name,
const std::string& from_str)
1038 if(!temp_string.empty()){
1039 temp_string = from_str.c_str() + temp_string;
1040 weapon_abilities += (!weapon_abilities.empty() && !temp_string.empty()) ?
"\n" :
"";
1041 weapon_abilities += temp_string;
1042 temp_string.clear();
1043 checking_name.clear();
1050 std::string temp_string, weapon_abilities;
1051 std::set<std::string> checking_name;
1052 for(
const auto [key, cfg] : specials_.all_children_view()) {
1054 const bool active = special_active(cfg, AFFECT_SELF, key);
1055 add_name(temp_string, active, cfg[
"name"].str(), checking_name);
1058 add_name_list(temp_string, weapon_abilities, checking_name,
"");
1060 weapon_specials_impl_self(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name,
checking_tags,
true);
1061 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Owned: "));
1063 weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name,
checking_tags,
"affect_allies",
true);
1065 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Taught: "));
1067 weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name,
checking_tags,
"affect_enemies",
true);
1069 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Taught: (by an enemy): "));
1073 for(
const auto [key, cfg] : other_attack_->specials_.all_children_view()) {
1075 const bool active = other_attack_->special_active(cfg, AFFECT_OTHER, key);
1076 add_name(temp_string, active, cfg[
"name"].str(), checking_name);
1080 weapon_specials_impl_self(temp_string, other_, other_attack_, shared_from_this(), other_loc_, AFFECT_OTHER, checking_name,
checking_tags);
1081 weapon_specials_impl_adj(temp_string, other_, other_attack_, shared_from_this(), other_loc_, AFFECT_OTHER, checking_name,
checking_tags);
1082 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Used by opponent: "));
1084 return weapon_abilities;
1088 std::string& temp_string,
1094 std::set<std::string>& checking_name,
1099 for(
const auto [key, cfg] : self->abilities().all_children_view()){
1101 const bool active = tag_checked && check_self_abilities_impl(self_attack, other_attack, cfg,
self, self_loc, whom, key, leader_bool);
1102 add_name(temp_string, active, cfg[
"name"].str(), checking_name);
1108 std::string& temp_string,
1114 std::set<std::string>& checking_name,
1116 const std::string& affect_adjacents,
1119 const unit_map& units = get_unit_map();
1122 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
1124 if (it == units.
end() || it->incapacitated())
1126 if(&*it ==
self.
get())
1128 for(
const auto [key, cfg] : it->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, *it,
i, self_loc, whom, key, leader_bool) && affect_allies;
1133 add_name(temp_string, active, cfg[
"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){
1706 std::vector<special_match>& id_result,
1707 const config& parent,
const std::string&
id,
1708 bool special_id=
true,
bool special_tags=
true) {
1709 if(special_id && special_tags){
1710 get_special_children(tag_result, id_result, parent,
id);
1711 }
else if(special_id && !special_tags){
1712 get_special_children_id(id_result, parent,
id);
1713 }
else if(!special_id && special_tags){
1714 get_special_children_tags(tag_result, parent,
id);
1722 show_recursion_warning(*
this, cfg);
1725 return (ability_active_impl(ability, cfg,
loc) && ability_affects_self(ability, cfg,
loc));
1732 show_recursion_warning(from, cfg);
1736 return (affects_side(cfg, side(), from.
side()) && from.
ability_active_impl(ability, cfg, adjacent[dir]) && ability_affects_adjacent(ability, cfg, dir,
loc, from));
1741 return (get_self_ability_bool(special, tag_name,
loc) && ability_affects_weapon(special, weapon,
false) && ability_affects_weapon(special, opp_weapon,
true));
1746 return (get_adj_ability_bool(special, tag_name, dir,
loc, from) && ability_affects_weapon(special, weapon,
false) && ability_affects_weapon(special, opp_weapon,
true));
1756 if(tag_name ==
"leadership" && leader_bool){
1757 if((*u).get_self_ability_bool_weapon(special, tag_name,
loc, self_attack, other_attack)) {
1761 if((*u).checking_tags().count(tag_name) != 0){
1762 if((*u).get_self_ability_bool(special, tag_name,
loc) &&
special_active_impl(self_attack, other_attack, special, whom, tag_name,
true)) {
1776 if(tag_name ==
"leadership" && leader_bool){
1777 if((*u).get_adj_ability_bool_weapon(special, tag_name, dir,
loc, from, self_attack, other_attack)) {
1781 if((*u).checking_tags().count(tag_name) != 0){
1782 if((*u).get_adj_ability_bool(special, tag_name, dir,
loc, from) &&
special_active_impl(self_attack, other_attack, special, whom, tag_name,
true)) {
1796 const unit_map& units = get_unit_map();
1798 std::vector<special_match> special_tag_matches_self;
1799 std::vector<special_match> special_id_matches_self;
1800 get_ability_children(special_tag_matches_self, special_id_matches_self, (*self_).abilities(), special, special_id , special_tags);
1802 for(
const special_match& entry : special_tag_matches_self) {
1809 for(
const special_match& entry : special_id_matches_self) {
1817 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
1819 if (it == units.
end() || it->incapacitated())
1821 if ( &*it ==
self_.get() )
1824 std::vector<special_match> special_tag_matches_adj;
1825 std::vector<special_match> special_id_matches_adj;
1826 get_ability_children(special_tag_matches_adj, special_id_matches_adj, it->abilities(), special, special_id , special_tags);
1828 for(
const special_match& entry : special_tag_matches_adj) {
1835 for(
const special_match& entry : special_id_matches_adj) {
1845 std::vector<special_match> special_tag_matches_other;
1846 std::vector<special_match> special_id_matches_other;
1847 get_ability_children(special_tag_matches_other, special_id_matches_other, (*other_).abilities(), special, special_id , special_tags);
1849 for(
const special_match& entry : special_tag_matches_other) {
1857 for(
const special_match& entry : special_id_matches_other) {
1865 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
1867 if (it == units.
end() || it->incapacitated())
1869 if ( &*it ==
other_.get() )
1872 std::vector<special_match> special_tag_matches_oadj;
1873 std::vector<special_match> special_id_matches_oadj;
1874 get_ability_children(special_tag_matches_oadj, special_id_matches_oadj, it->abilities(), special, special_id , special_tags);
1876 for(
const special_match& entry : special_tag_matches_oadj) {
1884 for(
const special_match& entry : special_id_matches_oadj) {
1900 if(
range().empty()){
1909 bool exclude_ability_attributes(
const std::string& tag_name,
const config &
filter)
1912 bool abilities_check = abilities_list::ability_value_tags().count(tag_name) != 0 || abilities_list::ability_no_value_tags().count(tag_name) != 0;
1913 if(
filter.has_attribute(
"active_on") && tag_name !=
"resistance" && abilities_check)
1915 if(
filter.has_attribute(
"apply_to") && tag_name !=
"resistance" && abilities_check)
1918 if(
filter.has_attribute(
"overwrite_specials") && abilities_list::weapon_number_tags().count(tag_name) == 0)
1921 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;
1922 if(
filter.has_attribute(
"value") && no_value_weapon_abilities_check)
1924 if(
filter.has_attribute(
"add") && no_value_weapon_abilities_check)
1926 if(
filter.has_attribute(
"sub") && no_value_weapon_abilities_check)
1928 if(
filter.has_attribute(
"multiply") && no_value_weapon_abilities_check)
1930 if(
filter.has_attribute(
"divide") && no_value_weapon_abilities_check)
1933 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;
1934 if(
filter.has_attribute(
"replacement_type") && tag_name !=
"damage_type" && all_engine)
1936 if(
filter.has_attribute(
"alternative_type") && tag_name !=
"damage_type" && all_engine)
1938 if(
filter.has_attribute(
"type") && tag_name !=
"plague" && all_engine)
1944 bool matches_ability_filter(
const config & cfg,
const std::string& tag_name,
const config &
filter)
1949 if(!exclude_ability_attributes(tag_name,
filter))
1955 if ( !filter_type.empty() &&
std::find(filter_type.begin(), filter_type.end(), tag_name) == filter_type.end() )
1963 if(!
filter[
"affect_adjacent"].empty()){
1964 bool adjacent = cfg.
has_child(
"affect_adjacent");
1965 if(
filter[
"affect_adjacent"].to_bool() != adjacent){
1997 if(!
filter[
"value"].empty()){
1998 if(tag_name ==
"drains"){
2002 }
else if(tag_name ==
"berserk"){
2006 }
else if(tag_name ==
"heal_on_hit" || tag_name ==
"heals" || tag_name ==
"regenerate" || tag_name ==
"leadership"){
2032 if(tag_name ==
"resistance"){
2061 auto fwml =
filter.optional_child(
"filter_wml");
2072 static bool common_matches_filter(
const config & cfg,
const std::string& tag_name,
const config &
filter)
2075 bool matches = matches_ability_filter(cfg, tag_name,
filter);
2078 for(
const auto [key, condition_cfg] :
filter.all_children_view() )
2082 matches = matches && common_matches_filter(cfg, tag_name, condition_cfg);
2085 else if ( key ==
"or" )
2086 matches = matches || common_matches_filter(cfg, tag_name, condition_cfg);
2089 else if ( key ==
"not" )
2090 matches = matches && !common_matches_filter(cfg, tag_name, condition_cfg);
2099 return common_matches_filter(cfg, tag_name,
filter);
2104 return common_matches_filter(cfg, tag_name,
filter);
2110 bool check_if_active =
filter[
"active"].to_bool();
2111 for(
const auto [key, cfg] :
specials().all_children_view()) {
2113 if(!check_if_active){
2126 for(
const auto [key, cfg] :
other_attack_->specials().all_children_view()) {
2139 if(!
filter[
"active"].to_bool()){
2142 const unit_map& units = get_unit_map();
2144 for(
const auto [key, cfg] : (*self_).abilities().all_children_view()) {
2145 if(
self_->ability_matches_filter(cfg, key,
filter)){
2153 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
2155 if (it == units.
end() || it->incapacitated())
2157 if ( &*it ==
self_.get() )
2160 for(
const auto [key, cfg] : it->abilities().all_children_view()) {
2169 for(
const auto [key, cfg] : (*other_).abilities().all_children_view()) {
2176 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
2178 if (it == units.
end() || it->incapacitated())
2180 if ( &*it ==
other_.get() )
2183 for(
const auto [key, cfg] : it->abilities().all_children_view()) {
2184 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)){
2195 if(
range().empty()){
2202 bool in_abilities_tag)
const
2222 const std::string& tag_name,
2223 bool in_abilities_tag)
2225 assert(self_attack || other_attack);
2226 bool is_attacker = self_attack ? self_attack->is_attacker_ : !other_attack->is_attacker_;
2227 bool is_for_listing = self_attack ? self_attack->is_for_listing_ : other_attack->is_for_listing_;
2233 if ( !special_affects_self(special, is_attacker) )
2237 if ( !special_affects_opponent(special, is_attacker) )
2242 const std::string & active_on = special[
"active_on"];
2243 if ( !active_on.empty() ) {
2244 if ( is_attacker && active_on !=
"offense" )
2246 if ( !is_attacker && active_on !=
"defense" )
2251 const unit_map& units = get_unit_map();
2253 unit_const_ptr self = self_attack ? self_attack->self_ : other_attack->other_;
2254 unit_const_ptr other = self_attack ? self_attack->other_ : other_attack->self_;
2255 map_location self_loc = self_attack ? self_attack->self_loc_ : other_attack->other_loc_;
2256 map_location other_loc = self_attack ? self_attack->other_loc_ : other_attack->self_loc_;
2258 if(
self ==
nullptr) {
2264 if(other ==
nullptr) {
2273 temporary_facing other_facing(other, other_loc.
get_relative_dir(self_loc));
2277 bool whom_is_self = ((whom ==
AFFECT_SELF) || ((whom ==
AFFECT_EITHER) && special_affects_self(special, is_attacker)));
2279 map_location their_loc = whom_is_self ? other_loc : self_loc;
2281 if (tag_name ==
"drains" && them && them->get_state(
"undrainable")) {
2284 if (tag_name ==
"plague" && them &&
2285 (them->get_state(
"unplagueable") ||
2289 if (tag_name ==
"poison" && them &&
2293 if (tag_name ==
"slow" && them &&
2297 if (tag_name ==
"petrifies" && them &&
2298 them->get_state(
"unpetrifiable")) {
2306 const map_location & att_loc = is_attacker ? self_loc : other_loc;
2307 const map_location & def_loc = is_attacker ? other_loc : self_loc;
2308 const const_attack_ptr& att_weapon = is_attacker ? self_attack : other_attack;
2309 const const_attack_ptr& def_weapon = is_attacker ? other_attack : self_attack;
2313 if (tag_name ==
"firststrike") {
2314 bool whom_is_defender = whom_is_self ? !is_attacker : is_attacker;
2315 if (whom_is_defender && att_weapon && att_weapon->has_special_or_ability(
"firststrike"))
2324 bool applied_both = special[
"apply_to"] ==
"both";
2325 const std::string& filter_self = in_abilities_tag ?
"filter_student" :
"filter_self";
2326 std::string self_check_if_recursion = (applied_both || whom_is_self) ? tag_name :
"";
2327 if (!special_unit_matches(
self, other, self_loc, self_attack, special, is_for_listing, filter_self, self_check_if_recursion))
2329 std::string opp_check_if_recursion = (applied_both || !whom_is_self) ? tag_name :
"";
2330 if (!special_unit_matches(other,
self, other_loc, other_attack, special, is_for_listing,
"filter_opponent", opp_check_if_recursion))
2334 bool applied_to_attacker = applied_both || (whom_is_self && is_attacker) || (!whom_is_self && !is_attacker);
2335 std::string att_check_if_recursion = applied_to_attacker ? tag_name :
"";
2336 if (!special_unit_matches(att, def, att_loc, att_weapon, special, is_for_listing,
"filter_attacker", att_check_if_recursion))
2338 bool applied_to_defender = applied_both || (whom_is_self && !is_attacker) || (!whom_is_self && is_attacker);
2339 std::string def_check_if_recursion= applied_to_defender ? tag_name :
"";
2340 if (!special_unit_matches(def, att, def_loc, def_weapon, special, is_for_listing,
"filter_defender", def_check_if_recursion))
2346 const std::string& filter_adjacent = in_abilities_tag ?
"filter_adjacent_student" :
"filter_adjacent";
2347 const std::string& filter_adjacent_location = in_abilities_tag ?
"filter_adjacent_student_location" :
"filter_adjacent_location";
2354 std::size_t count = 0;
2362 if (
i.has_attribute(
"is_enemy")) {
2380 std::size_t count = 0;
2385 if(!adj_filter.
match(adjacent[
static_cast<int>(
index)])) {
2415 if (
auto apply_filter = cfg.
optional_child(
"filter_base_value")) {
2422 return (cond_eq.
empty() || def == cond_eq.
to_int()) &&
2438 std::map<std::string,individual_effect> values_add;
2439 std::map<std::string,individual_effect> values_sub;
2440 std::map<std::string,individual_effect> values_mul;
2441 std::map<std::string,individual_effect> values_div;
2445 utils::optional<int> max_value = utils::nullopt;
2446 utils::optional<int> min_value = utils::nullopt;
2449 const config& cfg = *ability.ability_cfg;
2450 const std::string& effect_id = cfg[cfg[
"id"].
empty() ?
"name" :
"id"];
2458 callable.add(
"base_value", wfl::variant(def));
2459 return std::round(formula.evaluate(callable).as_int());
2462 int value_cum = cfg[
"cumulative"].to_bool() ? std::max(def, value) : value;
2465 set_effect_min.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2466 set_effect_max.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2469 if(value_cum > set_effect_max.
value) {
2470 set_effect_max.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2472 if(value_cum < set_effect_min.
value) {
2473 set_effect_min.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2481 max_value = max_value ? std::min(*max_value, cfg[
"max_value"].to_int()) : cfg[
"max_value"].to_int();
2484 min_value = min_value ? std::max(*min_value, cfg[
"min_value"].to_int()) : cfg[
"min_value"].to_int();
2490 callable.add(
"base_value", wfl::variant(def));
2491 return std::round(formula.evaluate(callable).as_int());
2494 if(add_effect == values_add.end() || add > add_effect->second.value) {
2495 values_add[effect_id].set(
ADD, add, ability.ability_cfg, ability.teacher_loc);
2500 callable.add(
"base_value", wfl::variant(def));
2501 return std::round(formula.evaluate(callable).as_int());
2504 if(sub_effect == values_sub.end() || sub < sub_effect->second.value) {
2505 values_sub[effect_id].set(
ADD, sub, ability.ability_cfg, ability.teacher_loc);
2510 callable.add(
"base_value", wfl::variant(def));
2511 return formula.evaluate(callable).as_decimal() / 1000.0 ;
2514 if(mul_effect == values_mul.end() || multiply > mul_effect->second.value) {
2515 values_mul[effect_id].set(
MUL, multiply, ability.ability_cfg, ability.teacher_loc);
2520 callable.add(
"base_value", wfl::variant(def));
2521 return formula.evaluate(callable).as_decimal() / 1000.0 ;
2525 ERR_NG <<
"division by zero with divide= in ability/weapon special " << effect_id;
2529 if(div_effect == values_div.end() || divide > div_effect->second.value) {
2530 values_div[effect_id].set(
DIV, divide, ability.ability_cfg, ability.teacher_loc);
2537 value_set = std::max(set_effect_max.
value, 0) + std::min(set_effect_min.
value, 0);
2538 if(set_effect_max.
value > def) {
2541 if(set_effect_min.
value < def) {
2554 double multiplier = 1.0;
2555 double divisor = 1.0;
2557 for(
const auto& val : values_mul) {
2558 multiplier *= val.second.value/100.0;
2562 for(
const auto& val : values_div) {
2563 divisor *= val.second.value/100.0;
2568 for(
const auto& val : values_add) {
2569 addition += val.second.value;
2575 int substraction = 0;
2576 for(
const auto& val : values_sub) {
2577 substraction += val.second.value;
2583 if(max_value && min_value && *min_value < *max_value) {
2585 }
else if(max_value && !min_value) {
2587 }
else if(min_value && !max_value) {
static void add_name(std::string &temp_string, bool active, const std::string &name, std::set< std::string > &checking_name)
static lg::log_domain log_engine("engine")
static void add_string_to_vector(std::vector< std::string > &image_list, const config &cfg, const std::string &attribute_name)
static void add_name_list(std::string &temp_string, std::string &weapon_abilities, std::set< std::string > &checking_name, const std::string &from_str)
static bool overwrite_special_affects(const config &special)
static lg::log_domain log_wml("wml")
static void get_ability_children(std::vector< special_match > &tag_result, std::vector< special_match > &id_result, const config &parent, const std::string &id, bool special_id=true, bool special_tags=true)
Gets the children of parent (which should be the abilities for an attack_type) and places the ones wh...
Helper similar to std::unique_lock for detecting when calculations such as has_special have entered i...
specials_context_t(const attack_type &weapon, bool attacking)
Initialize weapon specials context for listing.
const config & specials() const
bool special_active(const config &special, AFFECTS whom, const std::string &tag_name, bool in_abilities_tag=false) const
std::string weapon_specials() const
Returns a comma-separated string of active names for the specials of *this.
bool check_self_abilities(const config &cfg, const std::string &special) const
check_self_abilities : return an boolean value for checking of activities of abilities used like weap...
bool has_special(const std::string &special, bool simple_check=false, bool special_id=true, bool special_tags=true) const
Returns whether or not *this has a special with a tag or id equal to special.
std::string weapon_specials_value(const std::set< std::string > &checking_tags) const
int composite_value(const unit_ability_list &abil_list, int base_value) const
Return the special weapon value, considering specials.
const_attack_ptr other_attack_
void add_formula_context(wfl::map_formula_callable &) const
const std::string & range() const
bool has_special_or_ability_with_filter(const config &filter) const
const std::string & type() const
std::string select_replacement_type(const unit_ability_list &damage_type_list) const
Select best damage type based on frequency count for replacement_type.
unit_ability_list get_weapon_ability(const std::string &ability) const
Returns list for weapon like abilities for each ability type.
bool has_ability_with_filter(const config &filter) const
double modified_damage() const
Returns the damage per attack of this weapon, considering specials.
unit_ability_list get_specials_and_abilities(const std::string &special) const
static bool special_active_impl(const const_attack_ptr &self_attack, const const_attack_ptr &other_attack, const config &special, AFFECTS whom, const std::string &tag_name, bool in_abilities_tag=false)
Returns whether or not the given special is active for the specified unit, based on the current conte...
bool overwrite_special_checking(unit_ability_list &overwriters, const config &cfg, const std::string &tag_name) const
Check whether cfg would be overwritten by any element of overwriters.
bool check_adj_abilities(const config &cfg, const std::string &special, int dir, const unit &from) const
check_adj_abilities : return an boolean value for checking of activities of abilities used like weapo...
static void weapon_specials_impl_adj(std::string &temp_string, const unit_const_ptr &self, const const_attack_ptr &self_attack, const const_attack_ptr &other_attack, const map_location &self_loc, AFFECTS whom, std::set< std::string > &checking_name, const std::set< std::string > &checking_tags={}, const std::string &affect_adjacents="", bool leader_bool=false)
unit_ability_list overwrite_special_overwriter(unit_ability_list overwriters, const std::string &tag_name) const
Filter a list of abilities or weapon specials, removing any entries that don't own the overwrite_spec...
recursion_guard update_variables_recursion(const config &special) const
Tests which might otherwise cause infinite recursion should call this, check that the returned object...
std::pair< std::string, int > select_alternative_type(const unit_ability_list &damage_type_list, const unit_ability_list &resistance_list) const
Select best damage type based on highest damage for alternative_type.
std::vector< std::pair< t_string, t_string > > special_tooltips(boost::dynamic_bitset<> *active_list=nullptr) const
Returns a vector of names and descriptions for the specials of *this.
static void weapon_specials_impl_self(std::string &temp_string, const unit_const_ptr &self, const const_attack_ptr &self_attack, const const_attack_ptr &other_attack, const map_location &self_loc, AFFECTS whom, std::set< std::string > &checking_name, const std::set< std::string > &checking_tags={}, bool leader_bool=false)
weapon_specials_impl_self and weapon_specials_impl_adj : check if special name can be added.
std::pair< std::string, std::set< std::string > > damage_types() const
Return a type()/replacement_type and a list of alternative_types that should be displayed in the sele...
static bool check_self_abilities_impl(const const_attack_ptr &self_attack, const const_attack_ptr &other_attack, const config &special, const unit_const_ptr &u, const map_location &loc, AFFECTS whom, const std::string &tag_name, bool leader_bool=false)
check_self_abilities_impl : return an boolean value for checking of activities of abilities used like...
bool attack_empty() const
Returns true if this is a dummy attack_type, for example the placeholder that the unit_attack dialog ...
bool special_matches_filter(const config &cfg, const std::string &tag_name, const config &filter) const
Filter a list of abilities or weapon specials.
void modified_attacks(unsigned &min_attacks, unsigned &max_attacks) const
Calculates the number of attacks this weapon has, considering specials.
static bool check_adj_abilities_impl(const const_attack_ptr &self_attack, const const_attack_ptr &other_attack, const config &special, const unit_const_ptr &u, const unit &from, int dir, const map_location &loc, AFFECTS whom, const std::string &tag_name, bool leader_bool=false)
check_adj_abilities_impl : return an boolean value for checking of activities of abilities used like ...
bool has_special_with_filter(const config &filter) const
check if special matche
unit_ability_list get_specials(const std::string &special) const
Returns the currently active specials as an ability list, given the current context (see set_specials...
bool has_special_or_ability(const std::string &special, bool special_id=true, bool special_tags=true) const
used for abilities used like weapon and true specials
bool has_weapon_ability(const std::string &special, bool special_id=true, bool special_tags=true) const
used for abilities used like weapon
std::pair< std::string, int > effective_damage_type() const
The type of attack used and the resistance value that does the most damage.
Variant for storing WML attributes.
std::string str(const std::string &fallback="") const
int to_int(int def=0) const
auto apply_visitor(const V &visitor) const
Visitor support: Applies a visitor to the underlying variant.
bool blank() const
Tests for an attribute that was never set.
bool empty() const
Tests for an attribute that either was never set or was set to "".
A config object defines a single node in a WML file, with access to child nodes.
const attribute_value & get_or(const config_key_type key, const config_key_type default_key) const
Chooses a value.
config & mandatory_child(config_key_type key, int n=0)
Returns the nth child with the given key, or throws an error if there is none.
bool matches(const config &filter) const
auto all_children_view() const
In-order iteration over all children.
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
bool has_attribute(config_key_type key) const
child_itors child_range(config_key_type key)
config & child_or_add(config_key_type key)
Returns a reference to the first child with the given key.
const attribute_value * get(config_key_type key) const
Returns a pointer to the attribute with the given key or nullptr if it does not exist.
optional_config_impl< config > optional_child(config_key_type key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
config & add_child(config_key_type key)
Abstract class for exposing game data that doesn't depend on the GUI, however which for historical re...
const team & get_team(int side) const
This getter takes a 1-based side number, not a 0-based team number.
virtual const unit_map & units() const =0
const display_context & context() const
static display * get_singleton()
Returns the display object if a display object exists.
virtual const display_context & get_disp_context() const =0
virtual const unit_map & units() const override
virtual const gamemap & map() const override
bool is_village(const map_location &loc) const
This class stores all the data for a single 'side' (in game nomenclature).
bool is_enemy(int n) const
bool match(const map_location &loc) const
Helper similar to std::unique_lock for detecting when calculations such as abilities have entered inf...
int get_composite_value() const
std::vector< individual_effect > effect_list_
double get_composite_double_value() const
double composite_double_value_
void append(const unit_ability_list &other)
Appends the abilities from other to this, ignores other.loc()
std::pair< int, map_location > lowest(const std::string &key, int def=0) const
const map_location & loc() const
void emplace_back(T &&... args)
std::pair< int, map_location > highest(const std::string &key, int def=0) const
void append_if(const unit_ability_list &other, const Predicate &predicate)
Appends any abilities from other for which the given condition returns true to this,...
std::pair< int, map_location > get_extremum(const std::string &key, int def, const TComp &comp) const
bool matches(const unit &u, const map_location &loc) const
Determine if *this matches filter at a specified location.
unit_filter & set_use_flat_tod(bool value)
Container associating units to locations.
unit_ptr find_unit_ptr(const T &val)
unit_iterator find(std::size_t id)
const unit_type_map & types() const
A single unit type that the player may recruit.
const t_string & type_name() const
The name of the unit in the current language setting.
This class represents a single unit of a specific type.
A variable-expanding proxy for the config class.
void swap(config &lhs, config &rhs)
Implement non-member swap function for std::swap (calls config::swap).
std::string deprecated_message(const std::string &elem_name, DEP_LEVEL level, const version_info &version, const std::string &detail)
map_display and display: classes which take care of displaying the map and game-data on the screen.
Interfaces for manipulating version numbers of engine, add-ons, etc.
static std::string _(const char *str)
bool ability_active(const std::string &ability, const config &cfg, const map_location &loc) const
Check if an ability is active.
bool ability_affects_self(const std::string &ability, const config &cfg, const map_location &loc) const
Check if an ability affects the owning unit.
std::vector< const config * > open_queries_
While processing a recursive match, all the filters that are currently being checked,...
bool get_ability_bool(const std::string &tag_name, const map_location &loc) const
Checks whether this unit currently possesses or is affected by a given ability.
bool ability_affects_adjacent(const std::string &ability, const config &cfg, int dir, const map_location &loc, const unit &from) const
Check if an ability affects adjacent units.
std::vector< std::tuple< std::string, t_string, t_string, t_string > > ability_tooltips() const
Gets the names and descriptions of this unit's abilities.
unit_ability_list get_abilities_weapons(const std::string &tag_name, const map_location &loc, const_attack_ptr weapon=nullptr, const_attack_ptr opp_weapon=nullptr) const
bool has_ability_type(const std::string &ability) const
Check if the unit has an ability of a specific type.
unit_ability_list get_abilities(const std::string &tag_name, const map_location &loc) const
Gets the unit's active abilities of a particular type if it were on a specified location.
unit_race::GENDER gender_
bool get_self_ability_bool_weapon(const config &special, const std::string &tag_name, const map_location &loc, const const_attack_ptr &weapon=nullptr, const const_attack_ptr &opp_weapon=nullptr) const
Checks whether this unit currently possesses a given ability of leadership type.
recursion_guard()
Construct an empty instance, only useful for extending the lifetime of a recursion_guard returned fro...
bool get_self_ability_bool(const config &cfg, const std::string &ability, const map_location &loc) const
Checks whether this unit currently possesses a given ability, and that that ability is active.
std::vector< std::string > get_ability_list() const
Get a list of all abilities by ID.
bool ability_active_impl(const std::string &ability, const config &cfg, const map_location &loc) const
Check if an ability is active.
bool get_adj_ability_bool_weapon(const config &special, const std::string &tag_name, int dir, const map_location &loc, const unit &from, const const_attack_ptr &weapon=nullptr, const const_attack_ptr &opp_weapon=nullptr) const
Checks whether this unit is affected by a given ability of leadership type.
recursion_guard update_variables_recursion(const config &ability) const
recursion_guard & operator=(recursion_guard &&)
const std::set< std::string > & checking_tags() const
bool get_adj_ability_bool(const config &cfg, const std::string &ability, int dir, const map_location &loc, const unit &from) const
Checks whether this unit is affected by a given ability, and that that ability is active.
bool ability_matches_filter(const config &cfg, const std::string &tag_name, const config &filter) const
Verify what abilities attributes match with filter.
bool ability_affects_weapon(const config &cfg, const const_attack_ptr &weapon, bool is_opp) const
filters the weapons that condition the use of abilities for combat ([resistance],[leadership] or abil...
const unit_type & type() const
This unit's type, accounting for gender and variation.
const std::string & id() const
Gets this unit's id.
int side() const
The side this unit belongs to.
const t_string & name() const
Gets this unit's translatable display name.
@ STATE_POISONED
The unit is slowed - it moves slower and does less damage.
std::vector< std::string > halo_or_icon_abilities(const std::string &image_type) const
New lexcical_cast header.
void get_adjacent_tiles(const map_location &a, map_location *res)
Function which, given a location, will place all adjacent locations in res.
Standard logging facilities (interface).
void set(CURSOR_TYPE type)
Use the default parameter to reset cursors.
const color_t inactive_details_color
const color_t BUTTON_COLOR
std::vector< std::pair< int, int > > default_counts
static bool is_active(const widget *wgt)
std::stringstream & log_to_chat()
Use this to show WML errors in the ingame chat.
std::string span_color(const color_t &color, Args &&... data)
Applies Pango markup to the input specifying its display color.
filter_context * filter_con
bool filter_base_matches(const config &cfg, int def)
std::size_t index(std::string_view str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Utility functions for implementing [filter], [filter_ability], [filter_weapon], etc.
bool int_matches_if_present(const config &filter, const config &cfg, const std::string &attribute, utils::optional< int > def=utils::nullopt)
bool set_includes_if_present(const config &filter, const config &cfg, const std::string &attribute)
filter[attribute] and cfg[attribute] are assumed to be comma-separated lists.
bool double_matches_if_present(const config &filter, const config &cfg, const std::string &attribute, utils::optional< double > def=utils::nullopt)
Checks whether the filter matches the value of cfg[attribute].
bool int_matches_if_present_or_negative(const config &filter, const config &cfg, const std::string &attribute, const std::string &opposite, utils::optional< int > def=utils::nullopt)
Supports filters using "add" and "sub" attributes, for example a filter add=1 matching a cfg containi...
bool string_matches_if_present(const config &filter, const config &cfg, const std::string &attribute, const std::string &def)
bool bool_or_empty(const config &filter, const config &cfg, const std::string &attribute)
bool bool_matches_if_present(const config &filter, const config &cfg, const std::string &attribute, bool def)
Checks whether the filter matches the value of cfg[attribute].
void trim(std::string_view &s)
bool contains(const Container &container, const Value &value)
Returns true iff value is found in container.
std::vector< std::pair< int, int > > parse_ranges_unsigned(const std::string &str)
Handles a comma-separated list of inputs to parse_range, in a context that does not expect negative v...
void erase_if(Container &container, const Predicate &predicate)
Convenience wrapper for using std::remove_if on a container.
void sort_if(Container &container, const Predicate &predicate)
Convenience wrapper for using std::sort on a container.
std::vector< std::string > split(const config_attribute_value &val)
auto * find(Container &container, const Value &value)
Convenience wrapper for using find on a container without needing to comare to end()
std::string::const_iterator iterator
std::shared_ptr< const unit > unit_const_ptr
std::shared_ptr< const attack_type > const_attack_ptr
std::shared_ptr< unit > unit_ptr
const config::attribute_value & gender_value(const config &cfg, unit_race::GENDER gender, const std::string &male_key, const std::string &female_key, const std::string &default_key)
Chooses a value from the given config based on gender.
Encapsulates the map of the game.
static std::vector< direction > parse_directions(const std::string &str)
Parse_directions takes a comma-separated list, and filters out any invalid directions.
static std::vector< direction > all_directions()
direction
Valid directions which can be moved in our hexagonal world.
direction get_relative_dir(const map_location &loc, map_location::RELATIVE_DIR_MODE mode) const
static const map_location & null_location()
void set(value_modifier t, int val, const config *abil, const map_location &l)
Data typedef for unit_ability_list.
map_location student_loc
Used by the formula in the ability.
const config * ability_cfg
The contents of the ability tag, never nullptr.
map_location teacher_loc
The location of the teacher, that is the unit who owns the ability tags (different from student becau...
pointer get_shared_ptr() const
This is exactly the same as operator-> but it's slightly more readable, and can replace &*iter syntax...
static map_location::direction s
unit_type_data unit_types