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(self_->get_self_ability_bool(
cfg, key, self_loc_) && special_tooltip_active(
cfg, key)) {
927 bool active = !active_list || special_active(
cfg, AFFECT_SELF, key);
928 const std::string
name =
cfg[
"name_affected"];
929 const std::string desc =
cfg[
"description_affected"];
934 res.emplace_back(
name, desc);
937 active_list->push_back(active);
941 for(
const unit& u : get_unit_map()) {
942 if(!u.max_ability_radius() || u.incapacitated() || u.underlying_id() == self_->underlying_id()) {
947 if(distance > u.max_ability_radius()) {
950 int dir = find_direction(self_loc_, from_loc, distance);
951 for(
const auto [key,
cfg] : u.abilities().all_children_view()) {
952 if(self_->get_adj_ability_bool(
cfg, key, distance, dir, self_loc_, u, from_loc) && special_tooltip_active(
cfg, key)) {
953 bool active = !active_list || special_active(
cfg, AFFECT_SELF, key);
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(active);
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;
1396 bool special_affects_opponent(
const config& special,
bool is_attacker)
1399 const std::string& apply_to = special[
"apply_to"];
1400 if ( apply_to.empty() )
1402 if ( apply_to ==
"both" )
1404 if ( apply_to ==
"opponent" )
1406 if ( is_attacker && apply_to ==
"defender" )
1408 if ( !is_attacker && apply_to ==
"attacker" )
1418 bool special_affects_self(
const config& special,
bool is_attacker)
1421 const std::string& apply_to = special[
"apply_to"];
1422 if ( apply_to.empty() )
1424 if ( apply_to ==
"both" )
1426 if ( apply_to ==
"self" )
1428 if ( is_attacker && apply_to ==
"attacker" )
1430 if ( !is_attacker && apply_to ==
"defender")
1445 static std::vector<std::tuple<std::string, std::string>> already_shown;
1447 auto identifier = std::tuple<std::string, std::string>{attack->id(),
filter.debug()};
1452 std::string_view filter_text_view = std::get<1>(identifier);
1454 ERR_NG <<
"Looped recursion error for weapon '" << attack->id()
1455 <<
"' while checking weapon special '" << filter_text_view <<
"'";
1459 if(already_shown.size() > 100) {
1460 already_shown.clear();
1462 already_shown.push_back(std::move(identifier));
1482 const bool for_listing,
1483 const std::string & child_tag,
const std::string& check_if_recursion)
1494 if (!
filter[
"backstab"].
blank() && child_tag ==
"filter_opponent") {
1495 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.");
1498 if(
filter[
"backstab"].to_bool() && child_tag ==
"filter_opponent"){
1499 const std::string& backstab_formula =
"enemy_of(self, flanker) and not flanker.petrified where flanker = unit_at(direction_from(loc, other.facing))";
1501 if(!
filter.has_child(
"filter_opponent")){
1502 filter_child[
"formula"] = backstab_formula;
1505 filter_opponent[
"formula"] = backstab_formula;
1506 filter_child.
add_child(
"and", filter_opponent);
1512 if ( !filter_child )
1528 filter_lock = weapon->update_variables_recursion(
filter);
1530 show_recursion_warning(weapon,
filter);
1535 if (
auto filter_weapon = filter_child->
optional_child(
"filter_weapon") ) {
1536 if ( !weapon || !weapon->matches_filter(*filter_weapon, check_if_recursion) )
1543 return ufilt.matches(*u,
loc);
1545 return ufilt.matches(*u,
loc, *u2);
1563 return special_active(*i.ability_cfg, AFFECT_SELF, ability, true);
1569 return special_active_impl(other_attack_, shared_from_this(), *i.ability_cfg, AFFECT_OTHER, ability, true);
1584 if(!abil_list.
empty() && !overwriters.
empty()){
1600 const std::string& apply_to = special[
"overwrite_specials"];
1601 return (apply_to ==
"one_side" || apply_to ==
"both_sides");
1612 if(overwriters.
empty()){
1617 if(overwriters.
size() >= 2){
1620 auto oi = (*
i.ability_cfg).optional_child(
"overwrite");
1622 if(oi && !oi[
"priority"].empty()){
1623 l = oi[
"priority"].to_double(0);
1625 auto oj = (*j.
ability_cfg).optional_child(
"overwrite");
1627 if(oj && !oj[
"priority"].empty()){
1628 r = oj[
"priority"].to_double(0);
1642 if(overwriters.
empty()){
1646 for(
const auto& j : overwriters) {
1648 bool affect_side = ((*j.ability_cfg)[
"overwrite_specials"] ==
"one_side");
1650 auto overwrite_specials = (*j.ability_cfg).optional_child(
"overwrite");
1651 double priority = overwrite_specials ? overwrite_specials[
"priority"].to_double(0) : 0.00;
1656 bool prior = (priority > 0) ? (has_overwrite_specials && has_overwrite_specials[
"priority"].to_double(0) >= priority) :
true;
1660 bool one_side_overwritable =
true;
1664 if(affect_side && is_overwritable){
1665 if(special_affects_self(*j.ability_cfg,
is_attacker_)){
1668 else if(special_affects_opponent(*j.ability_cfg, !
is_attacker_)){
1674 bool special_matches =
true;
1675 if(overwrite_specials){
1676 auto overwrite_filter = (*overwrite_specials).optional_child(
"filter_specials");
1677 if(!overwrite_filter){
1678 overwrite_filter = (*overwrite_specials).optional_child(
"experimental_filter_specials");
1679 if(overwrite_filter){
1683 if(overwrite_filter && is_overwritable && one_side_overwritable){
1691 if(is_overwritable && one_side_overwritable && special_matches){
1702 show_recursion_warning(*
this,
cfg);
1705 return (ability_active_impl(ability,
cfg,
loc) && ability_affects_self(ability,
cfg,
loc));
1712 show_recursion_warning(from,
cfg);
1720 return (get_self_ability_bool(special, tag_name,
loc) && ability_affects_weapon(special, weapon,
false) && ability_affects_weapon(special, opp_weapon,
true));
1725 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));
1730 if(tag_name ==
"leadership" && leader_bool){
1731 if(u->get_self_ability_bool_weapon(special, tag_name,
loc, self_attack, other_attack)) {
1735 if(abilities_list::all_weapon_tags().count(tag_name) != 0){
1736 if(u->get_self_ability_bool(special, tag_name,
loc) &&
special_active_impl(self_attack, other_attack, special, whom, tag_name,
true)) {
1743 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)
1745 if(tag_name ==
"leadership" && leader_bool) {
1746 if(u->get_adj_ability_bool_weapon(special, tag_name, dist, dir,
loc, from, from_loc, self_attack, other_attack)) {
1750 if(abilities_list::all_weapon_tags().count(tag_name) != 0) {
1751 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)) {
1764 const std::string& special)
1766 for(
const config &
i : self->abilities().child_range(special)) {
1771 const unit_map& units = get_unit_map();
1772 for(
const unit& u : units) {
1773 if(!u.max_ability_radius() || u.incapacitated() || u.underlying_id() == self->underlying_id() || !u.max_ability_radius_type(special)) {
1778 if(distance > u.max_ability_radius_type(special)) {
1781 int dir = find_direction(self_loc, from_loc, distance);
1782 for(
const config &
i : u.abilities().child_range(special)) {
1783 if(
check_adj_abilities_impl(self_attack, other_attack,
i,
self, u, distance, dir, self_loc, from_loc, whom, special)) {
1802 if(
range().empty()){
1822 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)
1824 if(!filter_special.empty() && filter_special.count(special_id) == 0 && filter_special.count(tag_name) == 0)
1827 if(!filter_special_id.empty() && filter_special_id.count(special_id) == 0)
1830 if(!filter_special_type.empty() && filter_special_type.count(tag_name) == 0)
1850 const unit_map& units = get_unit_map();
1851 bool check_adjacent = sub_filter ?
filter[
"affect_adjacent"].to_bool(
true) :
true;
1852 for(
const auto [key,
cfg] : self->abilities().all_children_view()) {
1853 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);
1858 if(check_adjacent) {
1859 for(
const unit& u : units) {
1860 if(!u.max_ability_radius() || u.incapacitated() || u.underlying_id() == self->underlying_id()) {
1865 if(distance > u.max_ability_radius()) {
1868 int dir = find_direction(self_loc, from_loc, distance);
1870 for(
const auto [key,
cfg] : u.abilities().all_children_view()) {
1871 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);
1872 if(special_check &&
check_adj_abilities_impl(self_attack, other_attack,
cfg,
self, u, distance, dir, self_loc, from_loc, whom, key, leader_bool)) {
1883 if(
range().empty()){
1890 for(
const auto [key,
cfg] :
specials().all_children_view()) {
1891 if(special_checking(
cfg[
"id"].str(), key, filter_special, filter_special_id, filter_special_type)) {
1901 for(
const auto [key,
cfg] :
other_attack_->specials().all_children_view()) {
1902 if(special_checking(
cfg[
"id"].str(), key, filter_special, filter_special_id, filter_special_type)) {
1926 bool exclude_ability_attributes(
const std::string& tag_name,
const config &
filter)
1929 bool abilities_check = abilities_list::ability_value_tags().count(tag_name) != 0 || abilities_list::ability_no_value_tags().count(tag_name) != 0;
1930 if(
filter.has_attribute(
"active_on") && tag_name !=
"resistance" && abilities_check)
1932 if(
filter.has_attribute(
"apply_to") && tag_name !=
"resistance" && abilities_check)
1935 if(
filter.has_attribute(
"overwrite_specials") && abilities_list::weapon_number_tags().count(tag_name) == 0)
1938 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;
1939 if(
filter.has_attribute(
"cumulative") && no_value_weapon_abilities_check)
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))
2327 bool whom_is_self = special_affects_self(special,
is_attacker_);
2331 const unit_map& units = get_unit_map();
2336 if(
self ==
nullptr) {
2342 if(other ==
nullptr) {
2349 bool applied_both = special[
"apply_to"] ==
"both";
2350 std::string self_check_if_recursion = (applied_both || whom_is_self) ? tag_name :
"";
2351 if (!special_unit_matches(
self, other,
self_loc_, shared_from_this(), special,
is_for_listing_,
"filter_student", self_check_if_recursion))
2353 bool applied_to_attacker = applied_both || (whom_is_self &&
is_attacker_);
2354 std::string att_check_if_recursion = applied_to_attacker ? tag_name :
"";
2357 bool applied_to_defender = applied_both || (whom_is_self && !
is_attacker_);
2358 std::string def_check_if_recursion= applied_to_defender ? tag_name :
"";
2387 return (cond_eq.
empty() || def == cond_eq.
to_int()) &&
2400 if(tag_name ==
"plague") {
2402 const auto iter =
unit_types.
types().find(ability_or_special[
"type"]);
2428 return formula.
evaluate(callable).as_decimal() / 1000.0 ;
2438 int value_set = def;
2439 std::map<std::string,individual_effect> values_add;
2440 std::map<std::string,individual_effect> values_sub;
2441 std::map<std::string,individual_effect> values_mul;
2442 std::map<std::string,individual_effect> values_div;
2447 utils::optional<int> max_value = utils::nullopt;
2448 utils::optional<int> min_value = utils::nullopt;
2451 const config&
cfg = *ability.ability_cfg;
2452 const std::string& effect_id =
cfg[
cfg[
"id"].
empty() ?
"name" :
"id"];
2459 int value_cum = wham !=
EFFECT_CUMULABLE &&
cfg[
"cumulative"].to_bool() ? std::max(def, value) : value;
2461 set_effect_cum.
set(
SET, set_effect_cum.
value + value_cum, ability.ability_cfg, ability.teacher_loc);
2463 set_effect_cum.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2467 set_effect_min.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2468 set_effect_max.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2471 if(value_cum > set_effect_max.
value) {
2472 set_effect_max.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2474 if(value_cum < set_effect_min.
value) {
2475 set_effect_min.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2484 max_value = max_value ? std::min(*max_value, value) : value;
2488 min_value = min_value ? std::max(*min_value, value) : value;
2495 if(add_effect == values_add.end() || add > add_effect->second.value) {
2496 values_add[effect_id].set(
ADD, add, ability.ability_cfg, ability.teacher_loc);
2502 if(sub_effect == values_sub.end() || sub < sub_effect->second.value) {
2503 values_sub[effect_id].set(
ADD, sub, ability.ability_cfg, ability.teacher_loc);
2509 if(mul_effect == values_mul.end() || multiply > mul_effect->second.value) {
2510 values_mul[effect_id].set(
MUL, multiply, ability.ability_cfg, ability.teacher_loc);
2517 ERR_NG <<
"division by zero with divide= in ability/weapon special " << effect_id;
2521 if(div_effect == values_div.end() || divide > div_effect->second.value) {
2522 values_div[effect_id].set(
DIV, divide, ability.ability_cfg, ability.teacher_loc);
2529 value_set = std::max(set_effect_max.
value, 0) + std::min(set_effect_min.
value, 0);
2530 if(set_effect_max.
value > def) {
2533 if(set_effect_min.
value < def) {
2546 double multiplier = 1.0;
2547 double divisor = 1.0;
2549 for(
const auto& val : values_mul) {
2550 multiplier *= val.second.value/100.0;
2554 for(
const auto& val : values_div) {
2555 divisor *= val.second.value/100.0;
2560 for(
const auto& val : values_add) {
2561 addition += val.second.value;
2567 int substraction = 0;
2568 for(
const auto& val : values_sub) {
2569 substraction += val.second.value;
2574 value_set += set_effect_cum.
value;
2580 if(max_value && min_value && *min_value < *max_value) {
2582 }
else if(max_value && !min_value) {
2584 }
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 special_tooltip_active(const config &special, const std::string &tag_name) const
Returns whether or not the given special is active for the specified unit, based on the current conte...
std::vector< std::pair< t_string, t_string > > abilities_special_tooltips(boost::dynamic_bitset<> *active_list) 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...
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