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")) {
583 if (
std::find(dirs.begin(), dirs.end(), direction) == dirs.end()) {
587 auto filter =
i.optional_child(
"filter");
599 bool affect_self =
cfg[
"affect_self"].to_bool(
true);
600 if (!
filter || !affect_self)
return affect_self;
606 const std::string filter_tag_name = is_opp ?
"filter_second_weapon" :
"filter_weapon";
615 filter_lock = weapon->update_variables_recursion(
cfg);
617 show_recursion_warning(*
this,
cfg);
620 return weapon->matches_filter(
filter);
632 auto ret =
std::find(image_list.begin(), image_list.end(),
cfg[attribute_name].str());
633 if(ret == image_list.end()){
634 image_list.push_back(
cfg[attribute_name].str());
640 std::vector<std::string> image_list;
648 if(!
cfg[image_type +
"_image_self"].str().empty() &&
is_active){
653 const unit_map& units = get_unit_map();
655 for(
const unit& u : units) {
656 if(!u.has_ability_distant_image() || u.incapacitated() || same_unit(u, *
this)) {
661 if(distance > *u.has_ability_distant_image()) {
664 int dir = find_direction(
loc_, from_loc, distance);
665 for(
const auto [key,
cfg] : u.abilities_.all_children_view()) {
673 if(image_list.size() >= 2){
674 std::sort(image_list.begin(), image_list.end());
682 callable.
add(
"attacker",
wfl::variant(std::make_shared<wfl::unit_callable>(*att)));
685 callable.
add(
"defender",
wfl::variant(std::make_shared<wfl::unit_callable>(*def)));
692 template<
typename T,
typename TFuncFormula>
693 class get_ability_value_visitor
694 #ifdef USING_BOOST_VARIANT
695 :
public boost::static_visitor<T>
700 get_ability_value_visitor(T def,
const TFuncFormula& formula_handler) : def_(def), formula_handler_(formula_handler) {}
702 T operator()(
const utils::monostate&)
const {
return def_; }
703 T operator()(
bool)
const {
return def_; }
704 T operator()(
int i)
const {
return static_cast<T
>(
i); }
705 T operator()(
unsigned long long u)
const {
return static_cast<T
>(u); }
706 T operator()(
double d)
const {
return static_cast<T
>(
d); }
707 T operator()(
const t_string&)
const {
return def_; }
708 T operator()(
const std::string&
s)
const
710 if(
s.size() >= 2 &&
s[0] ==
'(') {
711 return formula_handler_(
s);
713 return lexical_cast_default<T>(
s, def_);
718 const TFuncFormula& formula_handler_;
721 template<
typename T,
typename TFuncFormula>
724 return v.
apply_visitor(get_ability_value_visitor(def, [&](
const std::string&
s) {
727 const unit_map& units = get_unit_map();
731 if(u_itor == units.
end()) {
736 att->add_formula_context(callable);
739 callable.add(
"student",
wfl::variant(std::make_shared<wfl::unit_callable>(*uptr)));
742 callable.add(
"other",
wfl::variant(std::make_shared<wfl::unit_callable>(*uptr)));
746 lg::log_to_chat() <<
"Formula error in ability or weapon special: " <<
e.type <<
" at " <<
e.filename <<
':' <<
e.line <<
")\n";
747 ERR_WML <<
"Formula error in ability or weapon special: " <<
e.type <<
" at " <<
e.filename <<
':' <<
e.line <<
")";
754 template<
typename TComp>
757 if ( cfgs_.empty() ) {
763 bool only_cumulative =
true;
773 if ((*
p.ability_cfg)[
"cumulative"].to_bool()) {
775 if (value < 0) value = -value;
776 if (only_cumulative && !comp(value, abs_max)) {
778 best_loc =
p.teacher_loc;
780 }
else if (only_cumulative || comp(flat, value)) {
781 only_cumulative =
false;
783 best_loc =
p.teacher_loc;
786 return std::pair(flat + stack, best_loc);
789 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;
790 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;
829 if(simple_check && specials().has_child(special)) {
833 for(
const config &
i : specials().child_range(special)) {
834 if(special_active(
i, AFFECT_SELF, special)) {
840 if ( simple_check || !other_attack_ ) {
844 for(
const config &
i : other_attack_->specials().child_range(special)) {
845 if(other_attack_->special_active(
i, AFFECT_OTHER, special)) {
862 for(
const config&
i : specials_.child_range(special)) {
863 if(special_active(
i, AFFECT_SELF, special)) {
872 for(
const config&
i : other_attack_->specials_.child_range(special)) {
873 if(other_attack_->special_active(
i, AFFECT_OTHER, special)) {
890 boost::dynamic_bitset<>* active_list)
const
893 std::vector<std::pair<t_string, t_string>> res;
895 active_list->clear();
898 for(
const auto [key,
cfg] : specials_.all_children_view()) {
899 bool active = !active_list || special_active(
cfg, AFFECT_EITHER, key);
901 std::string
name = active
909 std::string desc = active
910 ?
cfg[
"description"].str()
911 :
cfg.
get_or(
"description_inactive",
"description").
str();
919 active_list->push_back(active);
926 boost::dynamic_bitset<>* active_list)
const
928 std::vector<std::pair<t_string, t_string>> res;
930 active_list->clear();
932 std::set<std::string> checking_name;
936 for(
const auto [key,
cfg] : self_->abilities().all_children_view()) {
937 if(!active_list || check_self_abilities_impl(shared_from_this(), other_attack_,
cfg, self_, self_loc_, AFFECT_SELF, key,
false)) {
938 const std::string
name =
cfg[
"name_affected"];
939 const std::string desc =
cfg[
"description_affected"];
944 res.emplace_back(
name, desc);
947 active_list->push_back(
true);
951 for(
const unit& u : get_unit_map()) {
952 if(!u.has_ability_distant() || u.incapacitated() || same_unit(u, *self_)) {
957 if(distance > *u.has_ability_distant()) {
960 int dir = find_direction(self_loc_, from_loc, distance);
961 for(
const auto [key,
cfg] : u.abilities().all_children_view()) {
962 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)) {
963 const std::string
name =
cfg[
"name_affected"];
964 const std::string desc =
cfg[
"description_affected"];
969 res.emplace_back(
name, desc);
972 active_list->push_back(
true);
988 static void add_name(std::string& temp_string,
bool active,
const std::string&
name, std::set<std::string>& checking_name)
990 if (active && !
name.
empty() && checking_name.count(
name) == 0) {
991 checking_name.insert(
name);
992 if (!temp_string.empty()) temp_string +=
", ";
1007 std::vector<std::string> specials;
1009 for(
const auto [key,
cfg] : specials_.all_children_view()) {
1010 const bool active = special_active(
cfg, AFFECT_EITHER, key);
1012 std::string
name = active
1026 std::string temp_string;
1027 std::set<std::string> checking_name;
1028 weapon_specials_impl_self(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name);
1029 weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name, {},
"affect_allies");
1031 if(!temp_string.empty()) {
1032 specials.push_back(
"\n" + std::move(temp_string));
1038 static void add_name_list(std::string& temp_string, std::string& weapon_abilities, std::set<std::string>& checking_name,
const std::string& from_str)
1040 if(!temp_string.empty()){
1041 temp_string = from_str.c_str() + temp_string;
1042 weapon_abilities += (!weapon_abilities.empty() && !temp_string.empty()) ?
"\n" :
"";
1043 weapon_abilities += temp_string;
1044 temp_string.clear();
1045 checking_name.clear();
1052 std::string temp_string, weapon_abilities;
1053 std::set<std::string> checking_name;
1054 for(
const auto [key,
cfg] : specials_.all_children_view()) {
1056 const bool active = special_active(
cfg, AFFECT_SELF, key);
1057 add_name(temp_string, active,
cfg[
"name"].str(), checking_name);
1060 add_name_list(temp_string, weapon_abilities, checking_name,
"");
1062 weapon_specials_impl_self(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name,
checking_tags,
true);
1063 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Owned: "));
1065 weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name,
checking_tags,
"affect_allies",
true);
1067 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Taught: "));
1069 weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name,
checking_tags,
"affect_enemies",
true);
1071 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Taught: (by an enemy): "));
1075 for(
const auto [key,
cfg] : other_attack_->specials_.all_children_view()) {
1077 const bool active = other_attack_->special_active(
cfg, AFFECT_OTHER, key);
1078 add_name(temp_string, active,
cfg[
"name"].str(), checking_name);
1082 weapon_specials_impl_self(temp_string, other_, other_attack_, shared_from_this(), other_loc_, AFFECT_OTHER, checking_name,
checking_tags);
1083 weapon_specials_impl_adj(temp_string, other_, other_attack_, shared_from_this(), other_loc_, AFFECT_OTHER, checking_name,
checking_tags);
1084 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Used by opponent: "));
1086 return weapon_abilities;
1090 std::string& temp_string,
1096 std::set<std::string>& checking_name,
1101 for(
const auto [key,
cfg] : self->abilities().all_children_view()){
1103 const bool active = tag_checked && check_self_abilities_impl(self_attack, other_attack,
cfg,
self, self_loc, whom, key, leader_bool);
1110 std::string& temp_string,
1116 std::set<std::string>& checking_name,
1118 const std::string& affect_adjacents,
1121 const unit_map& units = get_unit_map();
1123 for(
const unit& u : units) {
1124 if(!u.has_ability_distant() || u.incapacitated() || same_unit(u, *
self)) {
1129 if(distance > *u.has_ability_distant()) {
1132 int dir = find_direction(self_loc, from_loc, distance);
1133 for(
const auto [key,
cfg] : u.abilities().all_children_view()) {
1135 bool default_bool = affect_adjacents ==
"affect_allies" ? true :
false;
1136 bool affect_allies = !affect_adjacents.empty() ?
cfg[affect_adjacents].to_bool(default_bool) :
true;
1137 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;
1164 : parent(weapon.shared_from_this())
1166 weapon.
self_ = std::move(
self);
1167 weapon.
other_ = std::move(other);
1184 : parent(weapon.shared_from_this())
1186 weapon.
self_ = std::move(
self);
1204 : parent(weapon.shared_from_this())
1216 : parent(weapon.shared_from_this())
1224 if(was_moved)
return;
1229 parent->is_attacker_ =
false;
1230 parent->other_attack_ =
nullptr;
1231 parent->is_for_listing_ =
false;
1235 : parent(std::move(other.parent))
1237 other.was_moved =
true;
1248 unsigned & max_attacks)
const
1253 if ( attacks_value < 0 ) {
1255 ERR_NG <<
"negative number of strikes after applying weapon specials";
1260 if ( !swarm_specials.
empty() ) {
1261 min_attacks = std::max<int>(0, swarm_specials.
highest(
"swarm_attacks_min").first);
1262 max_attacks = std::max<int>(0, swarm_specials.
highest(
"swarm_attacks_max", attacks_value).first);
1264 min_attacks = max_attacks = attacks_value;
1270 std::map<std::string, unsigned int> type_count;
1271 unsigned int max = 0;
1272 for(
auto&
i : damage_type_list) {
1274 if(
c.has_attribute(
"replacement_type")) {
1275 std::string
type =
c[
"replacement_type"].str();
1276 unsigned int count = ++type_count[
type];
1283 if (type_count.empty())
return type();
1285 std::vector<std::string> type_list;
1286 for(
auto&
i : type_count){
1287 if(
i.second == max){
1288 type_list.push_back(
i.first);
1292 if(type_list.empty())
return type();
1294 return type_list.front();
1299 std::map<std::string, int> type_res;
1300 int max_res = INT_MIN;
1302 for(
auto&
i : damage_type_list) {
1304 if(
c.has_attribute(
"alternative_type")) {
1305 std::string
type =
c[
"alternative_type"].str();
1306 if(type_res.count(
type) == 0){
1307 type_res[
type] = (*other_).resistance_value(resistance_list,
type);
1308 max_res = std::max(max_res, type_res[
type]);
1314 if (type_res.empty())
return {
"", INT_MIN};
1316 std::vector<std::string> type_list;
1317 for(
auto&
i : type_res){
1318 if(
i.second == max_res){
1319 type_list.push_back(
i.first);
1322 if(type_list.empty())
return {
"", INT_MIN};
1324 return {type_list.front(), max_res};
1337 resistance_list = (*other_).get_abilities_weapons(
"resistance",
other_loc_,
other_attack_, shared_from_this());
1339 return (!((*
i.ability_cfg)[
"active_on"].empty() || (!
is_attacker_ && (*
i.ability_cfg)[
"active_on"] ==
"offense") || (
is_attacker_ && (*
i.ability_cfg)[
"active_on"] ==
"defense")));
1343 int res =
other_ ? (*other_).resistance_value(resistance_list,
type()) : 100;
1344 if(damage_type_list.
empty()){
1345 return {
type(), res};
1351 res = replacement_type !=
type() ? (*other_).resistance_value(resistance_list, replacement_type) : res;
1352 replacement_type = alternative_type.second > res ? alternative_type.first : replacement_type;
1353 res = std::max(res, alternative_type.second);
1355 return {replacement_type, res};
1364 std::set<std::string> alternative_damage_types;
1365 if(damage_type_list.
empty()){
1366 return {
type(), alternative_damage_types};
1369 for(
auto&
i : damage_type_list) {
1371 if(
c.has_attribute(
"alternative_type")){
1372 alternative_damage_types.insert(
c[
"alternative_type"].str());
1376 return {replacement_type, alternative_damage_types};
1385 return damage_value;
1397 bool special_affects_opponent(
const config& special,
bool is_attacker)
1400 const std::string& apply_to = special[
"apply_to"];
1401 if ( apply_to.empty() )
1403 if ( apply_to ==
"both" )
1405 if ( apply_to ==
"opponent" )
1407 if ( is_attacker && apply_to ==
"defender" )
1409 if ( !is_attacker && apply_to ==
"attacker" )
1419 bool special_affects_self(
const config& special,
bool is_attacker)
1422 const std::string& apply_to = special[
"apply_to"];
1423 if ( apply_to.empty() )
1425 if ( apply_to ==
"both" )
1427 if ( apply_to ==
"self" )
1429 if ( is_attacker && apply_to ==
"attacker" )
1431 if ( !is_attacker && apply_to ==
"defender")
1446 static std::vector<std::tuple<std::string, std::string>> already_shown;
1448 auto identifier = std::tuple<std::string, std::string>{attack->id(),
filter.debug()};
1453 std::string_view filter_text_view = std::get<1>(identifier);
1455 ERR_NG <<
"Looped recursion error for weapon '" << attack->id()
1456 <<
"' while checking weapon special '" << filter_text_view <<
"'";
1460 if(already_shown.size() > 100) {
1461 already_shown.clear();
1463 already_shown.push_back(std::move(identifier));
1483 const bool for_listing,
1484 const std::string & child_tag,
const std::string& check_if_recursion)
1495 if (!
filter[
"backstab"].
blank() && child_tag ==
"filter_opponent") {
1496 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.");
1499 if(
filter[
"backstab"].to_bool() && child_tag ==
"filter_opponent"){
1500 const std::string& backstab_formula =
"enemy_of(self, flanker) and not flanker.petrified where flanker = unit_at(direction_from(loc, other.facing))";
1502 if(!
filter.has_child(
"filter_opponent")){
1503 filter_child[
"formula"] = backstab_formula;
1506 filter_opponent[
"formula"] = backstab_formula;
1507 filter_child.
add_child(
"and", filter_opponent);
1513 if ( !filter_child )
1529 filter_lock = weapon->update_variables_recursion(
filter);
1531 show_recursion_warning(weapon,
filter);
1536 if (
auto filter_weapon = filter_child->
optional_child(
"filter_weapon") ) {
1537 if ( !weapon || !weapon->matches_filter(*filter_weapon, check_if_recursion) )
1544 return ufilt.matches(*u,
loc);
1546 return ufilt.matches(*u,
loc, *u2);
1564 return special_active(*i.ability_cfg, AFFECT_SELF, ability, true);
1570 return special_active_impl(other_attack_, shared_from_this(), *i.ability_cfg, AFFECT_OTHER, ability, true);
1585 if(!abil_list.
empty() && !overwriters.
empty()){
1601 const std::string& apply_to = special[
"overwrite_specials"];
1602 return (apply_to ==
"one_side" || apply_to ==
"both_sides");
1613 if(overwriters.
empty()){
1618 if(overwriters.
size() >= 2){
1621 auto oi = (*
i.ability_cfg).optional_child(
"overwrite");
1623 if(oi && !oi[
"priority"].empty()){
1624 l = oi[
"priority"].to_double(0);
1626 auto oj = (*j.
ability_cfg).optional_child(
"overwrite");
1628 if(oj && !oj[
"priority"].empty()){
1629 r = oj[
"priority"].to_double(0);
1643 if(overwriters.
empty()){
1647 for(
const auto& j : overwriters) {
1649 bool affect_side = ((*j.ability_cfg)[
"overwrite_specials"] ==
"one_side");
1651 auto overwrite_specials = (*j.ability_cfg).optional_child(
"overwrite");
1652 double priority = overwrite_specials ? overwrite_specials[
"priority"].to_double(0) : 0.00;
1657 bool prior = (priority > 0) ? (has_overwrite_specials && has_overwrite_specials[
"priority"].to_double(0) >= priority) :
true;
1661 bool one_side_overwritable =
true;
1665 if(affect_side && is_overwritable){
1666 if(special_affects_self(*j.ability_cfg,
is_attacker_)){
1669 else if(special_affects_opponent(*j.ability_cfg, !
is_attacker_)){
1675 bool special_matches =
true;
1676 if(overwrite_specials){
1677 auto overwrite_filter = (*overwrite_specials).optional_child(
"filter_specials");
1678 if(!overwrite_filter){
1679 overwrite_filter = (*overwrite_specials).optional_child(
"experimental_filter_specials");
1680 if(overwrite_filter){
1684 if(overwrite_filter && is_overwritable && one_side_overwritable){
1692 if(is_overwritable && one_side_overwritable && special_matches){
1703 show_recursion_warning(*
this,
cfg);
1706 return (ability_active_impl(ability,
cfg,
loc) && ability_affects_self(ability,
cfg,
loc));
1713 show_recursion_warning(from,
cfg);
1721 return (get_self_ability_bool(special, tag_name,
loc) && ability_affects_weapon(special, weapon,
false) && ability_affects_weapon(special, opp_weapon,
true));
1726 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));
1736 if(tag_name ==
"leadership" && leader_bool){
1737 if(u->get_self_ability_bool_weapon(special, tag_name,
loc, self_attack, other_attack)) {
1741 if(u->checking_tags().count(tag_name) != 0){
1742 if(u->get_self_ability_bool(special, tag_name,
loc) &&
special_active_impl(self_attack, other_attack, special, whom, tag_name,
true)) {
1751 return check_adj_abilities_impl(shared_from_this(),
other_attack_,
cfg,
self_, from, dist, dir,
self_loc_, from_loc,
AFFECT_SELF, special,
true);
1754 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)
1756 if(tag_name ==
"leadership" && leader_bool) {
1757 if(u->get_adj_ability_bool_weapon(special, tag_name, dist, dir,
loc, from, from_loc, self_attack, other_attack)) {
1761 if(u->checking_tags().count(tag_name) != 0) {
1762 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)) {
1780 if(
range().empty()){
1787 const unit_map& units = get_unit_map();
1789 for(
const config &
i :
self_->abilities().child_range(special)) {
1795 for(
const unit& u : units) {
1796 if(!u.affect_distant(special) || u.incapacitated() || same_unit(u, *
self_)) {
1801 if(distance > *u.affect_distant(special)) {
1804 int dir = find_direction(
self_loc_, from_loc, distance);
1805 for(
const config &
i : u.abilities().child_range(special)) {
1814 for(
const config &
i :
other_->abilities().child_range(special)) {
1820 for(
const unit& u : units) {
1821 if(!u.affect_distant(special) || u.incapacitated() || same_unit(u, *
other_)) {
1826 if(distance > *u.affect_distant(special)) {
1829 int dir = find_direction(
other_loc_, from_loc, distance);
1830 for(
const config &
i : u.abilities().child_range(special)) {
1831 if(
check_adj_abilities_impl(
other_attack_, shared_from_this(),
i,
other_, u, distance, dir,
other_loc_, from_loc,
AFFECT_OTHER, special)) {
1843 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)
1845 if(!filter_special.empty() && filter_special.count(special_id) == 0 && filter_special.count(tag_name) == 0)
1848 if(!filter_special_id.empty() && filter_special_id.count(special_id) == 0)
1851 if(!filter_special_type.empty() && filter_special_type.count(tag_name) == 0)
1860 if(
range().empty()){
1867 for(
const auto [key,
cfg] :
specials().all_children_view()) {
1868 if(special_checking(
cfg[
"id"].str(), key, filter_special, filter_special_id, filter_special_type)) {
1878 for(
const auto [key,
cfg] :
other_attack_->specials().all_children_view()) {
1879 if(special_checking(
cfg[
"id"].str(), key, filter_special, filter_special_id, filter_special_type)) {
1891 const unit_map& units = get_unit_map();
1893 for(
const auto [key,
cfg] :
self_->abilities().all_children_view()) {
1894 if(special_checking(
cfg[
"id"].str(), key, filter_special, filter_special_id, filter_special_type)) {
1900 for(
const unit& u : units) {
1901 if(!u.has_ability_distant() || u.incapacitated() || same_unit(u, *
self_)) {
1906 if(distance > *u.has_ability_distant()) {
1909 int dir = find_direction(
self_loc_, from_loc, distance);
1911 for(
const auto [key,
cfg] : u.abilities().all_children_view()) {
1912 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)) {
1920 for(
const auto [key,
cfg] :
other_->abilities().all_children_view()) {
1921 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)){
1926 for(
const unit& u : units) {
1927 if(!u.has_ability_distant() || u.incapacitated() || same_unit(u, *
other_)) {
1932 if(distance > *u.has_ability_distant()) {
1935 int dir = find_direction(
other_loc_, from_loc, distance);
1937 for(
const auto [key,
cfg] : u.abilities().all_children_view()) {
1938 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)) {
1949 bool exclude_ability_attributes(
const std::string& tag_name,
const config &
filter)
1952 bool abilities_check = abilities_list::ability_value_tags().count(tag_name) != 0 || abilities_list::ability_no_value_tags().count(tag_name) != 0;
1953 if(
filter.has_attribute(
"active_on") && tag_name !=
"resistance" && abilities_check)
1955 if(
filter.has_attribute(
"apply_to") && tag_name !=
"resistance" && abilities_check)
1958 if(
filter.has_attribute(
"overwrite_specials") && abilities_list::weapon_number_tags().count(tag_name) == 0)
1961 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;
1962 if(
filter.has_attribute(
"value") && no_value_weapon_abilities_check)
1964 if(
filter.has_attribute(
"add") && no_value_weapon_abilities_check)
1966 if(
filter.has_attribute(
"sub") && no_value_weapon_abilities_check)
1968 if(
filter.has_attribute(
"multiply") && no_value_weapon_abilities_check)
1970 if(
filter.has_attribute(
"divide") && no_value_weapon_abilities_check)
1973 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;
1974 if(
filter.has_attribute(
"replacement_type") && tag_name !=
"damage_type" && all_engine)
1976 if(
filter.has_attribute(
"alternative_type") && tag_name !=
"damage_type" && all_engine)
1978 if(
filter.has_attribute(
"type") && tag_name !=
"plague" && all_engine)
1989 if(!exclude_ability_attributes(tag_name,
filter))
1995 if ( !filter_type.empty() &&
std::find(filter_type.begin(), filter_type.end(), tag_name) == filter_type.end() )
2003 if(!
filter[
"affect_adjacent"].empty()){
2005 if(
filter[
"affect_adjacent"].to_bool() != adjacent){
2037 if(!
filter[
"value"].empty()){
2038 if(tag_name ==
"drains"){
2042 }
else if(tag_name ==
"berserk"){
2046 }
else if(tag_name ==
"heal_on_hit" || tag_name ==
"heals" || tag_name ==
"regenerate" || tag_name ==
"leadership"){
2072 if(tag_name ==
"resistance"){
2101 auto fwml =
filter.optional_child(
"filter_wml");
2112 static bool common_matches_filter(
const config &
cfg,
const std::string& tag_name,
const config &
filter)
2115 bool matches = matches_ability_filter(
cfg, tag_name,
filter);
2118 for(
const auto [key, condition_cfg] :
filter.all_children_view() )
2122 matches = matches && common_matches_filter(
cfg, tag_name, condition_cfg);
2125 else if ( key ==
"or" )
2126 matches = matches || common_matches_filter(
cfg, tag_name, condition_cfg);
2129 else if ( key ==
"not" )
2130 matches = matches && !common_matches_filter(
cfg, tag_name, condition_cfg);
2139 return common_matches_filter(
cfg, tag_name,
filter);
2144 return common_matches_filter(
cfg, tag_name,
filter);
2149 if(
range().empty()){
2153 bool check_if_active =
filter[
"active"].to_bool();
2154 for(
const auto [key,
cfg] :
specials().all_children_view()) {
2156 if(!check_if_active) {
2166 for(
const auto [key,
cfg] :
other_attack_->specials().all_children_view()) {
2174 if(!check_if_active){
2178 const unit_map& units = get_unit_map();
2179 bool check_adjacent =
filter[
"affect_adjacent"].to_bool(
true);
2181 for(
const auto [key,
cfg] :
self_->abilities().all_children_view()) {
2188 if(check_adjacent) {
2189 for(
const unit& u : units) {
2190 if(!u.has_ability_distant() || u.incapacitated() || same_unit(u, *
self_)) {
2195 if(distance > *u.has_ability_distant()) {
2198 int dir = find_direction(
self_loc_, from_loc, distance);
2200 for(
const auto [key,
cfg] : u.abilities().all_children_view()) {
2210 for(
const auto [key,
cfg] :
other_->abilities().all_children_view()) {
2216 if(check_adjacent) {
2217 for(
const unit& u : units) {
2218 if(!u.has_ability_distant() || u.incapacitated() || same_unit(u, *
other_)) {
2223 if(distance > *u.has_ability_distant()) {
2226 int dir = find_direction(
other_loc_, from_loc, distance);
2228 for(
const auto [key,
cfg] : u.abilities().all_children_view()) {
2229 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)) {
2240 bool in_abilities_tag)
const
2260 const std::string& tag_name,
2261 bool in_abilities_tag)
2263 assert(self_attack || other_attack);
2264 bool is_attacker = self_attack ? self_attack->is_attacker_ : !other_attack->is_attacker_;
2265 bool is_for_listing = self_attack ? self_attack->is_for_listing_ : other_attack->is_for_listing_;
2271 if ( !special_affects_self(special, is_attacker) )
2275 if ( !special_affects_opponent(special, is_attacker) )
2280 const std::string & active_on = special[
"active_on"];
2281 if ( !active_on.empty() ) {
2282 if ( is_attacker && active_on !=
"offense" )
2284 if ( !is_attacker && active_on !=
"defense" )
2289 const unit_map& units = get_unit_map();
2291 unit_const_ptr self = self_attack ? self_attack->self_ : other_attack->other_;
2292 unit_const_ptr other = self_attack ? self_attack->other_ : other_attack->self_;
2293 map_location self_loc = self_attack ? self_attack->self_loc_ : other_attack->other_loc_;
2294 map_location other_loc = self_attack ? self_attack->other_loc_ : other_attack->self_loc_;
2296 if(
self ==
nullptr) {
2302 if(other ==
nullptr) {
2311 temporary_facing other_facing(other, other_loc.
get_relative_dir(self_loc));
2315 bool whom_is_self = ((whom ==
AFFECT_SELF) || ((whom ==
AFFECT_EITHER) && special_affects_self(special, is_attacker)));
2317 map_location their_loc = whom_is_self ? other_loc : self_loc;
2319 if (tag_name ==
"drains" && them && them->get_state(
"undrainable")) {
2322 if (tag_name ==
"plague" && them &&
2323 (them->get_state(
"unplagueable") ||
2327 if (tag_name ==
"poison" && them &&
2331 if (tag_name ==
"slow" && them &&
2335 if (tag_name ==
"petrifies" && them &&
2336 them->get_state(
"unpetrifiable")) {
2344 const map_location & att_loc = is_attacker ? self_loc : other_loc;
2345 const map_location & def_loc = is_attacker ? other_loc : self_loc;
2346 const const_attack_ptr& att_weapon = is_attacker ? self_attack : other_attack;
2347 const const_attack_ptr& def_weapon = is_attacker ? other_attack : self_attack;
2351 if (tag_name ==
"firststrike") {
2352 bool whom_is_defender = whom_is_self ? !is_attacker : is_attacker;
2353 if (whom_is_defender && att_weapon && att_weapon->has_special_or_ability(
"firststrike"))
2362 bool applied_both = special[
"apply_to"] ==
"both";
2363 const std::string& filter_self = in_abilities_tag ?
"filter_student" :
"filter_self";
2364 std::string self_check_if_recursion = (applied_both || whom_is_self) ? tag_name :
"";
2365 if (!special_unit_matches(
self, other, self_loc, self_attack, special, is_for_listing, filter_self, self_check_if_recursion))
2367 std::string opp_check_if_recursion = (applied_both || !whom_is_self) ? tag_name :
"";
2368 if (!special_unit_matches(other,
self, other_loc, other_attack, special, is_for_listing,
"filter_opponent", opp_check_if_recursion))
2372 bool applied_to_attacker = applied_both || (whom_is_self && is_attacker) || (!whom_is_self && !is_attacker);
2373 std::string att_check_if_recursion = applied_to_attacker ? tag_name :
"";
2374 if (!special_unit_matches(att, def, att_loc, att_weapon, special, is_for_listing,
"filter_attacker", att_check_if_recursion))
2376 bool applied_to_defender = applied_both || (whom_is_self && !is_attacker) || (!whom_is_self && is_attacker);
2377 std::string def_check_if_recursion= applied_to_defender ? tag_name :
"";
2378 if (!special_unit_matches(def, att, def_loc, def_weapon, special, is_for_listing,
"filter_defender", def_check_if_recursion))
2411 return (cond_eq.
empty() || def == cond_eq.
to_int()) &&
2424 if(tag_name ==
"plague") {
2426 const auto iter =
unit_types.
types().find(ability_or_special[
"type"]);
2447 std::map<std::string,individual_effect> values_add;
2448 std::map<std::string,individual_effect> values_sub;
2449 std::map<std::string,individual_effect> values_mul;
2450 std::map<std::string,individual_effect> values_div;
2454 utils::optional<int> max_value = utils::nullopt;
2455 utils::optional<int> min_value = utils::nullopt;
2458 const config&
cfg = *ability.ability_cfg;
2459 const std::string& effect_id =
cfg[
cfg[
"id"].
empty() ?
"name" :
"id"];
2467 callable.add(
"base_value", wfl::variant(def));
2468 return std::round(formula.evaluate(callable).as_int());
2471 int value_cum =
cfg[
"cumulative"].to_bool() ? std::max(def, value) : value;
2474 set_effect_min.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2475 set_effect_max.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2478 if(value_cum > set_effect_max.
value) {
2479 set_effect_max.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2481 if(value_cum < set_effect_min.
value) {
2482 set_effect_min.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2490 max_value = max_value ? std::min(*max_value,
cfg[
"max_value"].to_int()) :
cfg[
"max_value"].to_int();
2493 min_value = min_value ? std::max(*min_value,
cfg[
"min_value"].to_int()) :
cfg[
"min_value"].to_int();
2499 callable.add(
"base_value", wfl::variant(def));
2500 return std::round(formula.evaluate(callable).as_int());
2503 if(add_effect == values_add.end() || add > add_effect->second.value) {
2504 values_add[effect_id].set(
ADD, add, ability.ability_cfg, ability.teacher_loc);
2509 callable.add(
"base_value", wfl::variant(def));
2510 return std::round(formula.evaluate(callable).as_int());
2513 if(sub_effect == values_sub.end() || sub < sub_effect->second.value) {
2514 values_sub[effect_id].set(
ADD, sub, ability.ability_cfg, ability.teacher_loc);
2519 callable.add(
"base_value", wfl::variant(def));
2520 return formula.evaluate(callable).as_decimal() / 1000.0 ;
2523 if(mul_effect == values_mul.end() || multiply > mul_effect->second.value) {
2524 values_mul[effect_id].set(
MUL, multiply, ability.ability_cfg, ability.teacher_loc);
2529 callable.add(
"base_value", wfl::variant(def));
2530 return formula.evaluate(callable).as_decimal() / 1000.0 ;
2534 ERR_NG <<
"division by zero with divide= in ability/weapon special " << effect_id;
2538 if(div_effect == values_div.end() || divide > div_effect->second.value) {
2539 values_div[effect_id].set(
DIV, divide, ability.ability_cfg, ability.teacher_loc);
2546 value_set = std::max(set_effect_max.
value, 0) + std::min(set_effect_min.
value, 0);
2547 if(set_effect_max.
value > def) {
2550 if(set_effect_min.
value < def) {
2563 double multiplier = 1.0;
2564 double divisor = 1.0;
2566 for(
const auto& val : values_mul) {
2567 multiplier *= val.second.value/100.0;
2571 for(
const auto& val : values_div) {
2572 divisor *= val.second.value/100.0;
2577 for(
const auto& val : values_add) {
2578 addition += val.second.value;
2584 int substraction = 0;
2585 for(
const auto& val : values_sub) {
2586 substraction += val.second.value;
2592 if(max_value && min_value && *min_value < *max_value) {
2594 }
else if(max_value && !min_value) {
2596 }
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.
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
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
const map_location & get_location() const
The current map location this unit is at.
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