54 #define ERR_NG LOG_STREAM(err, log_engine)
57 #define ERR_WML LOG_STREAM(err, log_wml)
60 class temporary_facing
70 u_->set_facing(new_dir);
76 u_->set_facing(save_dir_);
153 const team& get_team(std::size_t side)
173 bool affects_side(
const config&
cfg, std::size_t side, std::size_t other_side)
175 const team& side_team = get_team(side);
177 if(side == other_side)
178 return cfg[
"affect_allies"].to_bool(
true);
180 return cfg[
"affect_enemies"].to_bool();
182 return cfg[
"affect_allies"].to_bool();
192 for(std::size_t j = 0; j < adjacent.size(); ++j) {
193 bool adj_or_dist = distance != 1 ?
distance_between(adjacent[j],
loc) == (distance - 1) : adjacent[j] ==
loc;
216 const unit_map& units = get_unit_map();
222 for(
const unit& u : units) {
223 if(!u.max_ability_radius() || u.incapacitated() || u.underlying_id() ==
underlying_id() || !u.max_ability_radius_type(tag_name)) {
228 if(distance > u.max_ability_radius_type(tag_name)) {
231 int dir = find_direction(
loc, from_loc, distance);
232 for(
const config&
i : u.abilities_.child_range(tag_name)) {
258 const unit_map& units = get_unit_map();
264 for(
const unit& u : units) {
265 if(!u.max_ability_radius() || u.incapacitated() || u.underlying_id() ==
underlying_id() || !u.max_ability_radius_type(tag_name)) {
270 if(distance > u.max_ability_radius_type(tag_name)) {
273 int dir = find_direction(
loc, from_loc, distance);
274 for(
const config&
i : u.abilities_.child_range(tag_name)) {
296 std::vector<std::string> res;
299 std::string
id =
cfg[
"id"];
301 res.push_back(std::move(
id));
314 bool add_ability_tooltip(
const config& ab,
const std::string& tag_name,
unit_race::GENDER gender, std::vector<std::tuple<std::string, t_string,t_string,t_string>>& res,
bool active)
331 "female_name_inactive",
"name_inactive");
352 std::vector<std::tuple<std::string, t_string,t_string,t_string>> res;
356 add_ability_tooltip(
cfg, tag_name,
gender_, res,
true);
364 std::vector<std::tuple<std::string, t_string,t_string,t_string>> res;
370 if(add_ability_tooltip(
cfg, tag_name,
gender_, res, active))
372 active_list.push_back(active);
389 static std::vector<std::tuple<std::string, std::string>> already_shown;
391 auto identifier = std::tuple<std::string, std::string>{
unit.
id(),
filter.debug()};
396 std::string_view filter_text_view = std::get<1>(identifier);
398 ERR_NG <<
"Looped recursion error for unit '" <<
unit.
id()
399 <<
"' while checking ability '" << filter_text_view <<
"'";
403 if(already_shown.size() > 100) {
404 already_shown.clear();
406 already_shown.push_back(std::move(identifier));
421 : parent(u.shared_from_this())
431 unit::recursion_guard::operator bool()
const {
437 assert(
this != &other);
446 assert(!parent->open_queries_.empty());
447 parent->open_queries_.pop_back();
455 show_recursion_warning(*
this,
cfg);
465 const unit_map& units = get_unit_map();
468 const std::string& filter_adjacent = in_abilities_tag ?
"filter_adjacent_student" :
"filter_adjacent";
469 const std::string& filter_adjacent_location = in_abilities_tag ?
"filter_adjacent_student_location" :
"filter_adjacent_location";
472 std::size_t radius =
i[
"radius"].to_int(1);
473 std::size_t count = 0;
475 ufilt.set_use_flat_tod(illuminates);
476 for(
const unit& u : units) {
479 if(u.underlying_id() ==
self.underlying_id() || distance > radius || !ufilt(u,
self)) {
483 for(
unsigned j = 0; j < adjacent.size(); ++j) {
484 bool adj_or_dist = distance != 1 ?
distance_between(adjacent[j], from_loc) == (distance - 1) : adjacent[j] == from_loc;
490 assert(dir >= 0 && dir <= 5);
492 if(
i.has_attribute(
"adjacent")) {
497 if(
i.has_attribute(
"is_enemy")) {
506 if(
i[
"count"].empty() && count == 0) {
515 std::size_t count = 0;
517 adj_filter.flatten(illuminates);
521 if(!adj_filter.match(adjacent[
static_cast<int>(
index)])) {
538 bool illuminates = ability ==
"illuminates";
556 bool illuminates = ability ==
"illuminates";
558 assert(dir >=0 && dir <= 5);
560 std::size_t radius = 1;
564 if(
i[
"radius"] !=
"all_map") {
565 radius =
i[
"radius"].to_int(1);
573 if (
i.has_attribute(
"adjacent")) {
578 auto filter =
i.optional_child(
"filter");
590 bool affect_self =
cfg[
"affect_self"].to_bool(
true);
591 if (!
filter || !affect_self)
return affect_self;
597 const std::string filter_tag_name = is_opp ?
"filter_second_weapon" :
"filter_weapon";
606 filter_lock = weapon->update_variables_recursion(
cfg);
608 show_recursion_warning(*
this,
cfg);
611 return weapon->matches_filter(
filter);
624 image_list.push_back(
cfg[attribute_name].str());
630 std::vector<std::string> image_list;
638 if(!
cfg[image_type +
"_image_self"].str().empty() &&
is_active){
643 const unit_map& units = get_unit_map();
645 for(
const unit& u : units) {
646 if(!u.max_ability_radius_image() || u.incapacitated() || u.underlying_id() ==
underlying_id()) {
651 if(distance > u.max_ability_radius_image()) {
654 int dir = find_direction(
loc_, from_loc, distance);
655 for(
const auto [key,
cfg] : u.abilities_.all_children_view()) {
663 if(image_list.size() >= 2){
664 std::sort(image_list.begin(), image_list.end());
672 callable.
add(
"attacker",
wfl::variant(std::make_shared<wfl::unit_callable>(*att)));
675 callable.
add(
"defender",
wfl::variant(std::make_shared<wfl::unit_callable>(*def)));
682 template<
typename T,
typename TFuncFormula>
683 class get_ability_value_visitor
684 #ifdef USING_BOOST_VARIANT
685 :
public boost::static_visitor<T>
690 get_ability_value_visitor(T def,
const TFuncFormula& formula_handler) : def_(def), formula_handler_(formula_handler) {}
692 T operator()(
const utils::monostate&)
const {
return def_; }
693 T operator()(
bool)
const {
return def_; }
694 T operator()(
int i)
const {
return static_cast<T
>(
i); }
695 T operator()(
unsigned long long u)
const {
return static_cast<T
>(u); }
696 T operator()(
double d)
const {
return static_cast<T
>(
d); }
697 T operator()(
const t_string&)
const {
return def_; }
698 T operator()(
const std::string&
s)
const
700 if(
s.size() >= 2 &&
s[0] ==
'(') {
701 return formula_handler_(
s);
703 return lexical_cast_default<T>(
s, def_);
708 const TFuncFormula& formula_handler_;
711 template<
typename T,
typename TFuncFormula>
714 return v.
apply_visitor(get_ability_value_visitor(def, [&](
const std::string&
s) {
717 const unit_map& units = get_unit_map();
721 if(u_itor == units.
end()) {
726 att->add_formula_context(callable);
729 callable.add(
"student",
wfl::variant(std::make_shared<wfl::unit_callable>(*uptr)));
732 callable.add(
"other",
wfl::variant(std::make_shared<wfl::unit_callable>(*uptr)));
736 lg::log_to_chat() <<
"Formula error in ability or weapon special: " <<
e.type <<
" at " <<
e.filename <<
':' <<
e.line <<
")\n";
737 ERR_WML <<
"Formula error in ability or weapon special: " <<
e.type <<
" at " <<
e.filename <<
':' <<
e.line <<
")";
744 template<
typename TComp>
747 if ( cfgs_.empty() ) {
753 bool only_cumulative =
true;
763 if ((*
p.ability_cfg)[
"cumulative"].to_bool()) {
765 if (value < 0) value = -value;
766 if (only_cumulative && !comp(value, abs_max)) {
768 best_loc =
p.teacher_loc;
770 }
else if (only_cumulative || comp(flat, value)) {
771 only_cumulative =
false;
773 best_loc =
p.teacher_loc;
776 return std::pair(flat + stack, best_loc);
779 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;
780 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;
819 if(simple_check && specials().has_child(special)) {
823 for(
const config &
i : specials().child_range(special)) {
824 if(special_active(
i, AFFECT_SELF, special)) {
830 if ( simple_check || !other_attack_ ) {
834 for(
const config &
i : other_attack_->specials().child_range(special)) {
835 if(other_attack_->special_active(
i, AFFECT_OTHER, special)) {
849 bool inverse_affect = abilities_list::weapon_inverse_affect_tags().count(special) != 0;
853 for(
const config&
i : specials_.child_range(special)) {
854 if(special_active(
i, inverse_affect ? AFFECT_OTHER : AFFECT_SELF, special)) {
863 for(
const config&
i : other_attack_->specials_.child_range(special)) {
864 if(other_attack_->special_active(
i, inverse_affect ? AFFECT_SELF : AFFECT_OTHER, special)) {
881 boost::dynamic_bitset<>* active_list)
const
884 std::vector<std::pair<t_string, t_string>> res;
886 active_list->clear();
889 for(
const auto [key,
cfg] : specials_.all_children_view()) {
890 bool active = !active_list || special_active(
cfg, AFFECT_EITHER, key);
892 std::string
name = active
900 std::string desc = active
901 ?
cfg[
"description"].str()
902 :
cfg.
get_or(
"description_inactive",
"description").
str();
910 active_list->push_back(active);
917 boost::dynamic_bitset<>* active_list)
const
919 std::vector<std::pair<t_string, t_string>> res;
921 active_list->clear();
923 std::set<std::string> checking_name;
927 for(
const auto [key,
cfg] : self_->abilities().all_children_view()) {
928 if(!active_list || check_self_abilities_impl(shared_from_this(), other_attack_,
cfg, self_, self_loc_, AFFECT_SELF, key,
false)) {
929 const std::string
name =
cfg[
"name_affected"];
930 const std::string desc =
cfg[
"description_affected"];
935 res.emplace_back(
name, desc);
938 active_list->push_back(
true);
942 for(
const unit& u : get_unit_map()) {
943 if(!u.max_ability_radius() || u.incapacitated() || u.underlying_id() == self_->underlying_id()) {
948 if(distance > u.max_ability_radius()) {
951 int dir = find_direction(self_loc_, from_loc, distance);
952 for(
const auto [key,
cfg] : u.abilities().all_children_view()) {
953 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)) {
954 const std::string
name =
cfg[
"name_affected"];
955 const std::string desc =
cfg[
"description_affected"];
960 res.emplace_back(
name, desc);
963 active_list->push_back(
true);
979 static void add_name(std::string& temp_string,
bool active,
const std::string&
name, std::set<std::string>& checking_name)
981 if (active && !
name.
empty() && checking_name.count(
name) == 0) {
982 checking_name.insert(
name);
983 if (!temp_string.empty()) temp_string +=
", ";
998 std::vector<std::string> specials;
1000 for(
const auto [key,
cfg] : specials_.all_children_view()) {
1001 const bool active = special_active(
cfg, AFFECT_EITHER, key);
1003 std::string
name = active
1017 std::string temp_string;
1018 std::set<std::string> checking_name;
1019 weapon_specials_impl_self(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name);
1020 weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name, {},
"affect_allies");
1022 if(!temp_string.empty()) {
1023 specials.push_back(
"\n" + std::move(temp_string));
1029 static void add_name_list(std::string& temp_string, std::string& weapon_abilities, std::set<std::string>& checking_name,
const std::string& from_str)
1031 if(!temp_string.empty()){
1032 temp_string = from_str.c_str() + temp_string;
1033 weapon_abilities += (!weapon_abilities.empty() && !temp_string.empty()) ?
"\n" :
"";
1034 weapon_abilities += temp_string;
1035 temp_string.clear();
1036 checking_name.clear();
1043 std::string temp_string, weapon_abilities;
1044 std::set<std::string> checking_name;
1045 for(
const auto [key,
cfg] : specials_.all_children_view()) {
1046 if(checking_tags.count(key) != 0) {
1047 const bool active = special_active(
cfg, AFFECT_SELF, key);
1048 add_name(temp_string, active,
cfg[
"name"].str(), checking_name);
1051 add_name_list(temp_string, weapon_abilities, checking_name,
"");
1053 weapon_specials_impl_self(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name, checking_tags,
true);
1054 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Owned: "));
1056 weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name, checking_tags,
"affect_allies",
true);
1058 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Taught: "));
1060 weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name, checking_tags,
"affect_enemies",
true);
1062 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Taught: (by an enemy): "));
1066 for(
const auto [key,
cfg] : other_attack_->specials_.all_children_view()) {
1067 if((checking_tags.count(key) != 0)){
1068 const bool active = other_attack_->special_active(
cfg, AFFECT_OTHER, key);
1069 add_name(temp_string, active,
cfg[
"name"].str(), checking_name);
1073 weapon_specials_impl_self(temp_string, other_, other_attack_, shared_from_this(), other_loc_, AFFECT_OTHER, checking_name, checking_tags);
1074 weapon_specials_impl_adj(temp_string, other_, other_attack_, shared_from_this(), other_loc_, AFFECT_OTHER, checking_name, checking_tags);
1075 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Used by opponent: "));
1077 return weapon_abilities;
1081 std::string& temp_string,
1087 std::set<std::string>& checking_name,
1088 const std::set<std::string>& checking_tags,
1092 for(
const auto [key,
cfg] : self->abilities().all_children_view()){
1093 bool tag_checked = (!checking_tags.empty()) ? (checking_tags.count(key) != 0) :
true;
1094 const bool active = tag_checked && check_self_abilities_impl(self_attack, other_attack,
cfg,
self, self_loc, whom, key, leader_bool);
1101 std::string& temp_string,
1107 std::set<std::string>& checking_name,
1108 const std::set<std::string>& checking_tags,
1109 const std::string& affect_adjacents,
1112 const unit_map& units = get_unit_map();
1114 for(
const unit& u : units) {
1115 if(!u.max_ability_radius() || u.incapacitated() || u.underlying_id() == self->underlying_id()) {
1120 if(distance > u.max_ability_radius()) {
1123 int dir = find_direction(self_loc, from_loc, distance);
1124 for(
const auto [key,
cfg] : u.abilities().all_children_view()) {
1125 bool tag_checked = !checking_tags.empty() ? checking_tags.count(key) != 0 :
true;
1126 bool default_bool = affect_adjacents ==
"affect_allies" ? true :
false;
1127 bool affect_allies = !affect_adjacents.empty() ?
cfg[affect_adjacents].to_bool(default_bool) :
true;
1128 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;
1155 : parent(weapon.shared_from_this())
1157 weapon.
self_ = std::move(
self);
1158 weapon.
other_ = std::move(other);
1175 : parent(weapon.shared_from_this())
1177 weapon.
self_ = std::move(
self);
1195 : parent(weapon.shared_from_this())
1207 : parent(weapon.shared_from_this())
1215 if(was_moved)
return;
1220 parent->is_attacker_ =
false;
1221 parent->other_attack_ =
nullptr;
1222 parent->is_for_listing_ =
false;
1226 : parent(std::move(other.parent))
1228 other.was_moved =
true;
1239 unsigned & max_attacks)
const
1244 if ( attacks_value < 0 ) {
1246 ERR_NG <<
"negative number of strikes after applying weapon specials";
1251 if ( !swarm_specials.
empty() ) {
1252 min_attacks = std::max<int>(0, swarm_specials.
highest(
"swarm_attacks_min").first);
1253 max_attacks = std::max<int>(0, swarm_specials.
highest(
"swarm_attacks_max", attacks_value).first);
1255 min_attacks = max_attacks = attacks_value;
1261 std::map<std::string, unsigned int> type_count;
1262 unsigned int max = 0;
1263 for(
auto&
i : damage_type_list) {
1265 if(
c.has_attribute(
"replacement_type")) {
1266 std::string
type =
c[
"replacement_type"].str();
1267 unsigned int count = ++type_count[
type];
1274 if (type_count.empty())
return type();
1276 std::vector<std::string> type_list;
1277 for(
auto&
i : type_count){
1278 if(
i.second == max){
1279 type_list.push_back(
i.first);
1283 if(type_list.empty())
return type();
1285 return type_list.front();
1290 std::map<std::string, int> type_res;
1291 int max_res = INT_MIN;
1293 for(
auto&
i : damage_type_list) {
1295 if(
c.has_attribute(
"alternative_type")) {
1296 std::string
type =
c[
"alternative_type"].str();
1297 if(type_res.count(
type) == 0){
1298 type_res[
type] = (*other_).resistance_value(resistance_list,
type);
1299 max_res = std::max(max_res, type_res[
type]);
1305 if (type_res.empty())
return {
"", INT_MIN};
1307 std::vector<std::string> type_list;
1308 for(
auto&
i : type_res){
1309 if(
i.second == max_res){
1310 type_list.push_back(
i.first);
1313 if(type_list.empty())
return {
"", INT_MIN};
1315 return {type_list.front(), max_res};
1328 resistance_list = (*other_).get_abilities_weapons(
"resistance",
other_loc_,
other_attack_, shared_from_this());
1330 return (!((*
i.ability_cfg)[
"active_on"].empty() || (!
is_attacker_ && (*
i.ability_cfg)[
"active_on"] ==
"offense") || (
is_attacker_ && (*
i.ability_cfg)[
"active_on"] ==
"defense")));
1334 int res =
other_ ? (*other_).resistance_value(resistance_list,
type()) : 100;
1335 if(damage_type_list.
empty()){
1336 return {
type(), res};
1342 res = replacement_type !=
type() ? (*other_).resistance_value(resistance_list, replacement_type) : res;
1343 replacement_type = alternative_type.second > res ? alternative_type.first : replacement_type;
1344 res = std::max(res, alternative_type.second);
1346 return {replacement_type, res};
1355 std::set<std::string> alternative_damage_types;
1356 if(damage_type_list.
empty()){
1357 return {
type(), alternative_damage_types};
1360 for(
auto&
i : damage_type_list) {
1362 if(
c.has_attribute(
"alternative_type")){
1363 alternative_damage_types.insert(
c[
"alternative_type"].str());
1367 return {replacement_type, alternative_damage_types};
1376 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);
1560 bool inverse_affect = abilities_list::weapon_inverse_affect_tags().count(ability) != 0;
1565 return special_active(*i.ability_cfg, inverse_affect ? AFFECT_OTHER : AFFECT_SELF, ability, true);
1571 return special_active_impl(other_attack_, shared_from_this(), *i.ability_cfg, inverse_affect ? AFFECT_SELF : AFFECT_OTHER, ability, true);
1586 if(!abil_list.
empty() && !overwriters.
empty()){
1602 const std::string& apply_to = special[
"overwrite_specials"];
1603 return (apply_to ==
"one_side" || apply_to ==
"both_sides");
1614 if(overwriters.
empty()){
1619 if(overwriters.
size() >= 2){
1622 auto oi = (*
i.ability_cfg).optional_child(
"overwrite");
1624 if(oi && !oi[
"priority"].empty()){
1625 l = oi[
"priority"].to_double(0);
1627 auto oj = (*j.
ability_cfg).optional_child(
"overwrite");
1629 if(oj && !oj[
"priority"].empty()){
1630 r = oj[
"priority"].to_double(0);
1644 if(overwriters.
empty()){
1648 for(
const auto& j : overwriters) {
1650 bool affect_side = ((*j.ability_cfg)[
"overwrite_specials"] ==
"one_side");
1652 auto overwrite_specials = (*j.ability_cfg).optional_child(
"overwrite");
1653 double priority = overwrite_specials ? overwrite_specials[
"priority"].to_double(0) : 0.00;
1658 bool prior = (priority > 0) ? (has_overwrite_specials && has_overwrite_specials[
"priority"].to_double(0) >= priority) :
true;
1662 bool one_side_overwritable =
true;
1666 if(affect_side && is_overwritable){
1667 if(special_affects_self(*j.ability_cfg,
is_attacker_)){
1670 else if(special_affects_opponent(*j.ability_cfg, !
is_attacker_)){
1676 bool special_matches =
true;
1677 if(overwrite_specials){
1678 auto overwrite_filter = (*overwrite_specials).optional_child(
"filter_specials");
1679 if(!overwrite_filter){
1680 overwrite_filter = (*overwrite_specials).optional_child(
"experimental_filter_specials");
1681 if(overwrite_filter){
1685 if(overwrite_filter && is_overwritable && one_side_overwritable){
1693 if(is_overwritable && one_side_overwritable && special_matches){
1704 show_recursion_warning(*
this,
cfg);
1707 return (ability_active_impl(ability,
cfg,
loc) && ability_affects_self(ability,
cfg,
loc));
1714 show_recursion_warning(from,
cfg);
1722 return (get_self_ability_bool(special, tag_name,
loc) && ability_affects_weapon(special, weapon,
false) && ability_affects_weapon(special, opp_weapon,
true));
1727 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));
1732 if(tag_name ==
"leadership" && leader_bool){
1733 if(u->get_self_ability_bool_weapon(special, tag_name,
loc, self_attack, other_attack)) {
1737 if(abilities_list::all_weapon_tags().count(tag_name) != 0){
1738 if(u->get_self_ability_bool(special, tag_name,
loc) &&
special_active_impl(self_attack, other_attack, special, whom, tag_name,
true)) {
1745 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)
1747 if(tag_name ==
"leadership" && leader_bool) {
1748 if(u->get_adj_ability_bool_weapon(special, tag_name, dist, dir,
loc, from, from_loc, self_attack, other_attack)) {
1752 if(abilities_list::all_weapon_tags().count(tag_name) != 0) {
1753 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)) {
1766 const std::string& special)
1768 for(
const config &
i : self->abilities().child_range(special)) {
1773 const unit_map& units = get_unit_map();
1774 for(
const unit& u : units) {
1775 if(!u.max_ability_radius() || u.incapacitated() || u.underlying_id() == self->underlying_id() || !u.max_ability_radius_type(special)) {
1780 if(distance > u.max_ability_radius_type(special)) {
1783 int dir = find_direction(self_loc, from_loc, distance);
1784 for(
const config &
i : u.abilities().child_range(special)) {
1785 if(
check_adj_abilities_impl(self_attack, other_attack,
i,
self, u, distance, dir, self_loc, from_loc, whom, special)) {
1804 if(
range().empty()){
1824 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)
1826 if(!filter_special.empty() && filter_special.count(special_id) == 0 && filter_special.count(tag_name) == 0)
1829 if(!filter_special_id.empty() && filter_special_id.count(special_id) == 0)
1832 if(!filter_special_type.empty() && filter_special_type.count(tag_name) == 0)
1852 const unit_map& units = get_unit_map();
1853 bool check_adjacent = sub_filter ?
filter[
"affect_adjacent"].to_bool(
true) :
true;
1854 for(
const auto [key,
cfg] : self->abilities().all_children_view()) {
1855 bool special_check = sub_filter ?
self->ability_matches_filter(
cfg, key,
filter) : special_checking(
cfg[
"id"].str(), key, filter_special, filter_special_id, filter_special_type);
1860 if(check_adjacent) {
1861 for(
const unit& u : units) {
1862 if(!u.max_ability_radius() || u.incapacitated() || u.underlying_id() == self->underlying_id()) {
1867 if(distance > u.max_ability_radius()) {
1870 int dir = find_direction(self_loc, from_loc, distance);
1872 for(
const auto [key,
cfg] : u.abilities().all_children_view()) {
1873 bool special_check = sub_filter ? u.ability_matches_filter(
cfg, key,
filter) : special_checking(
cfg[
"id"].str(), key, filter_special, filter_special_id, filter_special_type);
1874 if(special_check &&
check_adj_abilities_impl(self_attack, other_attack,
cfg,
self, u, distance, dir, self_loc, from_loc, whom, key, leader_bool)) {
1885 if(
range().empty()){
1892 for(
const auto [key,
cfg] :
specials().all_children_view()) {
1893 if(special_checking(
cfg[
"id"].str(), key, filter_special, filter_special_id, filter_special_type)) {
1903 for(
const auto [key,
cfg] :
other_attack_->specials().all_children_view()) {
1904 if(special_checking(
cfg[
"id"].str(), key, filter_special, filter_special_id, filter_special_type)) {
1928 bool exclude_ability_attributes(
const std::string& tag_name,
const config &
filter)
1931 bool abilities_check = abilities_list::ability_value_tags().count(tag_name) != 0 || abilities_list::ability_no_value_tags().count(tag_name) != 0;
1932 if(
filter.has_attribute(
"active_on") && tag_name !=
"resistance" && abilities_check)
1934 if(
filter.has_attribute(
"apply_to") && tag_name !=
"resistance" && abilities_check)
1937 if(
filter.has_attribute(
"overwrite_specials") && abilities_list::weapon_number_tags().count(tag_name) == 0)
1940 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;
1941 if(
filter.has_attribute(
"value") && no_value_weapon_abilities_check)
1943 if(
filter.has_attribute(
"add") && no_value_weapon_abilities_check)
1945 if(
filter.has_attribute(
"sub") && no_value_weapon_abilities_check)
1947 if(
filter.has_attribute(
"multiply") && no_value_weapon_abilities_check)
1949 if(
filter.has_attribute(
"divide") && no_value_weapon_abilities_check)
1952 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;
1953 if(
filter.has_attribute(
"replacement_type") && tag_name !=
"damage_type" && all_engine)
1955 if(
filter.has_attribute(
"alternative_type") && tag_name !=
"damage_type" && all_engine)
1957 if(
filter.has_attribute(
"type") && tag_name !=
"plague" && all_engine)
1968 if(!exclude_ability_attributes(tag_name,
filter))
1982 if(!
filter[
"affect_adjacent"].empty()){
1984 if(
filter[
"affect_adjacent"].to_bool() != adjacent){
2016 if(!
filter[
"value"].empty()){
2017 if(tag_name ==
"drains"){
2021 }
else if(tag_name ==
"berserk"){
2025 }
else if(tag_name ==
"heal_on_hit" || tag_name ==
"heals" || tag_name ==
"regenerate" || tag_name ==
"leadership"){
2051 if(tag_name ==
"resistance"){
2080 auto fwml =
filter.optional_child(
"filter_wml");
2091 static bool common_matches_filter(
const config &
cfg,
const std::string& tag_name,
const config &
filter)
2094 bool matches = matches_ability_filter(
cfg, tag_name,
filter);
2097 for(
const auto [key, condition_cfg] :
filter.all_children_view() )
2101 matches = matches && common_matches_filter(
cfg, tag_name, condition_cfg);
2104 else if ( key ==
"or" )
2105 matches = matches || common_matches_filter(
cfg, tag_name, condition_cfg);
2108 else if ( key ==
"not" )
2109 matches = matches && !common_matches_filter(
cfg, tag_name, condition_cfg);
2118 return common_matches_filter(
cfg, tag_name,
filter);
2123 return common_matches_filter(
cfg, tag_name,
filter);
2128 if(
range().empty()){
2132 bool check_if_active =
filter[
"active"].to_bool();
2133 for(
const auto [key,
cfg] :
specials().all_children_view()) {
2135 if(!check_if_active) {
2145 for(
const auto [key,
cfg] :
other_attack_->specials().all_children_view()) {
2153 if(!check_if_active){
2168 bool in_abilities_tag)
const
2188 const std::string& tag_name,
2189 bool in_abilities_tag)
2191 assert(self_attack || other_attack);
2192 bool is_attacker = self_attack ? self_attack->is_attacker_ : !other_attack->is_attacker_;
2193 bool is_for_listing = self_attack ? self_attack->is_for_listing_ : other_attack->is_for_listing_;
2199 if ( !special_affects_self(special, is_attacker) )
2203 if ( !special_affects_opponent(special, is_attacker) )
2208 const std::string & active_on = special[
"active_on"];
2209 if ( !active_on.empty() ) {
2210 if ( is_attacker && active_on !=
"offense" )
2212 if ( !is_attacker && active_on !=
"defense" )
2217 const unit_map& units = get_unit_map();
2219 unit_const_ptr self = self_attack ? self_attack->self_ : other_attack->other_;
2220 unit_const_ptr other = self_attack ? self_attack->other_ : other_attack->self_;
2221 map_location self_loc = self_attack ? self_attack->self_loc_ : other_attack->other_loc_;
2222 map_location other_loc = self_attack ? self_attack->other_loc_ : other_attack->self_loc_;
2224 if(
self ==
nullptr) {
2230 if(other ==
nullptr) {
2239 temporary_facing other_facing(other, other_loc.
get_relative_dir(self_loc));
2243 bool whom_is_self = ((whom ==
AFFECT_SELF) || ((whom ==
AFFECT_EITHER) && special_affects_self(special, is_attacker)));
2245 map_location their_loc = whom_is_self ? other_loc : self_loc;
2247 if (tag_name ==
"drains" && them && them->get_state(
"undrainable")) {
2250 if (tag_name ==
"plague" && them &&
2251 (them->get_state(
"unplagueable") ||
2255 if (tag_name ==
"poison" && them &&
2259 if (tag_name ==
"slow" && them &&
2263 if (tag_name ==
"petrifies" && them &&
2264 them->get_state(
"unpetrifiable")) {
2272 const map_location & att_loc = is_attacker ? self_loc : other_loc;
2273 const map_location & def_loc = is_attacker ? other_loc : self_loc;
2274 const const_attack_ptr& att_weapon = is_attacker ? self_attack : other_attack;
2275 const const_attack_ptr& def_weapon = is_attacker ? other_attack : self_attack;
2279 if (tag_name ==
"firststrike") {
2280 bool whom_is_defender = whom_is_self ? !is_attacker : is_attacker;
2281 if (whom_is_defender && att_weapon && att_weapon->has_special_or_ability(
"firststrike"))
2290 bool applied_both = special[
"apply_to"] ==
"both";
2291 const std::string& filter_self = in_abilities_tag ?
"filter_student" :
"filter_self";
2292 std::string self_check_if_recursion = (applied_both || whom_is_self) ? tag_name :
"";
2293 if (!special_unit_matches(
self, other, self_loc, self_attack, special, is_for_listing, filter_self, self_check_if_recursion))
2295 std::string opp_check_if_recursion = (applied_both || !whom_is_self) ? tag_name :
"";
2296 if (!special_unit_matches(other,
self, other_loc, other_attack, special, is_for_listing,
"filter_opponent", opp_check_if_recursion))
2300 bool applied_to_attacker = applied_both || (whom_is_self && is_attacker) || (!whom_is_self && !is_attacker);
2301 std::string att_check_if_recursion = applied_to_attacker ? tag_name :
"";
2302 if (!special_unit_matches(att, def, att_loc, att_weapon, special, is_for_listing,
"filter_attacker", att_check_if_recursion))
2304 bool applied_to_defender = applied_both || (whom_is_self && !is_attacker) || (!whom_is_self && is_attacker);
2305 std::string def_check_if_recursion= applied_to_defender ? tag_name :
"";
2306 if (!special_unit_matches(def, att, def_loc, def_weapon, special, is_for_listing,
"filter_defender", def_check_if_recursion))
2339 return (cond_eq.
empty() || def == cond_eq.
to_int()) &&
2352 if(tag_name ==
"plague") {
2354 const auto iter =
unit_types.
types().find(ability_or_special[
"type"]);
2375 std::map<std::string,individual_effect> values_add;
2376 std::map<std::string,individual_effect> values_sub;
2377 std::map<std::string,individual_effect> values_mul;
2378 std::map<std::string,individual_effect> values_div;
2382 utils::optional<int> max_value = utils::nullopt;
2383 utils::optional<int> min_value = utils::nullopt;
2386 const config&
cfg = *ability.ability_cfg;
2387 const std::string& effect_id =
cfg[
cfg[
"id"].
empty() ?
"name" :
"id"];
2395 callable.add(
"base_value", wfl::variant(def));
2396 return std::round(formula.evaluate(callable).as_int());
2399 int value_cum =
cfg[
"cumulative"].to_bool() ? std::max(def, value) : value;
2402 set_effect_min.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2403 set_effect_max.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2406 if(value_cum > set_effect_max.
value) {
2407 set_effect_max.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2409 if(value_cum < set_effect_min.
value) {
2410 set_effect_min.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2418 max_value = max_value ? std::min(*max_value,
cfg[
"max_value"].to_int()) :
cfg[
"max_value"].to_int();
2421 min_value = min_value ? std::max(*min_value,
cfg[
"min_value"].to_int()) :
cfg[
"min_value"].to_int();
2427 callable.add(
"base_value", wfl::variant(def));
2428 return std::round(formula.evaluate(callable).as_int());
2431 if(add_effect == values_add.end() || add > add_effect->second.value) {
2432 values_add[effect_id].set(
ADD, add, ability.ability_cfg, ability.teacher_loc);
2437 callable.add(
"base_value", wfl::variant(def));
2438 return std::round(formula.evaluate(callable).as_int());
2441 if(sub_effect == values_sub.end() || sub < sub_effect->second.value) {
2442 values_sub[effect_id].set(
ADD, sub, ability.ability_cfg, ability.teacher_loc);
2447 callable.add(
"base_value", wfl::variant(def));
2448 return formula.evaluate(callable).as_decimal() / 1000.0 ;
2451 if(mul_effect == values_mul.end() || multiply > mul_effect->second.value) {
2452 values_mul[effect_id].set(
MUL, multiply, ability.ability_cfg, ability.teacher_loc);
2457 callable.add(
"base_value", wfl::variant(def));
2458 return formula.evaluate(callable).as_decimal() / 1000.0 ;
2462 ERR_NG <<
"division by zero with divide= in ability/weapon special " << effect_id;
2466 if(div_effect == values_div.end() || divide > div_effect->second.value) {
2467 values_div[effect_id].set(
DIV, divide, ability.ability_cfg, ability.teacher_loc);
2474 value_set = std::max(set_effect_max.
value, 0) + std::min(set_effect_min.
value, 0);
2475 if(set_effect_max.
value > def) {
2478 if(set_effect_min.
value < def) {
2491 double multiplier = 1.0;
2492 double divisor = 1.0;
2494 for(
const auto& val : values_mul) {
2495 multiplier *= val.second.value/100.0;
2499 for(
const auto& val : values_div) {
2500 divisor *= val.second.value/100.0;
2505 for(
const auto& val : values_add) {
2506 addition += val.second.value;
2512 int substraction = 0;
2513 for(
const auto& val : values_sub) {
2514 substraction += val.second.value;
2520 if(max_value && min_value && *min_value < *max_value) {
2522 }
else if(max_value && !min_value) {
2524 }
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.
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 ...
static bool special_distant_filtering_impl(const const_attack_ptr &self_attack, const unit_const_ptr &self, const map_location &self_loc, const const_attack_ptr &other_attack, AFFECTS whom, const config &filter, bool sub_filter, bool leader_bool=false)
special_distant_filtering_impl : return an boolean value if special matche with filter
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.
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...
static bool has_ability_impl(const const_attack_ptr &self_attack, const unit_const_ptr &self, const map_location &self_loc, const const_attack_ptr &other_attack, AFFECTS whom, const std::string &special)
has_ability_impl : return an boolean value for checking of activities of abilities used like weapon
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.
std::size_t underlying_id() const
This unit's unique internal ID.
const t_string & name() const
Gets this unit's translatable display name.
@ STATE_POISONED
The unit is slowed - it moves slower and does less damage.
std::vector< std::string > halo_or_icon_abilities(const std::string &image_type) const
New lexcical_cast header.
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