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,
const std::string& tag_name,
unit_race::GENDER gender, std::vector<std::tuple<std::string, t_string,t_string,t_string>>& res,
bool active)
331 "female_name_inactive",
"name_inactive");
352 std::vector<std::tuple<std::string, t_string,t_string,t_string>> res;
356 add_ability_tooltip(cfg, tag_name,
gender_, res,
true);
364 std::vector<std::tuple<std::string, t_string,t_string,t_string>> res;
370 if(add_ability_tooltip(cfg, tag_name,
gender_, res, active))
372 active_list.push_back(active);
389 static std::vector<std::tuple<std::string, std::string>> already_shown;
391 auto identifier = std::tuple<std::string, std::string>{
unit.
id(),
filter.debug()};
396 std::string_view filter_text_view = std::get<1>(identifier);
398 ERR_NG <<
"Looped recursion error for unit '" <<
unit.
id()
399 <<
"' while checking ability '" << filter_text_view <<
"'";
403 if(already_shown.size() > 100) {
404 already_shown.clear();
406 already_shown.push_back(std::move(identifier));
421 : parent(u.shared_from_this())
431 unit::recursion_guard::operator bool()
const {
437 assert(
this != &other);
446 assert(!parent->open_queries_.empty());
447 parent->open_queries_.pop_back();
455 show_recursion_warning(*
this, cfg);
465 const unit_map& units = get_unit_map();
468 const std::string& filter_adjacent = in_abilities_tag ?
"filter_adjacent_student" :
"filter_adjacent";
469 const std::string& filter_adjacent_location = in_abilities_tag ?
"filter_adjacent_student_location" :
"filter_adjacent_location";
472 std::size_t radius =
i[
"radius"].to_int(1);
473 std::size_t count = 0;
475 ufilt.set_use_flat_tod(illuminates);
476 for(
const unit& u : units) {
479 if(&u == &
self || distance > radius || !ufilt(u,
self)) {
483 for(
unsigned j = 0; j < adjacent.size(); ++j) {
484 bool adj_or_dist = distance != 1 ?
distance_between(adjacent[j], from_loc) == (distance - 1) : adjacent[j] == from_loc;
490 assert(dir >= 0 && dir <= 5);
492 if(
i.has_attribute(
"adjacent")) {
497 if(
i.has_attribute(
"is_enemy")) {
506 if(
i[
"count"].empty() && count == 0) {
515 std::size_t count = 0;
517 adj_filter.flatten(illuminates);
521 if(!adj_filter.match(adjacent[
static_cast<int>(
index)])) {
538 bool illuminates = ability ==
"illuminates";
556 bool illuminates = ability ==
"illuminates";
558 assert(dir >=0 && dir <= 5);
560 std::size_t radius = 1;
564 if(
i[
"radius"] !=
"all_map") {
565 radius =
i[
"radius"].to_int(1);
573 if (
i.has_attribute(
"adjacent")) {
575 if (
std::find(dirs.begin(), dirs.end(), direction) == dirs.end()) {
579 auto filter =
i.optional_child(
"filter");
591 bool affect_self = cfg[
"affect_self"].to_bool(
true);
592 if (!
filter || !affect_self)
return affect_self;
598 const std::string filter_tag_name = is_opp ?
"filter_second_weapon" :
"filter_weapon";
607 filter_lock = weapon->update_variables_recursion(cfg);
609 show_recursion_warning(*
this, cfg);
612 return weapon->matches_filter(
filter);
624 auto ret =
std::find(image_list.begin(), image_list.end(), cfg[attribute_name].str());
625 if(ret == image_list.end()){
626 image_list.push_back(cfg[attribute_name].str());
632 std::vector<std::string> image_list;
640 if(!cfg[image_type +
"_image_self"].str().empty() &&
is_active){
645 const unit_map& units = get_unit_map();
647 for(
const unit& u : units) {
648 if(!u.has_ability_distant_image() || u.incapacitated() || &u ==
this) {
653 if(distance > *u.has_ability_distant_image()) {
656 int dir = find_direction(
loc_, from_loc, distance);
657 for(
const auto [key, cfg] : u.abilities_.all_children_view()) {
665 if(image_list.size() >= 2){
666 std::sort(image_list.begin(), image_list.end());
674 callable.
add(
"attacker",
wfl::variant(std::make_shared<wfl::unit_callable>(*att)));
677 callable.
add(
"defender",
wfl::variant(std::make_shared<wfl::unit_callable>(*def)));
684 template<
typename T,
typename TFuncFormula>
685 class get_ability_value_visitor
686 #ifdef USING_BOOST_VARIANT
687 :
public boost::static_visitor<T>
692 get_ability_value_visitor(T def,
const TFuncFormula& formula_handler) : def_(def), formula_handler_(formula_handler) {}
694 T operator()(
const utils::monostate&)
const {
return def_; }
695 T operator()(
bool)
const {
return def_; }
696 T operator()(
int i)
const {
return static_cast<T
>(
i); }
697 T operator()(
unsigned long long u)
const {
return static_cast<T
>(u); }
698 T operator()(
double d)
const {
return static_cast<T
>(
d); }
699 T operator()(
const t_string&)
const {
return def_; }
700 T operator()(
const std::string&
s)
const
702 if(
s.size() >= 2 &&
s[0] ==
'(') {
703 return formula_handler_(
s);
705 return lexical_cast_default<T>(
s, def_);
710 const TFuncFormula& formula_handler_;
713 template<
typename T,
typename TFuncFormula>
716 return v.
apply_visitor(get_ability_value_visitor(def, [&](
const std::string&
s) {
719 const unit_map& units = get_unit_map();
723 if(u_itor == units.
end()) {
728 att->add_formula_context(callable);
731 callable.add(
"student",
wfl::variant(std::make_shared<wfl::unit_callable>(*uptr)));
734 callable.add(
"other",
wfl::variant(std::make_shared<wfl::unit_callable>(*uptr)));
738 lg::log_to_chat() <<
"Formula error in ability or weapon special: " <<
e.type <<
" at " <<
e.filename <<
':' <<
e.line <<
")\n";
739 ERR_WML <<
"Formula error in ability or weapon special: " <<
e.type <<
" at " <<
e.filename <<
':' <<
e.line <<
")";
746 template<
typename TComp>
749 if ( cfgs_.empty() ) {
755 bool only_cumulative =
true;
765 if ((*
p.ability_cfg)[
"cumulative"].to_bool()) {
767 if (value < 0) value = -value;
768 if (only_cumulative && !comp(value, abs_max)) {
770 best_loc =
p.teacher_loc;
772 }
else if (only_cumulative || comp(flat, value)) {
773 only_cumulative =
false;
775 best_loc =
p.teacher_loc;
778 return std::pair(flat + stack, best_loc);
781 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;
782 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;
821 if(simple_check && specials().has_child(special)) {
825 for(
const config &
i : specials().child_range(special)) {
826 if(special_active(
i, AFFECT_SELF, special)) {
832 if ( simple_check || !other_attack_ ) {
836 for(
const config &
i : other_attack_->specials().child_range(special)) {
837 if(other_attack_->special_active(
i, AFFECT_OTHER, special)) {
854 for(
const config&
i : specials_.child_range(special)) {
855 if(special_active(
i, AFFECT_SELF, special)) {
864 for(
const config&
i : other_attack_->specials_.child_range(special)) {
865 if(other_attack_->special_active(
i, AFFECT_OTHER, special)) {
882 boost::dynamic_bitset<>* active_list)
const
885 std::vector<std::pair<t_string, t_string>> res;
887 active_list->clear();
890 for(
const auto [key, cfg] : specials_.all_children_view()) {
891 bool active = !active_list || special_active(cfg, AFFECT_EITHER, key);
893 std::string
name = active
895 : cfg.
get_or(
"name_inactive",
"name").
str();
901 std::string desc = active
902 ? cfg[
"description"].str()
903 : cfg.
get_or(
"description_inactive",
"description").
str();
911 active_list->push_back(active);
918 boost::dynamic_bitset<>* active_list)
const
920 std::vector<std::pair<t_string, t_string>> res;
922 active_list->clear();
924 std::set<std::string> checking_name;
928 for(
const auto [key, cfg] : self_->abilities().all_children_view()) {
929 if(!active_list || check_self_abilities_impl(shared_from_this(), other_attack_, cfg, self_, self_loc_, AFFECT_SELF, key,
false)) {
930 const std::string
name = cfg[
"name_affected"];
931 const std::string desc = cfg[
"description_affected"];
936 res.emplace_back(
name, desc);
939 active_list->push_back(
true);
943 for(
const unit& u : get_unit_map()) {
944 if(!u.has_ability_distant() || u.incapacitated() || &u == self_.get()) {
949 if(distance > *u.has_ability_distant()) {
952 int dir = find_direction(self_loc_, from_loc, distance);
953 for(
const auto [key, cfg] : u.abilities().all_children_view()) {
954 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)) {
955 const std::string
name = cfg[
"name_affected"];
956 const std::string desc = cfg[
"description_affected"];
961 res.emplace_back(
name, desc);
964 active_list->push_back(
true);
980 static void add_name(std::string& temp_string,
bool active,
const std::string&
name, std::set<std::string>& checking_name)
982 if (active && !
name.
empty() && checking_name.count(
name) == 0) {
983 checking_name.insert(
name);
984 if (!temp_string.empty()) temp_string +=
", ";
999 std::vector<std::string> specials;
1001 for(
const auto [key, cfg] : specials_.all_children_view()) {
1002 const bool active = special_active(cfg, AFFECT_EITHER, key);
1004 std::string
name = active
1006 : cfg.
get_or(
"name_inactive",
"name").
str();
1018 std::string temp_string;
1019 std::set<std::string> checking_name;
1020 weapon_specials_impl_self(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name);
1021 weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name, {},
"affect_allies");
1023 if(!temp_string.empty()) {
1024 specials.push_back(
"\n" + std::move(temp_string));
1030 static void add_name_list(std::string& temp_string, std::string& weapon_abilities, std::set<std::string>& checking_name,
const std::string& from_str)
1032 if(!temp_string.empty()){
1033 temp_string = from_str.c_str() + temp_string;
1034 weapon_abilities += (!weapon_abilities.empty() && !temp_string.empty()) ?
"\n" :
"";
1035 weapon_abilities += temp_string;
1036 temp_string.clear();
1037 checking_name.clear();
1044 std::string temp_string, weapon_abilities;
1045 std::set<std::string> checking_name;
1046 for(
const auto [key, cfg] : specials_.all_children_view()) {
1048 const bool active = special_active(cfg, AFFECT_SELF, key);
1049 add_name(temp_string, active, cfg[
"name"].str(), checking_name);
1052 add_name_list(temp_string, weapon_abilities, checking_name,
"");
1054 weapon_specials_impl_self(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name,
checking_tags,
true);
1055 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Owned: "));
1057 weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name,
checking_tags,
"affect_allies",
true);
1059 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Taught: "));
1061 weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name,
checking_tags,
"affect_enemies",
true);
1063 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Taught: (by an enemy): "));
1067 for(
const auto [key, cfg] : other_attack_->specials_.all_children_view()) {
1069 const bool active = other_attack_->special_active(cfg, AFFECT_OTHER, key);
1070 add_name(temp_string, active, cfg[
"name"].str(), checking_name);
1074 weapon_specials_impl_self(temp_string, other_, other_attack_, shared_from_this(), other_loc_, AFFECT_OTHER, checking_name,
checking_tags);
1075 weapon_specials_impl_adj(temp_string, other_, other_attack_, shared_from_this(), other_loc_, AFFECT_OTHER, checking_name,
checking_tags);
1076 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Used by opponent: "));
1078 return weapon_abilities;
1082 std::string& temp_string,
1088 std::set<std::string>& checking_name,
1093 for(
const auto [key, cfg] : self->abilities().all_children_view()){
1095 const bool active = tag_checked && check_self_abilities_impl(self_attack, other_attack, cfg,
self, self_loc, whom, key, leader_bool);
1096 add_name(temp_string, active, cfg.
get_or(
"name_affected",
"name").
str(), checking_name);
1102 std::string& temp_string,
1108 std::set<std::string>& checking_name,
1110 const std::string& affect_adjacents,
1113 const unit_map& units = get_unit_map();
1115 for(
const unit& u : units) {
1116 if(!u.has_ability_distant() || u.incapacitated() || &u ==
self.get()) {
1121 if(distance > *u.has_ability_distant()) {
1124 int dir = find_direction(self_loc, from_loc, distance);
1125 for(
const auto [key, cfg] : u.abilities().all_children_view()) {
1127 bool default_bool = affect_adjacents ==
"affect_allies" ? true :
false;
1128 bool affect_allies = !affect_adjacents.empty() ? cfg[affect_adjacents].to_bool(default_bool) :
true;
1129 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;
1130 add_name(temp_string, active, cfg.
get_or(
"name_affected",
"name").
str(), checking_name);
1156 : parent(weapon.shared_from_this())
1158 weapon.
self_ = std::move(
self);
1159 weapon.
other_ = std::move(other);
1176 : parent(weapon.shared_from_this())
1178 weapon.
self_ = std::move(
self);
1196 : parent(weapon.shared_from_this())
1208 : parent(weapon.shared_from_this())
1216 if(was_moved)
return;
1221 parent->is_attacker_ =
false;
1222 parent->other_attack_ =
nullptr;
1223 parent->is_for_listing_ =
false;
1227 : parent(std::move(other.parent))
1229 other.was_moved =
true;
1240 unsigned & max_attacks)
const
1245 if ( attacks_value < 0 ) {
1247 ERR_NG <<
"negative number of strikes after applying weapon specials";
1252 if ( !swarm_specials.
empty() ) {
1253 min_attacks = std::max<int>(0, swarm_specials.
highest(
"swarm_attacks_min").first);
1254 max_attacks = std::max<int>(0, swarm_specials.
highest(
"swarm_attacks_max", attacks_value).first);
1256 min_attacks = max_attacks = attacks_value;
1262 std::map<std::string, unsigned int> type_count;
1263 unsigned int max = 0;
1264 for(
auto&
i : damage_type_list) {
1266 if(
c.has_attribute(
"replacement_type")) {
1267 std::string
type =
c[
"replacement_type"].str();
1268 unsigned int count = ++type_count[
type];
1275 if (type_count.empty())
return type();
1277 std::vector<std::string> type_list;
1278 for(
auto&
i : type_count){
1279 if(
i.second == max){
1280 type_list.push_back(
i.first);
1284 if(type_list.empty())
return type();
1286 return type_list.front();
1291 std::map<std::string, int> type_res;
1292 int max_res = INT_MIN;
1294 for(
auto&
i : damage_type_list) {
1296 if(
c.has_attribute(
"alternative_type")) {
1297 std::string
type =
c[
"alternative_type"].str();
1298 if(type_res.count(
type) == 0){
1299 type_res[
type] = (*other_).resistance_value(resistance_list,
type);
1300 max_res = std::max(max_res, type_res[
type]);
1306 if (type_res.empty())
return {
"", INT_MIN};
1308 std::vector<std::string> type_list;
1309 for(
auto&
i : type_res){
1310 if(
i.second == max_res){
1311 type_list.push_back(
i.first);
1314 if(type_list.empty())
return {
"", INT_MIN};
1316 return {type_list.front(), max_res};
1329 resistance_list = (*other_).get_abilities_weapons(
"resistance",
other_loc_,
other_attack_, shared_from_this());
1331 return (!((*
i.ability_cfg)[
"active_on"].empty() || (!
is_attacker_ && (*
i.ability_cfg)[
"active_on"] ==
"offense") || (
is_attacker_ && (*
i.ability_cfg)[
"active_on"] ==
"defense")));
1335 int res =
other_ ? (*other_).resistance_value(resistance_list,
type()) : 100;
1336 if(damage_type_list.
empty()){
1337 return {
type(), res};
1343 res = replacement_type !=
type() ? (*other_).resistance_value(resistance_list, replacement_type) : res;
1344 replacement_type = alternative_type.second > res ? alternative_type.first : replacement_type;
1345 res = std::max(res, alternative_type.second);
1347 return {replacement_type, res};
1356 std::set<std::string> alternative_damage_types;
1357 if(damage_type_list.
empty()){
1358 return {
type(), alternative_damage_types};
1361 for(
auto&
i : damage_type_list) {
1363 if(
c.has_attribute(
"alternative_type")){
1364 alternative_damage_types.insert(
c[
"alternative_type"].str());
1368 return {replacement_type, alternative_damage_types};
1377 return damage_value;
1389 bool special_affects_opponent(
const config& special,
bool is_attacker)
1392 const std::string& apply_to = special[
"apply_to"];
1393 if ( apply_to.empty() )
1395 if ( apply_to ==
"both" )
1397 if ( apply_to ==
"opponent" )
1399 if ( is_attacker && apply_to ==
"defender" )
1401 if ( !is_attacker && apply_to ==
"attacker" )
1411 bool special_affects_self(
const config& special,
bool is_attacker)
1414 const std::string& apply_to = special[
"apply_to"];
1415 if ( apply_to.empty() )
1417 if ( apply_to ==
"both" )
1419 if ( apply_to ==
"self" )
1421 if ( is_attacker && apply_to ==
"attacker" )
1423 if ( !is_attacker && apply_to ==
"defender")
1438 static std::vector<std::tuple<std::string, std::string>> already_shown;
1440 auto identifier = std::tuple<std::string, std::string>{attack->id(),
filter.debug()};
1445 std::string_view filter_text_view = std::get<1>(identifier);
1447 ERR_NG <<
"Looped recursion error for weapon '" << attack->id()
1448 <<
"' while checking weapon special '" << filter_text_view <<
"'";
1452 if(already_shown.size() > 100) {
1453 already_shown.clear();
1455 already_shown.push_back(std::move(identifier));
1475 const bool for_listing,
1476 const std::string & child_tag,
const std::string& check_if_recursion)
1487 if (!
filter[
"backstab"].
blank() && child_tag ==
"filter_opponent") {
1488 deprecated_message(
"backstab= in weapon specials",
DEP_LEVEL::INDEFINITE,
"",
"Use [filter_opponent] with a formula instead; the code can be found in data/core/macros/ in the WEAPON_SPECIAL_BACKSTAB macro.");
1491 if(
filter[
"backstab"].to_bool() && child_tag ==
"filter_opponent"){
1492 const std::string& backstab_formula =
"enemy_of(self, flanker) and not flanker.petrified where flanker = unit_at(direction_from(loc, other.facing))";
1494 if(!
filter.has_child(
"filter_opponent")){
1495 filter_child[
"formula"] = backstab_formula;
1498 filter_opponent[
"formula"] = backstab_formula;
1499 filter_child.
add_child(
"and", filter_opponent);
1505 if ( !filter_child )
1521 filter_lock = weapon->update_variables_recursion(
filter);
1523 show_recursion_warning(weapon,
filter);
1528 if (
auto filter_weapon = filter_child->
optional_child(
"filter_weapon") ) {
1529 if ( !weapon || !weapon->matches_filter(*filter_weapon, check_if_recursion) )
1536 return ufilt.matches(*u,
loc);
1538 return ufilt.matches(*u,
loc, *u2);
1556 return special_active(*i.ability_cfg, AFFECT_SELF, ability, true);
1562 return special_active_impl(other_attack_, shared_from_this(), *i.ability_cfg, AFFECT_OTHER, ability, true);
1577 if(!abil_list.
empty() && !overwriters.
empty()){
1593 const std::string& apply_to = special[
"overwrite_specials"];
1594 return (apply_to ==
"one_side" || apply_to ==
"both_sides");
1605 if(overwriters.
empty()){
1610 if(overwriters.
size() >= 2){
1613 auto oi = (*
i.ability_cfg).optional_child(
"overwrite");
1615 if(oi && !oi[
"priority"].empty()){
1616 l = oi[
"priority"].to_double(0);
1618 auto oj = (*j.
ability_cfg).optional_child(
"overwrite");
1620 if(oj && !oj[
"priority"].empty()){
1621 r = oj[
"priority"].to_double(0);
1635 if(overwriters.
empty()){
1639 for(
const auto& j : overwriters) {
1641 bool affect_side = ((*j.ability_cfg)[
"overwrite_specials"] ==
"one_side");
1643 auto overwrite_specials = (*j.ability_cfg).optional_child(
"overwrite");
1644 double priority = overwrite_specials ? overwrite_specials[
"priority"].to_double(0) : 0.00;
1649 bool prior = (priority > 0) ? (has_overwrite_specials && has_overwrite_specials[
"priority"].to_double(0) >= priority) :
true;
1653 bool one_side_overwritable =
true;
1657 if(affect_side && is_overwritable){
1658 if(special_affects_self(*j.ability_cfg,
is_attacker_)){
1659 one_side_overwritable = special_affects_self(cfg,
is_attacker_);
1661 else if(special_affects_opponent(*j.ability_cfg, !
is_attacker_)){
1662 one_side_overwritable = special_affects_opponent(cfg, !
is_attacker_);
1667 bool special_matches =
true;
1668 if(overwrite_specials){
1669 auto overwrite_filter = (*overwrite_specials).optional_child(
"filter_specials");
1670 if(!overwrite_filter){
1671 overwrite_filter = (*overwrite_specials).optional_child(
"experimental_filter_specials");
1672 if(overwrite_filter){
1676 if(overwrite_filter && is_overwritable && one_side_overwritable){
1684 if(is_overwritable && one_side_overwritable && special_matches){
1695 show_recursion_warning(*
this, cfg);
1698 return (ability_active_impl(ability, cfg,
loc) && ability_affects_self(ability, cfg,
loc));
1705 show_recursion_warning(from, cfg);
1708 return (affects_side(cfg, side(), from.
side()) && from.
ability_active_impl(ability, cfg, from_loc) && ability_affects_adjacent(ability, cfg, dist, dir,
loc, from));
1713 return (get_self_ability_bool(special, tag_name,
loc) && ability_affects_weapon(special, weapon,
false) && ability_affects_weapon(special, opp_weapon,
true));
1718 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));
1728 if(tag_name ==
"leadership" && leader_bool){
1729 if(u->get_self_ability_bool_weapon(special, tag_name,
loc, self_attack, other_attack)) {
1733 if(u->checking_tags().count(tag_name) != 0){
1734 if(u->get_self_ability_bool(special, tag_name,
loc) &&
special_active_impl(self_attack, other_attack, special, whom, tag_name,
true)) {
1743 return check_adj_abilities_impl(shared_from_this(),
other_attack_, cfg,
self_, from, dist, dir,
self_loc_, from_loc,
AFFECT_SELF, special,
true);
1746 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)
1748 if(tag_name ==
"leadership" && leader_bool) {
1749 if(u->get_adj_ability_bool_weapon(special, tag_name, dist, dir,
loc, from, from_loc, self_attack, other_attack)) {
1753 if(u->checking_tags().count(tag_name) != 0) {
1754 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)) {
1772 if(
range().empty()){
1779 const unit_map& units = get_unit_map();
1781 for(
const config &
i :
self_->abilities().child_range(special)) {
1787 for(
const unit& u : units) {
1788 if(!u.affect_distant(special) || u.incapacitated() || &u ==
self_.get()) {
1793 if(distance > *u.affect_distant(special)) {
1796 int dir = find_direction(
self_loc_, from_loc, distance);
1797 for(
const config &
i : u.abilities().child_range(special)) {
1806 for(
const config &
i :
other_->abilities().child_range(special)) {
1812 for(
const unit& u : units) {
1813 if(!u.affect_distant(special) || u.incapacitated() || &u ==
other_.get()) {
1818 if(distance > *u.affect_distant(special)) {
1821 int dir = find_direction(
other_loc_, from_loc, distance);
1822 for(
const config &
i : u.abilities().child_range(special)) {
1823 if(
check_adj_abilities_impl(
other_attack_, shared_from_this(),
i,
other_, u, distance, dir,
other_loc_, from_loc,
AFFECT_OTHER, special)) {
1835 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)
1837 if(!filter_special.empty() && filter_special.count(special_id) == 0 && filter_special.count(tag_name) == 0)
1840 if(!filter_special_id.empty() && filter_special_id.count(special_id) == 0)
1843 if(!filter_special_type.empty() && filter_special_type.count(tag_name) == 0)
1852 if(
range().empty()){
1859 for(
const auto [key, cfg] :
specials().all_children_view()) {
1860 if(special_checking(cfg[
"id"].str(), key, filter_special, filter_special_id, filter_special_type)) {
1870 for(
const auto [key, cfg] :
other_attack_->specials().all_children_view()) {
1871 if(special_checking(cfg[
"id"].str(), key, filter_special, filter_special_id, filter_special_type)) {
1883 const unit_map& units = get_unit_map();
1885 for(
const auto [key, cfg] :
self_->abilities().all_children_view()) {
1886 if(special_checking(cfg[
"id"].str(), key, filter_special, filter_special_id, filter_special_type)) {
1892 for(
const unit& u : units) {
1893 if(!u.has_ability_distant() || u.incapacitated() || &u ==
self_.get()) {
1898 if(distance > *u.has_ability_distant()) {
1901 int dir = find_direction(
self_loc_, from_loc, distance);
1903 for(
const auto [key, cfg] : u.abilities().all_children_view()) {
1904 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)) {
1912 for(
const auto [key, cfg] :
other_->abilities().all_children_view()) {
1913 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)){
1918 for(
const unit& u : units) {
1919 if(!u.has_ability_distant() || u.incapacitated() || &u ==
other_.get()) {
1924 if(distance > *u.has_ability_distant()) {
1927 int dir = find_direction(
other_loc_, from_loc, distance);
1929 for(
const auto [key, cfg] : u.abilities().all_children_view()) {
1930 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)) {
1941 bool exclude_ability_attributes(
const std::string& tag_name,
const config &
filter)
1944 bool abilities_check = abilities_list::ability_value_tags().count(tag_name) != 0 || abilities_list::ability_no_value_tags().count(tag_name) != 0;
1945 if(
filter.has_attribute(
"active_on") && tag_name !=
"resistance" && abilities_check)
1947 if(
filter.has_attribute(
"apply_to") && tag_name !=
"resistance" && abilities_check)
1950 if(
filter.has_attribute(
"overwrite_specials") && abilities_list::weapon_number_tags().count(tag_name) == 0)
1953 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;
1954 if(
filter.has_attribute(
"value") && no_value_weapon_abilities_check)
1956 if(
filter.has_attribute(
"add") && no_value_weapon_abilities_check)
1958 if(
filter.has_attribute(
"sub") && no_value_weapon_abilities_check)
1960 if(
filter.has_attribute(
"multiply") && no_value_weapon_abilities_check)
1962 if(
filter.has_attribute(
"divide") && no_value_weapon_abilities_check)
1965 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;
1966 if(
filter.has_attribute(
"replacement_type") && tag_name !=
"damage_type" && all_engine)
1968 if(
filter.has_attribute(
"alternative_type") && tag_name !=
"damage_type" && all_engine)
1970 if(
filter.has_attribute(
"type") && tag_name !=
"plague" && all_engine)
1976 bool matches_ability_filter(
const config & cfg,
const std::string& tag_name,
const config &
filter)
1981 if(!exclude_ability_attributes(tag_name,
filter))
1987 if ( !filter_type.empty() &&
std::find(filter_type.begin(), filter_type.end(), tag_name) == filter_type.end() )
1995 if(!
filter[
"affect_adjacent"].empty()){
1996 bool adjacent = cfg.
has_child(
"affect_adjacent");
1997 if(
filter[
"affect_adjacent"].to_bool() != adjacent){
2029 if(!
filter[
"value"].empty()){
2030 if(tag_name ==
"drains"){
2034 }
else if(tag_name ==
"berserk"){
2038 }
else if(tag_name ==
"heal_on_hit" || tag_name ==
"heals" || tag_name ==
"regenerate" || tag_name ==
"leadership"){
2064 if(tag_name ==
"resistance"){
2093 auto fwml =
filter.optional_child(
"filter_wml");
2104 static bool common_matches_filter(
const config & cfg,
const std::string& tag_name,
const config &
filter)
2107 bool matches = matches_ability_filter(cfg, tag_name,
filter);
2110 for(
const auto [key, condition_cfg] :
filter.all_children_view() )
2114 matches = matches && common_matches_filter(cfg, tag_name, condition_cfg);
2117 else if ( key ==
"or" )
2118 matches = matches || common_matches_filter(cfg, tag_name, condition_cfg);
2121 else if ( key ==
"not" )
2122 matches = matches && !common_matches_filter(cfg, tag_name, condition_cfg);
2131 return common_matches_filter(cfg, tag_name,
filter);
2136 return common_matches_filter(cfg, tag_name,
filter);
2141 if(
range().empty()){
2145 bool check_if_active =
filter[
"active"].to_bool();
2146 for(
const auto [key, cfg] :
specials().all_children_view()) {
2148 if(!check_if_active) {
2158 for(
const auto [key, cfg] :
other_attack_->specials().all_children_view()) {
2166 if(!check_if_active){
2170 const unit_map& units = get_unit_map();
2171 bool check_adjacent =
filter[
"affect_adjacent"].to_bool(
true);
2173 for(
const auto [key, cfg] :
self_->abilities().all_children_view()) {
2174 if(
self_->ability_matches_filter(cfg, key,
filter)) {
2180 if(check_adjacent) {
2181 for(
const unit& u : units) {
2182 if(!u.has_ability_distant() || u.incapacitated() || &u ==
self_.get()) {
2187 if(distance > *u.has_ability_distant()) {
2190 int dir = find_direction(
self_loc_, from_loc, distance);
2192 for(
const auto [key, cfg] : u.abilities().all_children_view()) {
2202 for(
const auto [key, cfg] :
other_->abilities().all_children_view()) {
2208 if(check_adjacent) {
2209 for(
const unit& u : units) {
2210 if(!u.has_ability_distant() || u.incapacitated() || &u ==
other_.get()) {
2215 if(distance > *u.has_ability_distant()) {
2218 int dir = find_direction(
other_loc_, from_loc, distance);
2220 for(
const auto [key, cfg] : u.abilities().all_children_view()) {
2221 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)) {
2232 bool in_abilities_tag)
const
2252 const std::string& tag_name,
2253 bool in_abilities_tag)
2255 assert(self_attack || other_attack);
2256 bool is_attacker = self_attack ? self_attack->is_attacker_ : !other_attack->is_attacker_;
2257 bool is_for_listing = self_attack ? self_attack->is_for_listing_ : other_attack->is_for_listing_;
2263 if ( !special_affects_self(special, is_attacker) )
2267 if ( !special_affects_opponent(special, is_attacker) )
2272 const std::string & active_on = special[
"active_on"];
2273 if ( !active_on.empty() ) {
2274 if ( is_attacker && active_on !=
"offense" )
2276 if ( !is_attacker && active_on !=
"defense" )
2281 const unit_map& units = get_unit_map();
2283 unit_const_ptr self = self_attack ? self_attack->self_ : other_attack->other_;
2284 unit_const_ptr other = self_attack ? self_attack->other_ : other_attack->self_;
2285 map_location self_loc = self_attack ? self_attack->self_loc_ : other_attack->other_loc_;
2286 map_location other_loc = self_attack ? self_attack->other_loc_ : other_attack->self_loc_;
2288 if(
self ==
nullptr) {
2294 if(other ==
nullptr) {
2303 temporary_facing other_facing(other, other_loc.
get_relative_dir(self_loc));
2307 bool whom_is_self = ((whom ==
AFFECT_SELF) || ((whom ==
AFFECT_EITHER) && special_affects_self(special, is_attacker)));
2309 map_location their_loc = whom_is_self ? other_loc : self_loc;
2311 if (tag_name ==
"drains" && them && them->get_state(
"undrainable")) {
2314 if (tag_name ==
"plague" && them &&
2315 (them->get_state(
"unplagueable") ||
2319 if (tag_name ==
"poison" && them &&
2323 if (tag_name ==
"slow" && them &&
2327 if (tag_name ==
"petrifies" && them &&
2328 them->get_state(
"unpetrifiable")) {
2336 const map_location & att_loc = is_attacker ? self_loc : other_loc;
2337 const map_location & def_loc = is_attacker ? other_loc : self_loc;
2338 const const_attack_ptr& att_weapon = is_attacker ? self_attack : other_attack;
2339 const const_attack_ptr& def_weapon = is_attacker ? other_attack : self_attack;
2343 if (tag_name ==
"firststrike") {
2344 bool whom_is_defender = whom_is_self ? !is_attacker : is_attacker;
2345 if (whom_is_defender && att_weapon && att_weapon->has_special_or_ability(
"firststrike"))
2354 bool applied_both = special[
"apply_to"] ==
"both";
2355 const std::string& filter_self = in_abilities_tag ?
"filter_student" :
"filter_self";
2356 std::string self_check_if_recursion = (applied_both || whom_is_self) ? tag_name :
"";
2357 if (!special_unit_matches(
self, other, self_loc, self_attack, special, is_for_listing, filter_self, self_check_if_recursion))
2359 std::string opp_check_if_recursion = (applied_both || !whom_is_self) ? tag_name :
"";
2360 if (!special_unit_matches(other,
self, other_loc, other_attack, special, is_for_listing,
"filter_opponent", opp_check_if_recursion))
2364 bool applied_to_attacker = applied_both || (whom_is_self && is_attacker) || (!whom_is_self && !is_attacker);
2365 std::string att_check_if_recursion = applied_to_attacker ? tag_name :
"";
2366 if (!special_unit_matches(att, def, att_loc, att_weapon, special, is_for_listing,
"filter_attacker", att_check_if_recursion))
2368 bool applied_to_defender = applied_both || (whom_is_self && !is_attacker) || (!whom_is_self && is_attacker);
2369 std::string def_check_if_recursion= applied_to_defender ? tag_name :
"";
2370 if (!special_unit_matches(def, att, def_loc, def_weapon, special, is_for_listing,
"filter_defender", def_check_if_recursion))
2396 if (
auto apply_filter = cfg.
optional_child(
"filter_base_value")) {
2403 return (cond_eq.
empty() || def == cond_eq.
to_int()) &&
2416 if(tag_name ==
"plague") {
2418 const auto iter =
unit_types.
types().find(ability_or_special[
"type"]);
2439 std::map<std::string,individual_effect> values_add;
2440 std::map<std::string,individual_effect> values_sub;
2441 std::map<std::string,individual_effect> values_mul;
2442 std::map<std::string,individual_effect> values_div;
2446 utils::optional<int> max_value = utils::nullopt;
2447 utils::optional<int> min_value = utils::nullopt;
2450 const config& cfg = *ability.ability_cfg;
2451 const std::string& effect_id = cfg[cfg[
"id"].
empty() ?
"name" :
"id"];
2459 callable.add(
"base_value", wfl::variant(def));
2460 return std::round(formula.evaluate(callable).as_int());
2463 int value_cum = cfg[
"cumulative"].to_bool() ? std::max(def, value) : value;
2466 set_effect_min.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2467 set_effect_max.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2470 if(value_cum > set_effect_max.
value) {
2471 set_effect_max.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2473 if(value_cum < set_effect_min.
value) {
2474 set_effect_min.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2482 max_value = max_value ? std::min(*max_value, cfg[
"max_value"].to_int()) : cfg[
"max_value"].to_int();
2485 min_value = min_value ? std::max(*min_value, cfg[
"min_value"].to_int()) : cfg[
"min_value"].to_int();
2491 callable.add(
"base_value", wfl::variant(def));
2492 return std::round(formula.evaluate(callable).as_int());
2495 if(add_effect == values_add.end() || add > add_effect->second.value) {
2496 values_add[effect_id].set(
ADD, add, ability.ability_cfg, ability.teacher_loc);
2501 callable.add(
"base_value", wfl::variant(def));
2502 return std::round(formula.evaluate(callable).as_int());
2505 if(sub_effect == values_sub.end() || sub < sub_effect->second.value) {
2506 values_sub[effect_id].set(
ADD, sub, ability.ability_cfg, ability.teacher_loc);
2511 callable.add(
"base_value", wfl::variant(def));
2512 return formula.evaluate(callable).as_decimal() / 1000.0 ;
2515 if(mul_effect == values_mul.end() || multiply > mul_effect->second.value) {
2516 values_mul[effect_id].set(
MUL, multiply, ability.ability_cfg, ability.teacher_loc);
2521 callable.add(
"base_value", wfl::variant(def));
2522 return formula.evaluate(callable).as_decimal() / 1000.0 ;
2526 ERR_NG <<
"division by zero with divide= in ability/weapon special " << effect_id;
2530 if(div_effect == values_div.end() || divide > div_effect->second.value) {
2531 values_div[effect_id].set(
DIV, divide, ability.ability_cfg, ability.teacher_loc);
2538 value_set = std::max(set_effect_max.
value, 0) + std::min(set_effect_min.
value, 0);
2539 if(set_effect_max.
value > def) {
2542 if(set_effect_min.
value < def) {
2555 double multiplier = 1.0;
2556 double divisor = 1.0;
2558 for(
const auto& val : values_mul) {
2559 multiplier *= val.second.value/100.0;
2563 for(
const auto& val : values_div) {
2564 divisor *= val.second.value/100.0;
2569 for(
const auto& val : values_add) {
2570 addition += val.second.value;
2576 int substraction = 0;
2577 for(
const auto& val : values_sub) {
2578 substraction += val.second.value;
2584 if(max_value && min_value && *min_value < *max_value) {
2586 }
else if(max_value && !min_value) {
2588 }
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 bool ability_active_adjacent_helper(const unit &self, bool illuminates, const config &cfg, const map_location &loc, bool in_abilities_tag)
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
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.
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 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
std::string substitute_variables(const std::string &str, const std::string &tag_name, const config &ability_or_special)
Substitute gettext variables in name and description of abilities and specials.
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::string join(const T &v, const std::string &s=",")
Generates a new string joining container items in a list.
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