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;
224 const unit_map& units = get_unit_map();
230 for(
const unit& u : units) {
231 if(!u.has_ability_distant() || u.incapacitated() || same_unit(u, *
this) || !u.affect_distant(tag_name)) {
236 if(distance > *u.affect_distant(tag_name)) {
239 int dir = find_direction(
loc, from_loc, distance);
240 for(
const config&
i : u.abilities_.child_range(tag_name)) {
266 const unit_map& units = get_unit_map();
272 for(
const unit& u : units) {
273 if(!u.has_ability_distant() || u.incapacitated() || same_unit(u, *
this) || !u.affect_distant(tag_name)) {
278 if(distance > *u.affect_distant(tag_name)) {
281 int dir = find_direction(
loc, from_loc, distance);
282 for(
const config&
i : u.abilities_.child_range(tag_name)) {
304 std::vector<std::string> res;
307 std::string
id =
cfg[
"id"];
309 res.push_back(std::move(
id));
322 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)
339 "female_name_inactive",
"name_inactive");
360 std::vector<std::tuple<std::string, t_string,t_string,t_string>> res;
364 add_ability_tooltip(
cfg, tag_name,
gender_, res,
true);
372 std::vector<std::tuple<std::string, t_string,t_string,t_string>> res;
378 if(add_ability_tooltip(
cfg, tag_name,
gender_, res, active))
380 active_list.push_back(active);
397 static std::vector<std::tuple<std::string, std::string>> already_shown;
399 auto identifier = std::tuple<std::string, std::string>{
unit.
id(),
filter.debug()};
404 std::string_view filter_text_view = std::get<1>(identifier);
406 ERR_NG <<
"Looped recursion error for unit '" <<
unit.
id()
407 <<
"' while checking ability '" << filter_text_view <<
"'";
411 if(already_shown.size() > 100) {
412 already_shown.clear();
414 already_shown.push_back(std::move(identifier));
429 : parent(u.shared_from_this())
439 unit::recursion_guard::operator bool()
const {
445 assert(
this != &other);
454 assert(!parent->open_queries_.empty());
455 parent->open_queries_.pop_back();
463 show_recursion_warning(*
this,
cfg);
473 const unit_map& units = get_unit_map();
476 const std::string& filter_adjacent = in_abilities_tag ?
"filter_adjacent_student" :
"filter_adjacent";
477 const std::string& filter_adjacent_location = in_abilities_tag ?
"filter_adjacent_student_location" :
"filter_adjacent_location";
480 std::size_t radius =
i[
"radius"].to_int(1);
481 std::size_t count = 0;
483 ufilt.set_use_flat_tod(illuminates);
484 for(
const unit& u : units) {
487 if(same_unit(u,
self) || distance > radius || !ufilt(u,
self)) {
491 for(
unsigned j = 0; j < adjacent.size(); ++j) {
492 bool adj_or_dist = distance != 1 ?
distance_between(adjacent[j], from_loc) == (distance - 1) : adjacent[j] == from_loc;
498 assert(dir >= 0 && dir <= 5);
500 if(
i.has_attribute(
"adjacent")) {
505 if(
i.has_attribute(
"is_enemy")) {
514 if(
i[
"count"].empty() && count == 0) {
523 std::size_t count = 0;
525 adj_filter.flatten(illuminates);
529 if(!adj_filter.match(adjacent[
static_cast<int>(
index)])) {
546 bool illuminates = ability ==
"illuminates";
564 bool illuminates = ability ==
"illuminates";
566 assert(dir >=0 && dir <= 5);
568 std::size_t radius = 1;
572 if(
i[
"radius"] !=
"all_map") {
573 radius =
i[
"radius"].to_int(1);
581 if (
i.has_attribute(
"adjacent")) {
586 auto filter =
i.optional_child(
"filter");
598 bool affect_self =
cfg[
"affect_self"].to_bool(
true);
599 if (!
filter || !affect_self)
return affect_self;
605 const std::string filter_tag_name = is_opp ?
"filter_second_weapon" :
"filter_weapon";
614 filter_lock = weapon->update_variables_recursion(
cfg);
616 show_recursion_warning(*
this,
cfg);
619 return weapon->matches_filter(
filter);
632 image_list.push_back(
cfg[attribute_name].str());
638 std::vector<std::string> image_list;
646 if(!
cfg[image_type +
"_image_self"].str().empty() &&
is_active){
651 const unit_map& units = get_unit_map();
653 for(
const unit& u : units) {
654 if(!u.has_ability_distant_image() || u.incapacitated() || same_unit(u, *
this)) {
659 if(distance > *u.has_ability_distant_image()) {
662 int dir = find_direction(
loc_, from_loc, distance);
663 for(
const auto [key,
cfg] : u.abilities_.all_children_view()) {
671 if(image_list.size() >= 2){
672 std::sort(image_list.begin(), image_list.end());
680 callable.
add(
"attacker",
wfl::variant(std::make_shared<wfl::unit_callable>(*att)));
683 callable.
add(
"defender",
wfl::variant(std::make_shared<wfl::unit_callable>(*def)));
690 template<
typename T,
typename TFuncFormula>
691 class get_ability_value_visitor
692 #ifdef USING_BOOST_VARIANT
693 :
public boost::static_visitor<T>
698 get_ability_value_visitor(T def,
const TFuncFormula& formula_handler) : def_(def), formula_handler_(formula_handler) {}
700 T operator()(
const utils::monostate&)
const {
return def_; }
701 T operator()(
bool)
const {
return def_; }
702 T operator()(
int i)
const {
return static_cast<T
>(
i); }
703 T operator()(
unsigned long long u)
const {
return static_cast<T
>(u); }
704 T operator()(
double d)
const {
return static_cast<T
>(
d); }
705 T operator()(
const t_string&)
const {
return def_; }
706 T operator()(
const std::string&
s)
const
708 if(
s.size() >= 2 &&
s[0] ==
'(') {
709 return formula_handler_(
s);
711 return lexical_cast_default<T>(
s, def_);
716 const TFuncFormula& formula_handler_;
719 template<
typename T,
typename TFuncFormula>
722 return v.
apply_visitor(get_ability_value_visitor(def, [&](
const std::string&
s) {
725 const unit_map& units = get_unit_map();
729 if(u_itor == units.
end()) {
734 att->add_formula_context(callable);
737 callable.add(
"student",
wfl::variant(std::make_shared<wfl::unit_callable>(*uptr)));
740 callable.add(
"other",
wfl::variant(std::make_shared<wfl::unit_callable>(*uptr)));
744 lg::log_to_chat() <<
"Formula error in ability or weapon special: " <<
e.type <<
" at " <<
e.filename <<
':' <<
e.line <<
")\n";
745 ERR_WML <<
"Formula error in ability or weapon special: " <<
e.type <<
" at " <<
e.filename <<
':' <<
e.line <<
")";
752 template<
typename TComp>
755 if ( cfgs_.empty() ) {
761 bool only_cumulative =
true;
771 if ((*
p.ability_cfg)[
"cumulative"].to_bool()) {
773 if (value < 0) value = -value;
774 if (only_cumulative && !comp(value, abs_max)) {
776 best_loc =
p.teacher_loc;
778 }
else if (only_cumulative || comp(flat, value)) {
779 only_cumulative =
false;
781 best_loc =
p.teacher_loc;
784 return std::pair(flat + stack, best_loc);
787 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;
788 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;
827 if(simple_check && specials().has_child(special)) {
831 for(
const config &
i : specials().child_range(special)) {
832 if(special_active(
i, AFFECT_SELF, special)) {
838 if ( simple_check || !other_attack_ ) {
842 for(
const config &
i : other_attack_->specials().child_range(special)) {
843 if(other_attack_->special_active(
i, AFFECT_OTHER, special)) {
857 bool inverse_affect = abilities_list::weapon_inverse_affect_tags().count(special) != 0;
861 for(
const config&
i : specials_.child_range(special)) {
862 if(special_active(
i, inverse_affect ? AFFECT_OTHER : AFFECT_SELF, special)) {
871 for(
const config&
i : other_attack_->specials_.child_range(special)) {
872 if(other_attack_->special_active(
i, inverse_affect ? AFFECT_SELF : AFFECT_OTHER, special)) {
889 boost::dynamic_bitset<>* active_list)
const
892 std::vector<std::pair<t_string, t_string>> res;
894 active_list->clear();
897 for(
const auto [key,
cfg] : specials_.all_children_view()) {
898 bool active = !active_list || special_active(
cfg, AFFECT_EITHER, key);
900 std::string
name = active
908 std::string desc = active
909 ?
cfg[
"description"].str()
910 :
cfg.
get_or(
"description_inactive",
"description").
str();
918 active_list->push_back(active);
925 boost::dynamic_bitset<>* active_list)
const
927 std::vector<std::pair<t_string, t_string>> res;
929 active_list->clear();
931 std::set<std::string> checking_name;
935 for(
const auto [key,
cfg] : self_->abilities().all_children_view()) {
936 if(!active_list || check_self_abilities_impl(shared_from_this(), other_attack_,
cfg, self_, self_loc_, AFFECT_SELF, key,
false)) {
937 const std::string
name =
cfg[
"name_affected"];
938 const std::string desc =
cfg[
"description_affected"];
943 res.emplace_back(
name, desc);
946 active_list->push_back(
true);
950 for(
const unit& u : get_unit_map()) {
951 if(!u.has_ability_distant() || u.incapacitated() || same_unit(u, *self_)) {
956 if(distance > *u.has_ability_distant()) {
959 int dir = find_direction(self_loc_, from_loc, distance);
960 for(
const auto [key,
cfg] : u.abilities().all_children_view()) {
961 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)) {
962 const std::string
name =
cfg[
"name_affected"];
963 const std::string desc =
cfg[
"description_affected"];
968 res.emplace_back(
name, desc);
971 active_list->push_back(
true);
987 static void add_name(std::string& temp_string,
bool active,
const std::string&
name, std::set<std::string>& checking_name)
989 if (active && !
name.
empty() && checking_name.count(
name) == 0) {
990 checking_name.insert(
name);
991 if (!temp_string.empty()) temp_string +=
", ";
1006 std::vector<std::string> specials;
1008 for(
const auto [key,
cfg] : specials_.all_children_view()) {
1009 const bool active = special_active(
cfg, AFFECT_EITHER, key);
1011 std::string
name = active
1025 std::string temp_string;
1026 std::set<std::string> checking_name;
1027 weapon_specials_impl_self(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name);
1028 weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name, {},
"affect_allies");
1030 if(!temp_string.empty()) {
1031 specials.push_back(
"\n" + std::move(temp_string));
1037 static void add_name_list(std::string& temp_string, std::string& weapon_abilities, std::set<std::string>& checking_name,
const std::string& from_str)
1039 if(!temp_string.empty()){
1040 temp_string = from_str.c_str() + temp_string;
1041 weapon_abilities += (!weapon_abilities.empty() && !temp_string.empty()) ?
"\n" :
"";
1042 weapon_abilities += temp_string;
1043 temp_string.clear();
1044 checking_name.clear();
1051 std::string temp_string, weapon_abilities;
1052 std::set<std::string> checking_name;
1053 for(
const auto [key,
cfg] : specials_.all_children_view()) {
1054 if(checking_tags.count(key) != 0) {
1055 const bool active = special_active(
cfg, AFFECT_SELF, key);
1056 add_name(temp_string, active,
cfg[
"name"].str(), checking_name);
1059 add_name_list(temp_string, weapon_abilities, checking_name,
"");
1061 weapon_specials_impl_self(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name, checking_tags,
true);
1062 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Owned: "));
1064 weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name, checking_tags,
"affect_allies",
true);
1066 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Taught: "));
1068 weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name, checking_tags,
"affect_enemies",
true);
1070 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Taught: (by an enemy): "));
1074 for(
const auto [key,
cfg] : other_attack_->specials_.all_children_view()) {
1075 if((checking_tags.count(key) != 0)){
1076 const bool active = other_attack_->special_active(
cfg, AFFECT_OTHER, key);
1077 add_name(temp_string, active,
cfg[
"name"].str(), checking_name);
1081 weapon_specials_impl_self(temp_string, other_, other_attack_, shared_from_this(), other_loc_, AFFECT_OTHER, checking_name, checking_tags);
1082 weapon_specials_impl_adj(temp_string, other_, other_attack_, shared_from_this(), other_loc_, AFFECT_OTHER, checking_name, checking_tags);
1083 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Used by opponent: "));
1085 return weapon_abilities;
1089 std::string& temp_string,
1095 std::set<std::string>& checking_name,
1096 const std::set<std::string>& checking_tags,
1100 for(
const auto [key,
cfg] : self->abilities().all_children_view()){
1101 bool tag_checked = (!checking_tags.empty()) ? (checking_tags.count(key) != 0) :
true;
1102 const bool active = tag_checked && check_self_abilities_impl(self_attack, other_attack,
cfg,
self, self_loc, whom, key, leader_bool);
1109 std::string& temp_string,
1115 std::set<std::string>& checking_name,
1116 const std::set<std::string>& checking_tags,
1117 const std::string& affect_adjacents,
1120 const unit_map& units = get_unit_map();
1122 for(
const unit& u : units) {
1123 if(!u.has_ability_distant() || u.incapacitated() || same_unit(u, *
self)) {
1128 if(distance > *u.has_ability_distant()) {
1131 int dir = find_direction(self_loc, from_loc, distance);
1132 for(
const auto [key,
cfg] : u.abilities().all_children_view()) {
1133 bool tag_checked = !checking_tags.empty() ? checking_tags.count(key) != 0 :
true;
1134 bool default_bool = affect_adjacents ==
"affect_allies" ? true :
false;
1135 bool affect_allies = !affect_adjacents.empty() ?
cfg[affect_adjacents].to_bool(default_bool) :
true;
1136 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;
1163 : parent(weapon.shared_from_this())
1165 weapon.
self_ = std::move(
self);
1166 weapon.
other_ = std::move(other);
1183 : parent(weapon.shared_from_this())
1185 weapon.
self_ = std::move(
self);
1203 : parent(weapon.shared_from_this())
1215 : parent(weapon.shared_from_this())
1223 if(was_moved)
return;
1228 parent->is_attacker_ =
false;
1229 parent->other_attack_ =
nullptr;
1230 parent->is_for_listing_ =
false;
1234 : parent(std::move(other.parent))
1236 other.was_moved =
true;
1247 unsigned & max_attacks)
const
1252 if ( attacks_value < 0 ) {
1254 ERR_NG <<
"negative number of strikes after applying weapon specials";
1259 if ( !swarm_specials.
empty() ) {
1260 min_attacks = std::max<int>(0, swarm_specials.
highest(
"swarm_attacks_min").first);
1261 max_attacks = std::max<int>(0, swarm_specials.
highest(
"swarm_attacks_max", attacks_value).first);
1263 min_attacks = max_attacks = attacks_value;
1269 std::map<std::string, unsigned int> type_count;
1270 unsigned int max = 0;
1271 for(
auto&
i : damage_type_list) {
1273 if(
c.has_attribute(
"replacement_type")) {
1274 std::string
type =
c[
"replacement_type"].str();
1275 unsigned int count = ++type_count[
type];
1282 if (type_count.empty())
return type();
1284 std::vector<std::string> type_list;
1285 for(
auto&
i : type_count){
1286 if(
i.second == max){
1287 type_list.push_back(
i.first);
1291 if(type_list.empty())
return type();
1293 return type_list.front();
1298 std::map<std::string, int> type_res;
1299 int max_res = INT_MIN;
1301 for(
auto&
i : damage_type_list) {
1303 if(
c.has_attribute(
"alternative_type")) {
1304 std::string
type =
c[
"alternative_type"].str();
1305 if(type_res.count(
type) == 0){
1306 type_res[
type] = (*other_).resistance_value(resistance_list,
type);
1307 max_res = std::max(max_res, type_res[
type]);
1313 if (type_res.empty())
return {
"", INT_MIN};
1315 std::vector<std::string> type_list;
1316 for(
auto&
i : type_res){
1317 if(
i.second == max_res){
1318 type_list.push_back(
i.first);
1321 if(type_list.empty())
return {
"", INT_MIN};
1323 return {type_list.front(), max_res};
1336 resistance_list = (*other_).get_abilities_weapons(
"resistance",
other_loc_,
other_attack_, shared_from_this());
1338 return (!((*
i.ability_cfg)[
"active_on"].empty() || (!
is_attacker_ && (*
i.ability_cfg)[
"active_on"] ==
"offense") || (
is_attacker_ && (*
i.ability_cfg)[
"active_on"] ==
"defense")));
1342 int res =
other_ ? (*other_).resistance_value(resistance_list,
type()) : 100;
1343 if(damage_type_list.
empty()){
1344 return {
type(), res};
1350 res = replacement_type !=
type() ? (*other_).resistance_value(resistance_list, replacement_type) : res;
1351 replacement_type = alternative_type.second > res ? alternative_type.first : replacement_type;
1352 res = std::max(res, alternative_type.second);
1354 return {replacement_type, res};
1363 std::set<std::string> alternative_damage_types;
1364 if(damage_type_list.
empty()){
1365 return {
type(), alternative_damage_types};
1368 for(
auto&
i : damage_type_list) {
1370 if(
c.has_attribute(
"alternative_type")){
1371 alternative_damage_types.insert(
c[
"alternative_type"].str());
1375 return {replacement_type, alternative_damage_types};
1384 return damage_value;
1405 bool special_affects_opponent(
const config& special,
bool is_attacker)
1408 const std::string& apply_to = special[
"apply_to"];
1409 if ( apply_to.empty() )
1411 if ( apply_to ==
"both" )
1413 if ( apply_to ==
"opponent" )
1415 if ( is_attacker && apply_to ==
"defender" )
1417 if ( !is_attacker && apply_to ==
"attacker" )
1427 bool special_affects_self(
const config& special,
bool is_attacker)
1430 const std::string& apply_to = special[
"apply_to"];
1431 if ( apply_to.empty() )
1433 if ( apply_to ==
"both" )
1435 if ( apply_to ==
"self" )
1437 if ( is_attacker && apply_to ==
"attacker" )
1439 if ( !is_attacker && apply_to ==
"defender")
1454 static std::vector<std::tuple<std::string, std::string>> already_shown;
1456 auto identifier = std::tuple<std::string, std::string>{attack->id(),
filter.debug()};
1461 std::string_view filter_text_view = std::get<1>(identifier);
1463 ERR_NG <<
"Looped recursion error for weapon '" << attack->id()
1464 <<
"' while checking weapon special '" << filter_text_view <<
"'";
1468 if(already_shown.size() > 100) {
1469 already_shown.clear();
1471 already_shown.push_back(std::move(identifier));
1491 const bool for_listing,
1492 const std::string & child_tag,
const std::string& check_if_recursion)
1503 if (!
filter[
"backstab"].
blank() && child_tag ==
"filter_opponent") {
1504 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.");
1507 if(
filter[
"backstab"].to_bool() && child_tag ==
"filter_opponent"){
1508 const std::string& backstab_formula =
"enemy_of(self, flanker) and not flanker.petrified where flanker = unit_at(direction_from(loc, other.facing))";
1510 if(!
filter.has_child(
"filter_opponent")){
1511 filter_child[
"formula"] = backstab_formula;
1514 filter_opponent[
"formula"] = backstab_formula;
1515 filter_child.
add_child(
"and", filter_opponent);
1521 if ( !filter_child )
1537 filter_lock = weapon->update_variables_recursion(
filter);
1539 show_recursion_warning(weapon,
filter);
1544 if (
auto filter_weapon = filter_child->
optional_child(
"filter_weapon") ) {
1545 if ( !weapon || !weapon->matches_filter(*filter_weapon, check_if_recursion) )
1552 return ufilt.matches(*u,
loc);
1554 return ufilt.matches(*u,
loc, *u2);
1568 bool inverse_affect = abilities_list::weapon_inverse_affect_tags().count(ability) != 0;
1573 return special_active(*i.ability_cfg, inverse_affect ? AFFECT_OTHER : AFFECT_SELF, ability, true);
1579 return special_active_impl(other_attack_, shared_from_this(), *i.ability_cfg, inverse_affect ? AFFECT_SELF : AFFECT_OTHER, ability, true);
1594 if(!abil_list.
empty() && !overwriters.
empty()){
1610 const std::string& apply_to = special[
"overwrite_specials"];
1611 return (apply_to ==
"one_side" || apply_to ==
"both_sides");
1622 if(overwriters.
empty()){
1627 if(overwriters.
size() >= 2){
1630 auto oi = (*
i.ability_cfg).optional_child(
"overwrite");
1632 if(oi && !oi[
"priority"].empty()){
1633 l = oi[
"priority"].to_double(0);
1635 auto oj = (*j.
ability_cfg).optional_child(
"overwrite");
1637 if(oj && !oj[
"priority"].empty()){
1638 r = oj[
"priority"].to_double(0);
1652 if(overwriters.
empty()){
1656 for(
const auto& j : overwriters) {
1658 bool affect_side = ((*j.ability_cfg)[
"overwrite_specials"] ==
"one_side");
1660 auto overwrite_specials = (*j.ability_cfg).optional_child(
"overwrite");
1661 double priority = overwrite_specials ? overwrite_specials[
"priority"].to_double(0) : 0.00;
1666 bool prior = (priority > 0) ? (has_overwrite_specials && has_overwrite_specials[
"priority"].to_double(0) >= priority) :
true;
1670 bool one_side_overwritable =
true;
1674 if(affect_side && is_overwritable){
1675 if(special_affects_self(*j.ability_cfg,
is_attacker_)){
1678 else if(special_affects_opponent(*j.ability_cfg, !
is_attacker_)){
1684 bool special_matches =
true;
1685 if(overwrite_specials){
1686 auto overwrite_filter = (*overwrite_specials).optional_child(
"filter_specials");
1687 if(!overwrite_filter){
1688 overwrite_filter = (*overwrite_specials).optional_child(
"experimental_filter_specials");
1689 if(overwrite_filter){
1693 if(overwrite_filter && is_overwritable && one_side_overwritable){
1701 if(is_overwritable && one_side_overwritable && special_matches){
1712 show_recursion_warning(*
this,
cfg);
1715 return (ability_active_impl(ability,
cfg,
loc) && ability_affects_self(ability,
cfg,
loc));
1722 show_recursion_warning(from,
cfg);
1730 return (get_self_ability_bool(special, tag_name,
loc) && ability_affects_weapon(special, weapon,
false) && ability_affects_weapon(special, opp_weapon,
true));
1735 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));
1745 if(tag_name ==
"leadership" && leader_bool){
1746 if(u->get_self_ability_bool_weapon(special, tag_name,
loc, self_attack, other_attack)) {
1750 if(abilities_list::all_weapon_tags().count(tag_name) != 0){
1751 if(u->get_self_ability_bool(special, tag_name,
loc) &&
special_active_impl(self_attack, other_attack, special, whom, tag_name,
true)) {
1760 return check_adj_abilities_impl(shared_from_this(),
other_attack_,
cfg,
self_, from, dist, dir,
self_loc_, from_loc,
AFFECT_SELF, special,
true);
1763 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)
1765 if(tag_name ==
"leadership" && leader_bool) {
1766 if(u->get_adj_ability_bool_weapon(special, tag_name, dist, dir,
loc, from, from_loc, self_attack, other_attack)) {
1770 if(abilities_list::all_weapon_tags().count(tag_name) != 0) {
1771 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)) {
1789 if(
range().empty()){
1796 const unit_map& units = get_unit_map();
1798 for(
const config &
i :
self_->abilities().child_range(special)) {
1804 for(
const unit& u : units) {
1805 if(!u.affect_distant(special) || u.incapacitated() || same_unit(u, *
self_)) {
1810 if(distance > *u.affect_distant(special)) {
1813 int dir = find_direction(
self_loc_, from_loc, distance);
1814 for(
const config &
i : u.abilities().child_range(special)) {
1823 for(
const config &
i :
other_->abilities().child_range(special)) {
1829 for(
const unit& u : units) {
1830 if(!u.affect_distant(special) || u.incapacitated() || same_unit(u, *
other_)) {
1835 if(distance > *u.affect_distant(special)) {
1838 int dir = find_direction(
other_loc_, from_loc, distance);
1839 for(
const config &
i : u.abilities().child_range(special)) {
1840 if(
check_adj_abilities_impl(
other_attack_, shared_from_this(),
i,
other_, u, distance, dir,
other_loc_, from_loc,
AFFECT_OTHER, special)) {
1852 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)
1854 if(!filter_special.empty() && filter_special.count(special_id) == 0 && filter_special.count(tag_name) == 0)
1857 if(!filter_special_id.empty() && filter_special_id.count(special_id) == 0)
1860 if(!filter_special_type.empty() && filter_special_type.count(tag_name) == 0)
1869 if(
range().empty()){
1876 for(
const auto [key,
cfg] :
specials().all_children_view()) {
1877 if(special_checking(
cfg[
"id"].str(), key, filter_special, filter_special_id, filter_special_type)) {
1887 for(
const auto [key,
cfg] :
other_attack_->specials().all_children_view()) {
1888 if(special_checking(
cfg[
"id"].str(), key, filter_special, filter_special_id, filter_special_type)) {
1900 const unit_map& units = get_unit_map();
1902 for(
const auto [key,
cfg] :
self_->abilities().all_children_view()) {
1903 if(special_checking(
cfg[
"id"].str(), key, filter_special, filter_special_id, filter_special_type)) {
1909 for(
const unit& u : units) {
1910 if(!u.has_ability_distant() || u.incapacitated() || same_unit(u, *
self_)) {
1915 if(distance > *u.has_ability_distant()) {
1918 int dir = find_direction(
self_loc_, from_loc, distance);
1920 for(
const auto [key,
cfg] : u.abilities().all_children_view()) {
1921 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)) {
1929 for(
const auto [key,
cfg] :
other_->abilities().all_children_view()) {
1930 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)){
1935 for(
const unit& u : units) {
1936 if(!u.has_ability_distant() || u.incapacitated() || same_unit(u, *
other_)) {
1941 if(distance > *u.has_ability_distant()) {
1944 int dir = find_direction(
other_loc_, from_loc, distance);
1946 for(
const auto [key,
cfg] : u.abilities().all_children_view()) {
1947 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)) {
1958 bool exclude_ability_attributes(
const std::string& tag_name,
const config &
filter)
1961 bool abilities_check = abilities_list::ability_value_tags().count(tag_name) != 0 || abilities_list::ability_no_value_tags().count(tag_name) != 0;
1962 if(
filter.has_attribute(
"active_on") && tag_name !=
"resistance" && abilities_check)
1964 if(
filter.has_attribute(
"apply_to") && tag_name !=
"resistance" && abilities_check)
1967 if(
filter.has_attribute(
"overwrite_specials") && abilities_list::weapon_number_tags().count(tag_name) == 0)
1970 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;
1971 if(
filter.has_attribute(
"value") && no_value_weapon_abilities_check)
1973 if(
filter.has_attribute(
"add") && no_value_weapon_abilities_check)
1975 if(
filter.has_attribute(
"sub") && no_value_weapon_abilities_check)
1977 if(
filter.has_attribute(
"multiply") && no_value_weapon_abilities_check)
1979 if(
filter.has_attribute(
"divide") && no_value_weapon_abilities_check)
1982 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;
1983 if(
filter.has_attribute(
"replacement_type") && tag_name !=
"damage_type" && all_engine)
1985 if(
filter.has_attribute(
"alternative_type") && tag_name !=
"damage_type" && all_engine)
1987 if(
filter.has_attribute(
"type") && tag_name !=
"plague" && all_engine)
1998 if(!exclude_ability_attributes(tag_name,
filter))
2012 if(!
filter[
"affect_adjacent"].empty()){
2014 if(
filter[
"affect_adjacent"].to_bool() != adjacent){
2046 if(!
filter[
"value"].empty()){
2047 if(tag_name ==
"drains"){
2051 }
else if(tag_name ==
"berserk"){
2055 }
else if(tag_name ==
"heal_on_hit" || tag_name ==
"heals" || tag_name ==
"regenerate" || tag_name ==
"leadership"){
2081 if(tag_name ==
"resistance"){
2110 auto fwml =
filter.optional_child(
"filter_wml");
2121 static bool common_matches_filter(
const config &
cfg,
const std::string& tag_name,
const config &
filter)
2124 bool matches = matches_ability_filter(
cfg, tag_name,
filter);
2127 for(
const auto [key, condition_cfg] :
filter.all_children_view() )
2131 matches = matches && common_matches_filter(
cfg, tag_name, condition_cfg);
2134 else if ( key ==
"or" )
2135 matches = matches || common_matches_filter(
cfg, tag_name, condition_cfg);
2138 else if ( key ==
"not" )
2139 matches = matches && !common_matches_filter(
cfg, tag_name, condition_cfg);
2148 return common_matches_filter(
cfg, tag_name,
filter);
2153 return common_matches_filter(
cfg, tag_name,
filter);
2158 if(
range().empty()){
2162 bool check_if_active =
filter[
"active"].to_bool();
2163 for(
const auto [key,
cfg] :
specials().all_children_view()) {
2165 if(!check_if_active) {
2175 for(
const auto [key,
cfg] :
other_attack_->specials().all_children_view()) {
2183 if(!check_if_active){
2187 const unit_map& units = get_unit_map();
2188 bool check_adjacent =
filter[
"affect_adjacent"].to_bool(
true);
2190 for(
const auto [key,
cfg] :
self_->abilities().all_children_view()) {
2197 if(check_adjacent) {
2198 for(
const unit& u : units) {
2199 if(!u.has_ability_distant() || u.incapacitated() || same_unit(u, *
self_)) {
2204 if(distance > *u.has_ability_distant()) {
2207 int dir = find_direction(
self_loc_, from_loc, distance);
2209 for(
const auto [key,
cfg] : u.abilities().all_children_view()) {
2219 for(
const auto [key,
cfg] :
other_->abilities().all_children_view()) {
2225 if(check_adjacent) {
2226 for(
const unit& u : units) {
2227 if(!u.has_ability_distant() || u.incapacitated() || same_unit(u, *
other_)) {
2232 if(distance > *u.has_ability_distant()) {
2235 int dir = find_direction(
other_loc_, from_loc, distance);
2237 for(
const auto [key,
cfg] : u.abilities().all_children_view()) {
2238 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)) {
2249 bool in_abilities_tag)
const
2269 const std::string& tag_name,
2270 bool in_abilities_tag)
2272 assert(self_attack || other_attack);
2273 bool is_attacker = self_attack ? self_attack->is_attacker_ : !other_attack->is_attacker_;
2274 bool is_for_listing = self_attack ? self_attack->is_for_listing_ : other_attack->is_for_listing_;
2280 if ( !special_affects_self(special, is_attacker) )
2284 if ( !special_affects_opponent(special, is_attacker) )
2289 const std::string & active_on = special[
"active_on"];
2290 if ( !active_on.empty() ) {
2291 if ( is_attacker && active_on !=
"offense" )
2293 if ( !is_attacker && active_on !=
"defense" )
2298 const unit_map& units = get_unit_map();
2300 unit_const_ptr self = self_attack ? self_attack->self_ : other_attack->other_;
2301 unit_const_ptr other = self_attack ? self_attack->other_ : other_attack->self_;
2302 map_location self_loc = self_attack ? self_attack->self_loc_ : other_attack->other_loc_;
2303 map_location other_loc = self_attack ? self_attack->other_loc_ : other_attack->self_loc_;
2305 if(
self ==
nullptr) {
2311 if(other ==
nullptr) {
2320 temporary_facing other_facing(other, other_loc.
get_relative_dir(self_loc));
2324 bool whom_is_self = ((whom ==
AFFECT_SELF) || ((whom ==
AFFECT_EITHER) && special_affects_self(special, is_attacker)));
2326 map_location their_loc = whom_is_self ? other_loc : self_loc;
2328 if (tag_name ==
"drains" && them && them->get_state(
"undrainable")) {
2331 if (tag_name ==
"plague" && them &&
2332 (them->get_state(
"unplagueable") ||
2336 if (tag_name ==
"poison" && them &&
2340 if (tag_name ==
"slow" && them &&
2344 if (tag_name ==
"petrifies" && them &&
2345 them->get_state(
"unpetrifiable")) {
2353 const map_location & att_loc = is_attacker ? self_loc : other_loc;
2354 const map_location & def_loc = is_attacker ? other_loc : self_loc;
2355 const const_attack_ptr& att_weapon = is_attacker ? self_attack : other_attack;
2356 const const_attack_ptr& def_weapon = is_attacker ? other_attack : self_attack;
2360 if (tag_name ==
"firststrike") {
2361 bool whom_is_defender = whom_is_self ? !is_attacker : is_attacker;
2362 if (whom_is_defender && att_weapon && att_weapon->has_special_or_ability(
"firststrike"))
2371 bool applied_both = special[
"apply_to"] ==
"both";
2372 const std::string& filter_self = in_abilities_tag ?
"filter_student" :
"filter_self";
2373 std::string self_check_if_recursion = (applied_both || whom_is_self) ? tag_name :
"";
2374 if (!special_unit_matches(
self, other, self_loc, self_attack, special, is_for_listing, filter_self, self_check_if_recursion))
2376 std::string opp_check_if_recursion = (applied_both || !whom_is_self) ? tag_name :
"";
2377 if (!special_unit_matches(other,
self, other_loc, other_attack, special, is_for_listing,
"filter_opponent", opp_check_if_recursion))
2381 bool applied_to_attacker = applied_both || (whom_is_self && is_attacker) || (!whom_is_self && !is_attacker);
2382 std::string att_check_if_recursion = applied_to_attacker ? tag_name :
"";
2383 if (!special_unit_matches(att, def, att_loc, att_weapon, special, is_for_listing,
"filter_attacker", att_check_if_recursion))
2385 bool applied_to_defender = applied_both || (whom_is_self && !is_attacker) || (!whom_is_self && is_attacker);
2386 std::string def_check_if_recursion= applied_to_defender ? tag_name :
"";
2387 if (!special_unit_matches(def, att, def_loc, def_weapon, special, is_for_listing,
"filter_defender", def_check_if_recursion))
2420 return (cond_eq.
empty() || def == cond_eq.
to_int()) &&
2433 if(tag_name ==
"plague") {
2435 const auto iter =
unit_types.
types().find(ability_or_special[
"type"]);
2456 std::map<std::string,individual_effect> values_add;
2457 std::map<std::string,individual_effect> values_sub;
2458 std::map<std::string,individual_effect> values_mul;
2459 std::map<std::string,individual_effect> values_div;
2463 utils::optional<int> max_value = utils::nullopt;
2464 utils::optional<int> min_value = utils::nullopt;
2467 const config&
cfg = *ability.ability_cfg;
2468 const std::string& effect_id =
cfg[
cfg[
"id"].
empty() ?
"name" :
"id"];
2476 callable.add(
"base_value", wfl::variant(def));
2477 return std::round(formula.evaluate(callable).as_int());
2480 int value_cum =
cfg[
"cumulative"].to_bool() ? std::max(def, value) : value;
2483 set_effect_min.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2484 set_effect_max.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2487 if(value_cum > set_effect_max.
value) {
2488 set_effect_max.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2490 if(value_cum < set_effect_min.
value) {
2491 set_effect_min.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2499 max_value = max_value ? std::min(*max_value,
cfg[
"max_value"].to_int()) :
cfg[
"max_value"].to_int();
2502 min_value = min_value ? std::max(*min_value,
cfg[
"min_value"].to_int()) :
cfg[
"min_value"].to_int();
2508 callable.add(
"base_value", wfl::variant(def));
2509 return std::round(formula.evaluate(callable).as_int());
2512 if(add_effect == values_add.end() || add > add_effect->second.value) {
2513 values_add[effect_id].set(
ADD, add, ability.ability_cfg, ability.teacher_loc);
2518 callable.add(
"base_value", wfl::variant(def));
2519 return std::round(formula.evaluate(callable).as_int());
2522 if(sub_effect == values_sub.end() || sub < sub_effect->second.value) {
2523 values_sub[effect_id].set(
ADD, sub, ability.ability_cfg, ability.teacher_loc);
2528 callable.add(
"base_value", wfl::variant(def));
2529 return formula.evaluate(callable).as_decimal() / 1000.0 ;
2532 if(mul_effect == values_mul.end() || multiply > mul_effect->second.value) {
2533 values_mul[effect_id].set(
MUL, multiply, ability.ability_cfg, ability.teacher_loc);
2538 callable.add(
"base_value", wfl::variant(def));
2539 return formula.evaluate(callable).as_decimal() / 1000.0 ;
2543 ERR_NG <<
"division by zero with divide= in ability/weapon special " << effect_id;
2547 if(div_effect == values_div.end() || divide > div_effect->second.value) {
2548 values_div[effect_id].set(
DIV, divide, ability.ability_cfg, ability.teacher_loc);
2555 value_set = std::max(set_effect_max.
value, 0) + std::min(set_effect_min.
value, 0);
2556 if(set_effect_max.
value > def) {
2559 if(set_effect_min.
value < def) {
2572 double multiplier = 1.0;
2573 double divisor = 1.0;
2575 for(
const auto& val : values_mul) {
2576 multiplier *= val.second.value/100.0;
2580 for(
const auto& val : values_div) {
2581 divisor *= val.second.value/100.0;
2586 for(
const auto& val : values_add) {
2587 addition += val.second.value;
2593 int substraction = 0;
2594 for(
const auto& val : values_sub) {
2595 substraction += val.second.value;
2601 if(max_value && min_value && *min_value < *max_value) {
2603 }
else if(max_value && !min_value) {
2605 }
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.
int modified_chance_to_hit(int cth, bool special_only=false) const
Return the defense value, 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.
int as_int(int fallback=0) const
Returns the variant's value as an integer.
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
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
const map_location & get_location() const
The current map location this unit is at.
New lexcical_cast header.
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.
void get_adjacent_tiles(const map_location &a, utils::span< map_location, 6 > res)
Function which, given a location, will place all adjacent locations in res.
Standard logging facilities (interface).
void set(CURSOR_TYPE type)
Use the default parameter to reset cursors.
const color_t TITLE_COLOR
const color_t INACTIVE_COLOR
std::vector< std::pair< int, int > > default_counts
static bool is_active(const widget *wgt)
std::stringstream & log_to_chat()
Use this to show WML errors in the ingame chat.
std::string span_color(const color_t &color, Args &&... data)
Applies Pango markup to the input specifying its display color.
filter_context * filter_con
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)
@ EFFECT_WITHOUT_CLAMP_MIN_MAX
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)
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