53 #define ERR_NG LOG_STREAM(err, log_engine)
56 #define ERR_WML LOG_STREAM(err, log_wml)
59 class temporary_facing
69 u_->set_facing(new_dir);
75 u_->set_facing(save_dir_);
152 const team& get_team(std::size_t side)
172 bool affects_side(
const config&
cfg, std::size_t side, std::size_t other_side)
174 const team& side_team = get_team(side);
176 if(side == other_side)
177 return cfg[
"affect_allies"].to_bool(
true);
179 return cfg[
"affect_enemies"].to_bool();
181 return cfg[
"affect_allies"].to_bool();
191 for(std::size_t j = 0; j < adjacent.size(); ++j) {
192 bool adj_or_dist = distance != 1 ?
distance_between(adjacent[j],
loc) == (distance - 1) : adjacent[j] ==
loc;
215 const unit_map& units = get_unit_map();
221 for(
const unit& u : units) {
222 if(!u.max_ability_radius() || u.incapacitated() || u.underlying_id() ==
underlying_id() || !u.max_ability_radius_type(tag_name)) {
227 if(distance > u.max_ability_radius_type(tag_name)) {
230 int dir = find_direction(
loc, from_loc, distance);
231 for(
const config&
i : u.abilities_.child_range(tag_name)) {
257 const unit_map& units = get_unit_map();
263 for(
const unit& u : units) {
264 if(!u.max_ability_radius() || u.incapacitated() || u.underlying_id() ==
underlying_id() || !u.max_ability_radius_type(tag_name)) {
269 if(distance > u.max_ability_radius_type(tag_name)) {
272 int dir = find_direction(
loc, from_loc, distance);
273 for(
const config&
i : u.abilities_.child_range(tag_name)) {
295 std::vector<std::string> res;
298 std::string
id =
cfg[
"id"];
300 res.push_back(std::move(
id));
313 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)
330 "female_name_inactive",
"name_inactive");
351 std::vector<std::tuple<std::string, t_string,t_string,t_string>> res;
355 add_ability_tooltip(
cfg, tag_name,
gender_, res,
true);
363 std::vector<std::tuple<std::string, t_string,t_string,t_string>> res;
369 if(add_ability_tooltip(
cfg, tag_name,
gender_, res, active))
371 active_list.push_back(active);
388 static std::vector<std::tuple<std::string, std::string>> already_shown;
390 auto identifier = std::tuple<std::string, std::string>{
unit.
id(),
filter.debug()};
395 std::string_view filter_text_view = std::get<1>(identifier);
397 ERR_NG <<
"Looped recursion error for unit '" <<
unit.
id()
398 <<
"' while checking ability '" << filter_text_view <<
"'";
402 if(already_shown.size() > 100) {
403 already_shown.clear();
405 already_shown.push_back(std::move(identifier));
420 : parent(u.shared_from_this())
430 unit::recursion_guard::operator bool()
const {
436 assert(
this != &other);
445 assert(!parent->open_queries_.empty());
446 parent->open_queries_.pop_back();
454 show_recursion_warning(*
this,
cfg);
464 const unit_map& units = get_unit_map();
467 const std::string& filter_adjacent = in_abilities_tag ?
"filter_adjacent_student" :
"filter_adjacent";
468 const std::string& filter_adjacent_location = in_abilities_tag ?
"filter_adjacent_student_location" :
"filter_adjacent_location";
471 std::size_t radius =
i[
"radius"].to_int(1);
472 std::size_t count = 0;
474 ufilt.set_use_flat_tod(illuminates);
475 for(
const unit& u : units) {
478 if(u.underlying_id() ==
self.underlying_id() || distance > radius || !ufilt(u,
self)) {
482 for(
unsigned j = 0; j < adjacent.size(); ++j) {
483 bool adj_or_dist = distance != 1 ?
distance_between(adjacent[j], from_loc) == (distance - 1) : adjacent[j] == from_loc;
489 assert(dir >= 0 && dir <= 5);
491 if(
i.has_attribute(
"adjacent")) {
496 if(
i.has_attribute(
"is_enemy")) {
505 if(
i[
"count"].empty() && count == 0) {
514 std::size_t count = 0;
516 adj_filter.flatten(illuminates);
520 if(!adj_filter.match(adjacent[
static_cast<int>(
index)])) {
537 bool illuminates = ability ==
"illuminates";
555 bool illuminates = ability ==
"illuminates";
557 assert(dir >=0 && dir <= 5);
559 std::size_t radius = 1;
563 if(
i[
"radius"] !=
"all_map") {
564 radius =
i[
"radius"].to_int(1);
572 if (
i.has_attribute(
"adjacent")) {
577 auto filter =
i.optional_child(
"filter");
589 bool affect_self =
cfg[
"affect_self"].to_bool(
true);
590 if (!
filter || !affect_self)
return affect_self;
596 const std::string filter_tag_name = is_opp ?
"filter_second_weapon" :
"filter_weapon";
605 filter_lock = weapon->update_variables_recursion(
cfg);
607 show_recursion_warning(*
this,
cfg);
610 return weapon->matches_filter(
filter);
623 image_list.push_back(
cfg[attribute_name].str());
629 std::vector<std::string> image_list;
637 if(!
cfg[image_type +
"_image_self"].str().empty() &&
is_active){
642 const unit_map& units = get_unit_map();
644 for(
const unit& u : units) {
645 if(!u.max_ability_radius_image() || u.incapacitated() || u.underlying_id() ==
underlying_id()) {
650 if(distance > u.max_ability_radius_image()) {
653 int dir = find_direction(
loc_, from_loc, distance);
654 for(
const auto [key,
cfg] : u.abilities_.all_children_view()) {
662 if(image_list.size() >= 2){
663 std::sort(image_list.begin(), image_list.end());
671 callable.
add(
"attacker",
wfl::variant(std::make_shared<wfl::unit_callable>(*att)));
674 callable.
add(
"defender",
wfl::variant(std::make_shared<wfl::unit_callable>(*def)));
681 template<
typename T,
typename TFuncFormula>
682 class get_ability_value_visitor
683 #ifdef USING_BOOST_VARIANT
684 :
public boost::static_visitor<T>
689 get_ability_value_visitor(T def,
const TFuncFormula& formula_handler) : def_(def), formula_handler_(formula_handler) {}
691 T operator()(
const utils::monostate&)
const {
return def_; }
692 T operator()(
bool)
const {
return def_; }
693 T operator()(
int i)
const {
return static_cast<T
>(
i); }
694 T operator()(
unsigned long long u)
const {
return static_cast<T
>(u); }
695 T operator()(
double d)
const {
return static_cast<T
>(
d); }
696 T operator()(
const t_string&)
const {
return def_; }
697 T operator()(
const std::string&
s)
const
699 if(
s.size() >= 2 &&
s[0] ==
'(') {
700 return formula_handler_(
s);
702 return lexical_cast_default<T>(
s, def_);
707 const TFuncFormula& formula_handler_;
710 template<
typename T,
typename TFuncFormula>
713 return v.
apply_visitor(get_ability_value_visitor(def, [&](
const std::string&
s) {
716 const unit_map& units = get_unit_map();
720 if(u_itor == units.
end()) {
725 att->add_formula_context(callable);
728 callable.add(
"student",
wfl::variant(std::make_shared<wfl::unit_callable>(*uptr)));
731 callable.add(
"other",
wfl::variant(std::make_shared<wfl::unit_callable>(*uptr)));
735 lg::log_to_chat() <<
"Formula error in ability or weapon special: " <<
e.type <<
" at " <<
e.filename <<
':' <<
e.line <<
")\n";
736 ERR_WML <<
"Formula error in ability or weapon special: " <<
e.type <<
" at " <<
e.filename <<
':' <<
e.line <<
")";
743 template<
typename TComp>
746 if ( cfgs_.empty() ) {
752 bool only_cumulative =
true;
762 if ((*
p.ability_cfg)[
"cumulative"].to_bool()) {
764 if (value < 0) value = -value;
765 if (only_cumulative && !comp(value, abs_max)) {
767 best_loc =
p.teacher_loc;
769 }
else if (only_cumulative || comp(flat, value)) {
770 only_cumulative =
false;
772 best_loc =
p.teacher_loc;
775 return std::pair(flat + stack, best_loc);
778 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;
779 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;
818 if(simple_check && specials().has_child(special)) {
822 for(
const config &
i : specials().child_range(special)) {
823 if(special_active(
i, AFFECT_SELF, special)) {
829 if ( simple_check || !other_attack_ ) {
833 for(
const config &
i : other_attack_->specials().child_range(special)) {
834 if(other_attack_->special_active(
i, AFFECT_OTHER, special)) {
851 for(
const config&
i : specials_.child_range(special)) {
852 if(special_active(
i, AFFECT_SELF, special)) {
861 for(
const config&
i : other_attack_->specials_.child_range(special)) {
862 if(other_attack_->special_active(
i, AFFECT_OTHER, special)) {
879 boost::dynamic_bitset<>* active_list)
const
882 std::vector<std::pair<t_string, t_string>> res;
884 active_list->clear();
887 for(
const auto [key,
cfg] : specials_.all_children_view()) {
888 bool active = !active_list || special_active(
cfg, AFFECT_EITHER, key);
890 std::string
name = active
898 std::string desc = active
899 ?
cfg[
"description"].str()
900 :
cfg.
get_or(
"description_inactive",
"description").
str();
908 active_list->push_back(active);
915 boost::dynamic_bitset<>* active_list)
const
917 std::vector<std::pair<t_string, t_string>> res;
919 active_list->clear();
921 std::set<std::string> checking_name;
925 for(
const auto [key,
cfg] : self_->abilities().all_children_view()) {
926 if(!active_list || check_self_abilities_impl(shared_from_this(), other_attack_,
cfg, self_, self_loc_, AFFECT_SELF, key,
false)) {
927 const std::string
name =
cfg[
"name_affected"];
928 const std::string desc =
cfg[
"description_affected"];
933 res.emplace_back(
name, desc);
936 active_list->push_back(
true);
940 for(
const unit& u : get_unit_map()) {
941 if(!u.max_ability_radius() || u.incapacitated() || u.underlying_id() == self_->underlying_id()) {
946 if(distance > u.max_ability_radius()) {
949 int dir = find_direction(self_loc_, from_loc, distance);
950 for(
const auto [key,
cfg] : u.abilities().all_children_view()) {
951 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)) {
952 const std::string
name =
cfg[
"name_affected"];
953 const std::string desc =
cfg[
"description_affected"];
958 res.emplace_back(
name, desc);
961 active_list->push_back(
true);
977 static void add_name(std::string& temp_string,
bool active,
const std::string&
name, std::set<std::string>& checking_name)
979 if (active && !
name.
empty() && checking_name.count(
name) == 0) {
980 checking_name.insert(
name);
981 if (!temp_string.empty()) temp_string +=
", ";
996 std::vector<std::string> specials;
998 for(
const auto [key,
cfg] : specials_.all_children_view()) {
999 const bool active = special_active(
cfg, AFFECT_EITHER, key);
1001 std::string
name = active
1015 std::string temp_string;
1016 std::set<std::string> checking_name;
1017 weapon_specials_impl_self(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name);
1018 weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name, {},
"affect_allies");
1020 if(!temp_string.empty()) {
1021 specials.push_back(
"\n" + std::move(temp_string));
1027 static void add_name_list(std::string& temp_string, std::string& weapon_abilities, std::set<std::string>& checking_name,
const std::string& from_str)
1029 if(!temp_string.empty()){
1030 temp_string = from_str.c_str() + temp_string;
1031 weapon_abilities += (!weapon_abilities.empty() && !temp_string.empty()) ?
"\n" :
"";
1032 weapon_abilities += temp_string;
1033 temp_string.clear();
1034 checking_name.clear();
1041 std::string temp_string, weapon_abilities;
1042 std::set<std::string> checking_name;
1043 for(
const auto [key,
cfg] : specials_.all_children_view()) {
1044 if(checking_tags.count(key) != 0) {
1045 const bool active = special_active(
cfg, AFFECT_SELF, key);
1046 add_name(temp_string, active,
cfg[
"name"].str(), checking_name);
1049 add_name_list(temp_string, weapon_abilities, checking_name,
"");
1051 weapon_specials_impl_self(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name, checking_tags,
true);
1052 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Owned: "));
1054 weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name, checking_tags,
"affect_allies",
true);
1056 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Taught: "));
1058 weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name, checking_tags,
"affect_enemies",
true);
1060 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Taught: (by an enemy): "));
1064 for(
const auto [key,
cfg] : other_attack_->specials_.all_children_view()) {
1065 if((checking_tags.count(key) != 0)){
1066 const bool active = other_attack_->special_active(
cfg, AFFECT_OTHER, key);
1067 add_name(temp_string, active,
cfg[
"name"].str(), checking_name);
1071 weapon_specials_impl_self(temp_string, other_, other_attack_, shared_from_this(), other_loc_, AFFECT_OTHER, checking_name, checking_tags);
1072 weapon_specials_impl_adj(temp_string, other_, other_attack_, shared_from_this(), other_loc_, AFFECT_OTHER, checking_name, checking_tags);
1073 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Used by opponent: "));
1075 return weapon_abilities;
1079 std::string& temp_string,
1085 std::set<std::string>& checking_name,
1086 const std::set<std::string>& checking_tags,
1090 for(
const auto [key,
cfg] : self->abilities().all_children_view()){
1091 bool tag_checked = (!checking_tags.empty()) ? (checking_tags.count(key) != 0) :
true;
1092 const bool active = tag_checked && check_self_abilities_impl(self_attack, other_attack,
cfg,
self, self_loc, whom, key, leader_bool);
1099 std::string& temp_string,
1105 std::set<std::string>& checking_name,
1106 const std::set<std::string>& checking_tags,
1107 const std::string& affect_adjacents,
1110 const unit_map& units = get_unit_map();
1112 for(
const unit& u : units) {
1113 if(!u.max_ability_radius() || u.incapacitated() || u.underlying_id() == self->underlying_id()) {
1118 if(distance > u.max_ability_radius()) {
1121 int dir = find_direction(self_loc, from_loc, distance);
1122 for(
const auto [key,
cfg] : u.abilities().all_children_view()) {
1123 bool tag_checked = !checking_tags.empty() ? checking_tags.count(key) != 0 :
true;
1124 bool default_bool = affect_adjacents ==
"affect_allies" ? true :
false;
1125 bool affect_allies = !affect_adjacents.empty() ?
cfg[affect_adjacents].to_bool(default_bool) :
true;
1126 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;
1153 : parent(weapon.shared_from_this())
1155 weapon.
self_ = std::move(
self);
1156 weapon.
other_ = std::move(other);
1173 : parent(weapon.shared_from_this())
1175 weapon.
self_ = std::move(
self);
1193 : parent(weapon.shared_from_this())
1205 : parent(weapon.shared_from_this())
1213 if(was_moved)
return;
1218 parent->is_attacker_ =
false;
1219 parent->other_attack_ =
nullptr;
1220 parent->is_for_listing_ =
false;
1224 : parent(std::move(other.parent))
1226 other.was_moved =
true;
1237 unsigned & max_attacks)
const
1242 if ( attacks_value < 0 ) {
1244 ERR_NG <<
"negative number of strikes after applying weapon specials";
1249 if ( !swarm_specials.
empty() ) {
1250 min_attacks = std::max<int>(0, swarm_specials.
highest(
"swarm_attacks_min").first);
1251 max_attacks = std::max<int>(0, swarm_specials.
highest(
"swarm_attacks_max", attacks_value).first);
1253 min_attacks = max_attacks = attacks_value;
1259 std::map<std::string, unsigned int> type_count;
1260 unsigned int max = 0;
1261 for(
auto&
i : damage_type_list) {
1263 if(
c.has_attribute(
"replacement_type")) {
1264 std::string
type =
c[
"replacement_type"].str();
1265 unsigned int count = ++type_count[
type];
1272 if (type_count.empty())
return type();
1274 std::vector<std::string> type_list;
1275 for(
auto&
i : type_count){
1276 if(
i.second == max){
1277 type_list.push_back(
i.first);
1281 if(type_list.empty())
return type();
1283 return type_list.front();
1288 std::map<std::string, int> type_res;
1289 int max_res = INT_MIN;
1291 for(
auto&
i : damage_type_list) {
1293 if(
c.has_attribute(
"alternative_type")) {
1294 std::string
type =
c[
"alternative_type"].str();
1295 if(type_res.count(
type) == 0){
1296 type_res[
type] = (*other_).resistance_value(resistance_list,
type);
1297 max_res = std::max(max_res, type_res[
type]);
1303 if (type_res.empty())
return {
"", INT_MIN};
1305 std::vector<std::string> type_list;
1306 for(
auto&
i : type_res){
1307 if(
i.second == max_res){
1308 type_list.push_back(
i.first);
1311 if(type_list.empty())
return {
"", INT_MIN};
1313 return {type_list.front(), max_res};
1326 resistance_list = (*other_).get_abilities_weapons(
"resistance",
other_loc_,
other_attack_, shared_from_this());
1328 return (!((*
i.ability_cfg)[
"active_on"].empty() || (!
is_attacker_ && (*
i.ability_cfg)[
"active_on"] ==
"offense") || (
is_attacker_ && (*
i.ability_cfg)[
"active_on"] ==
"defense")));
1332 int res =
other_ ? (*other_).resistance_value(resistance_list,
type()) : 100;
1333 if(damage_type_list.
empty()){
1334 return {
type(), res};
1340 res = replacement_type !=
type() ? (*other_).resistance_value(resistance_list, replacement_type) : res;
1341 replacement_type = alternative_type.second > res ? alternative_type.first : replacement_type;
1342 res = std::max(res, alternative_type.second);
1344 return {replacement_type, res};
1353 std::set<std::string> alternative_damage_types;
1354 if(damage_type_list.
empty()){
1355 return {
type(), alternative_damage_types};
1358 for(
auto&
i : damage_type_list) {
1360 if(
c.has_attribute(
"alternative_type")){
1361 alternative_damage_types.insert(
c[
"alternative_type"].str());
1365 return {replacement_type, alternative_damage_types};
1374 return damage_value;
1394 bool special_affects_opponent(
const config& special,
bool is_attacker)
1397 const std::string& apply_to = special[
"apply_to"];
1398 if ( apply_to.empty() )
1400 if ( apply_to ==
"both" )
1402 if ( apply_to ==
"opponent" )
1404 if ( is_attacker && apply_to ==
"defender" )
1406 if ( !is_attacker && apply_to ==
"attacker" )
1416 bool special_affects_self(
const config& special,
bool is_attacker)
1419 const std::string& apply_to = special[
"apply_to"];
1420 if ( apply_to.empty() )
1422 if ( apply_to ==
"both" )
1424 if ( apply_to ==
"self" )
1426 if ( is_attacker && apply_to ==
"attacker" )
1428 if ( !is_attacker && apply_to ==
"defender")
1443 static std::vector<std::tuple<std::string, std::string>> already_shown;
1445 auto identifier = std::tuple<std::string, std::string>{attack->id(),
filter.debug()};
1450 std::string_view filter_text_view = std::get<1>(identifier);
1452 ERR_NG <<
"Looped recursion error for weapon '" << attack->id()
1453 <<
"' while checking weapon special '" << filter_text_view <<
"'";
1457 if(already_shown.size() > 100) {
1458 already_shown.clear();
1460 already_shown.push_back(std::move(identifier));
1480 const bool for_listing,
1481 const std::string & child_tag,
const std::string& check_if_recursion)
1492 if (!
filter[
"backstab"].
blank() && child_tag ==
"filter_opponent") {
1493 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.");
1496 if(
filter[
"backstab"].to_bool() && child_tag ==
"filter_opponent"){
1497 const std::string& backstab_formula =
"enemy_of(self, flanker) and not flanker.petrified where flanker = unit_at(direction_from(loc, other.facing))";
1499 if(!
filter.has_child(
"filter_opponent")){
1500 filter_child[
"formula"] = backstab_formula;
1503 filter_opponent[
"formula"] = backstab_formula;
1504 filter_child.
add_child(
"and", filter_opponent);
1510 if ( !filter_child )
1526 filter_lock = weapon->update_variables_recursion(
filter);
1528 show_recursion_warning(weapon,
filter);
1533 if (
auto filter_weapon = filter_child->
optional_child(
"filter_weapon") ) {
1534 if ( !weapon || !weapon->matches_filter(*filter_weapon, check_if_recursion) )
1541 return ufilt.matches(*u,
loc);
1543 return ufilt.matches(*u,
loc, *u2);
1561 return special_active(*i.ability_cfg, AFFECT_SELF, ability, true);
1567 return special_active_impl(other_attack_, shared_from_this(), *i.ability_cfg, AFFECT_OTHER, ability, true);
1582 if(!abil_list.
empty() && !overwriters.
empty()){
1598 const std::string& apply_to = special[
"overwrite_specials"];
1599 return (apply_to ==
"one_side" || apply_to ==
"both_sides");
1610 if(overwriters.
empty()){
1615 if(overwriters.
size() >= 2){
1618 auto oi = (*
i.ability_cfg).optional_child(
"overwrite");
1620 if(oi && !oi[
"priority"].empty()){
1621 l = oi[
"priority"].to_double(0);
1623 auto oj = (*j.
ability_cfg).optional_child(
"overwrite");
1625 if(oj && !oj[
"priority"].empty()){
1626 r = oj[
"priority"].to_double(0);
1640 if(overwriters.
empty()){
1644 for(
const auto& j : overwriters) {
1646 bool affect_side = ((*j.ability_cfg)[
"overwrite_specials"] ==
"one_side");
1648 auto overwrite_specials = (*j.ability_cfg).optional_child(
"overwrite");
1649 double priority = overwrite_specials ? overwrite_specials[
"priority"].to_double(0) : 0.00;
1654 bool prior = (priority > 0) ? (has_overwrite_specials && has_overwrite_specials[
"priority"].to_double(0) >= priority) :
true;
1658 bool one_side_overwritable =
true;
1662 if(affect_side && is_overwritable){
1663 if(special_affects_self(*j.ability_cfg,
is_attacker_)){
1666 else if(special_affects_opponent(*j.ability_cfg, !
is_attacker_)){
1672 bool special_matches =
true;
1673 if(overwrite_specials){
1674 auto overwrite_filter = (*overwrite_specials).optional_child(
"filter_specials");
1675 if(!overwrite_filter){
1676 overwrite_filter = (*overwrite_specials).optional_child(
"experimental_filter_specials");
1677 if(overwrite_filter){
1681 if(overwrite_filter && is_overwritable && one_side_overwritable){
1689 if(is_overwritable && one_side_overwritable && special_matches){
1700 show_recursion_warning(*
this,
cfg);
1703 return (ability_active_impl(ability,
cfg,
loc) && ability_affects_self(ability,
cfg,
loc));
1710 show_recursion_warning(from,
cfg);
1718 return (get_self_ability_bool(special, tag_name,
loc) && ability_affects_weapon(special, weapon,
false) && ability_affects_weapon(special, opp_weapon,
true));
1723 return (get_adj_ability_bool(special, tag_name, dist, dir,
loc, from, from_loc) && ability_affects_weapon(special, weapon,
false) && ability_affects_weapon(special, opp_weapon,
true));
1728 if(tag_name ==
"leadership" && leader_bool){
1729 if(u->get_self_ability_bool_weapon(special, tag_name,
loc, self_attack, other_attack)) {
1733 if(abilities_list::all_weapon_tags().count(tag_name) != 0){
1734 if(u->get_self_ability_bool(special, tag_name,
loc) &&
special_active_impl(self_attack, other_attack, special, whom, tag_name,
true)) {
1741 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)
1743 if(tag_name ==
"leadership" && leader_bool) {
1744 if(u->get_adj_ability_bool_weapon(special, tag_name, dist, dir,
loc, from, from_loc, self_attack, other_attack)) {
1748 if(abilities_list::all_weapon_tags().count(tag_name) != 0) {
1749 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)) {
1762 const std::string& special)
1764 for(
const config &
i : self->abilities().child_range(special)) {
1769 const unit_map& units = get_unit_map();
1770 for(
const unit& u : units) {
1771 if(!u.max_ability_radius() || u.incapacitated() || u.underlying_id() == self->underlying_id() || !u.max_ability_radius_type(special)) {
1776 if(distance > u.max_ability_radius_type(special)) {
1779 int dir = find_direction(self_loc, from_loc, distance);
1780 for(
const config &
i : u.abilities().child_range(special)) {
1781 if(
check_adj_abilities_impl(self_attack, other_attack,
i,
self, u, distance, dir, self_loc, from_loc, whom, special)) {
1800 if(
range().empty()){
1820 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)
1822 if(!filter_special.empty() && filter_special.count(special_id) == 0 && filter_special.count(tag_name) == 0)
1825 if(!filter_special_id.empty() && filter_special_id.count(special_id) == 0)
1828 if(!filter_special_type.empty() && filter_special_type.count(tag_name) == 0)
1848 const unit_map& units = get_unit_map();
1849 bool check_adjacent = sub_filter ?
filter[
"affect_adjacent"].to_bool(
true) :
true;
1850 for(
const auto [key,
cfg] : self->abilities().all_children_view()) {
1851 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);
1856 if(check_adjacent) {
1857 for(
const unit& u : units) {
1858 if(!u.max_ability_radius() || u.incapacitated() || u.underlying_id() == self->underlying_id()) {
1863 if(distance > u.max_ability_radius()) {
1866 int dir = find_direction(self_loc, from_loc, distance);
1868 for(
const auto [key,
cfg] : u.abilities().all_children_view()) {
1869 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);
1870 if(special_check &&
check_adj_abilities_impl(self_attack, other_attack,
cfg,
self, u, distance, dir, self_loc, from_loc, whom, key, leader_bool)) {
1881 if(
range().empty()){
1888 for(
const auto [key,
cfg] :
specials().all_children_view()) {
1889 if(special_checking(
cfg[
"id"].str(), key, filter_special, filter_special_id, filter_special_type)) {
1899 for(
const auto [key,
cfg] :
other_attack_->specials().all_children_view()) {
1900 if(special_checking(
cfg[
"id"].str(), key, filter_special, filter_special_id, filter_special_type)) {
1924 bool exclude_ability_attributes(
const std::string& tag_name,
const config &
filter)
1927 bool abilities_check = abilities_list::ability_value_tags().count(tag_name) != 0 || abilities_list::ability_no_value_tags().count(tag_name) != 0;
1928 if(
filter.has_attribute(
"active_on") && tag_name !=
"resistance" && abilities_check)
1930 if(
filter.has_attribute(
"apply_to") && tag_name !=
"resistance" && abilities_check)
1933 if(
filter.has_attribute(
"overwrite_specials") && abilities_list::weapon_number_tags().count(tag_name) == 0)
1936 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;
1937 if(
filter.has_attribute(
"value") && no_value_weapon_abilities_check)
1939 if(
filter.has_attribute(
"add") && no_value_weapon_abilities_check)
1941 if(
filter.has_attribute(
"sub") && no_value_weapon_abilities_check)
1943 if(
filter.has_attribute(
"multiply") && no_value_weapon_abilities_check)
1945 if(
filter.has_attribute(
"divide") && no_value_weapon_abilities_check)
1948 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;
1949 if(
filter.has_attribute(
"replacement_type") && tag_name !=
"damage_type" && all_engine)
1951 if(
filter.has_attribute(
"alternative_type") && tag_name !=
"damage_type" && all_engine)
1953 if(
filter.has_attribute(
"type") && tag_name !=
"plague" && all_engine)
1964 if(!exclude_ability_attributes(tag_name,
filter))
1978 if(!
filter[
"affect_adjacent"].empty()){
1980 if(
filter[
"affect_adjacent"].to_bool() != adjacent){
2012 if(!
filter[
"value"].empty()){
2013 if(tag_name ==
"drains"){
2017 }
else if(tag_name ==
"berserk"){
2021 }
else if(tag_name ==
"heal_on_hit" || tag_name ==
"heals" || tag_name ==
"regenerate" || tag_name ==
"leadership"){
2047 if(tag_name ==
"resistance"){
2076 auto fwml =
filter.optional_child(
"filter_wml");
2087 static bool common_matches_filter(
const config &
cfg,
const std::string& tag_name,
const config &
filter)
2090 bool matches = matches_ability_filter(
cfg, tag_name,
filter);
2093 for(
const auto [key, condition_cfg] :
filter.all_children_view() )
2097 matches = matches && common_matches_filter(
cfg, tag_name, condition_cfg);
2100 else if ( key ==
"or" )
2101 matches = matches || common_matches_filter(
cfg, tag_name, condition_cfg);
2104 else if ( key ==
"not" )
2105 matches = matches && !common_matches_filter(
cfg, tag_name, condition_cfg);
2114 return common_matches_filter(
cfg, tag_name,
filter);
2119 return common_matches_filter(
cfg, tag_name,
filter);
2124 if(
range().empty()){
2128 bool check_if_active =
filter[
"active"].to_bool();
2129 for(
const auto [key,
cfg] :
specials().all_children_view()) {
2131 if(!check_if_active) {
2141 for(
const auto [key,
cfg] :
other_attack_->specials().all_children_view()) {
2149 if(!check_if_active){
2164 bool in_abilities_tag)
const
2184 const std::string& tag_name,
2185 bool in_abilities_tag)
2187 assert(self_attack || other_attack);
2188 bool is_attacker = self_attack ? self_attack->is_attacker_ : !other_attack->is_attacker_;
2189 bool is_for_listing = self_attack ? self_attack->is_for_listing_ : other_attack->is_for_listing_;
2195 if ( !special_affects_self(special, is_attacker) )
2199 if ( !special_affects_opponent(special, is_attacker) )
2204 const std::string & active_on = special[
"active_on"];
2205 if ( !active_on.empty() ) {
2206 if ( is_attacker && active_on !=
"offense" )
2208 if ( !is_attacker && active_on !=
"defense" )
2213 const unit_map& units = get_unit_map();
2215 unit_const_ptr self = self_attack ? self_attack->self_ : other_attack->other_;
2216 unit_const_ptr other = self_attack ? self_attack->other_ : other_attack->self_;
2217 map_location self_loc = self_attack ? self_attack->self_loc_ : other_attack->other_loc_;
2218 map_location other_loc = self_attack ? self_attack->other_loc_ : other_attack->self_loc_;
2220 if(
self ==
nullptr) {
2226 if(other ==
nullptr) {
2235 temporary_facing other_facing(other, other_loc.
get_relative_dir(self_loc));
2239 bool whom_is_self = ((whom ==
AFFECT_SELF) || ((whom ==
AFFECT_EITHER) && special_affects_self(special, is_attacker)));
2241 map_location their_loc = whom_is_self ? other_loc : self_loc;
2243 if (tag_name ==
"drains" && them && them->get_state(
"undrainable")) {
2246 if (tag_name ==
"plague" && them &&
2247 (them->get_state(
"unplagueable") ||
2251 if (tag_name ==
"poison" && them &&
2255 if (tag_name ==
"slow" && them &&
2259 if (tag_name ==
"petrifies" && them &&
2260 them->get_state(
"unpetrifiable")) {
2268 const map_location & att_loc = is_attacker ? self_loc : other_loc;
2269 const map_location & def_loc = is_attacker ? other_loc : self_loc;
2270 const const_attack_ptr& att_weapon = is_attacker ? self_attack : other_attack;
2271 const const_attack_ptr& def_weapon = is_attacker ? other_attack : self_attack;
2275 if (tag_name ==
"firststrike") {
2276 bool whom_is_defender = whom_is_self ? !is_attacker : is_attacker;
2277 if (whom_is_defender && att_weapon && att_weapon->has_special_or_ability(
"firststrike"))
2286 bool applied_both = special[
"apply_to"] ==
"both";
2287 const std::string& filter_self = in_abilities_tag ?
"filter_student" :
"filter_self";
2288 std::string self_check_if_recursion = (applied_both || whom_is_self) ? tag_name :
"";
2289 if (!special_unit_matches(
self, other, self_loc, self_attack, special, is_for_listing, filter_self, self_check_if_recursion))
2291 std::string opp_check_if_recursion = (applied_both || !whom_is_self) ? tag_name :
"";
2292 if (!special_unit_matches(other,
self, other_loc, other_attack, special, is_for_listing,
"filter_opponent", opp_check_if_recursion))
2296 bool applied_to_attacker = applied_both || (whom_is_self && is_attacker) || (!whom_is_self && !is_attacker);
2297 std::string att_check_if_recursion = applied_to_attacker ? tag_name :
"";
2298 if (!special_unit_matches(att, def, att_loc, att_weapon, special, is_for_listing,
"filter_attacker", att_check_if_recursion))
2300 bool applied_to_defender = applied_both || (whom_is_self && !is_attacker) || (!whom_is_self && is_attacker);
2301 std::string def_check_if_recursion= applied_to_defender ? tag_name :
"";
2302 if (!special_unit_matches(def, att, def_loc, def_weapon, special, is_for_listing,
"filter_defender", def_check_if_recursion))
2335 return (cond_eq.
empty() || def == cond_eq.
to_int()) &&
2348 if(tag_name ==
"plague") {
2350 const auto iter =
unit_types.
types().find(ability_or_special[
"type"]);
2376 return formula.
evaluate(callable).as_decimal() / 1000.0 ;
2386 int value_set = def;
2387 std::map<std::string,individual_effect> values_add;
2388 std::map<std::string,individual_effect> values_sub;
2389 std::map<std::string,individual_effect> values_mul;
2390 std::map<std::string,individual_effect> values_div;
2395 utils::optional<int> max_value = utils::nullopt;
2396 utils::optional<int> min_value = utils::nullopt;
2399 const config&
cfg = *ability.ability_cfg;
2400 const std::string& effect_id =
cfg[
cfg[
"id"].
empty() ?
"name" :
"id"];
2407 int value_cum = wham !=
EFFECT_CUMULABLE &&
cfg[
"cumulative"].to_bool() ? std::max(def, value) : value;
2409 set_effect_cum.
set(
SET, set_effect_cum.
value + value_cum, ability.ability_cfg, ability.teacher_loc);
2411 set_effect_cum.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2415 set_effect_min.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2416 set_effect_max.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2419 if(value_cum > set_effect_max.
value) {
2420 set_effect_max.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2422 if(value_cum < set_effect_min.
value) {
2423 set_effect_min.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2432 max_value = max_value ? std::min(*max_value, value) : value;
2436 min_value = min_value ? std::max(*min_value, value) : value;
2443 if(add_effect == values_add.end() || add > add_effect->second.value) {
2444 values_add[effect_id].set(
ADD, add, ability.ability_cfg, ability.teacher_loc);
2450 if(sub_effect == values_sub.end() || sub < sub_effect->second.value) {
2451 values_sub[effect_id].set(
ADD, sub, ability.ability_cfg, ability.teacher_loc);
2457 if(mul_effect == values_mul.end() || multiply > mul_effect->second.value) {
2458 values_mul[effect_id].set(
MUL, multiply, ability.ability_cfg, ability.teacher_loc);
2465 ERR_NG <<
"division by zero with divide= in ability/weapon special " << effect_id;
2469 if(div_effect == values_div.end() || divide > div_effect->second.value) {
2470 values_div[effect_id].set(
DIV, divide, ability.ability_cfg, ability.teacher_loc);
2477 value_set = std::max(set_effect_max.
value, 0) + std::min(set_effect_min.
value, 0);
2478 if(set_effect_max.
value > def) {
2481 if(set_effect_min.
value < def) {
2494 double multiplier = 1.0;
2495 double divisor = 1.0;
2497 for(
const auto& val : values_mul) {
2498 multiplier *= val.second.value/100.0;
2502 for(
const auto& val : values_div) {
2503 divisor *= val.second.value/100.0;
2508 for(
const auto& val : values_add) {
2509 addition += val.second.value;
2515 int substraction = 0;
2516 for(
const auto& val : values_sub) {
2517 substraction += val.second.value;
2522 value_set += set_effect_cum.
value;
2528 if(max_value && min_value && *min_value < *max_value) {
2530 }
else if(max_value && !min_value) {
2532 }
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.
config & add_child(std::string_view key)
void insert(std::string_view key, T &&value)
Inserts an attribute into the config.
const attribute_value & get_or(const std::string_view key, const std::string_view default_key) const
Chooses a value.
optional_config_impl< config > optional_child(std::string_view key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
bool matches(const config &filter) const
config & child_or_add(std::string_view key)
Returns a reference to the first child with the given key.
auto all_children_view() const
In-order iteration over all children.
child_itors child_range(std::string_view key)
const attribute_value * get(std::string_view key) const
Returns a pointer to the attribute with the given key or nullptr if it does not exist.
bool has_child(std::string_view key) const
Determine whether a config has a child or not.
config & mandatory_child(std::string_view key, int n=0)
Returns the nth child with the given key, or throws an error if there is none.
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()
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) noexcept
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.
int individual_value_double(const config::attribute_value *v, int def, const unit_ability &ability, const map_location &loc, const const_attack_ptr &att)
bool filter_base_matches(const config &cfg, int def)
@ EFFECT_WITHOUT_CLAMP_MIN_MAX
int individual_value_int(const config::attribute_value *v, int def, const unit_ability &ability, const map_location &loc, const const_attack_ptr &att)
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