39 #include "formula/callable_objects.hpp"
47 #define ERR_NG LOG_STREAM(err, log_engine)
50 #define ERR_WML LOG_STREAM(err, log_wml)
53 class temporary_facing
63 u_->set_facing(new_dir);
69 u_->set_facing(save_dir_);
146 const team& get_team(std::size_t side)
166 bool affects_side(
const config& cfg, std::size_t side, std::size_t other_side)
168 const team& side_team = get_team(side);
170 if(side == other_side)
171 return cfg[
"affect_allies"].to_bool(
true);
173 return cfg[
"affect_enemies"].to_bool();
175 return cfg[
"affect_allies"].to_bool();
190 const unit_map& units = get_unit_map();
193 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
195 if (it == units.
end() || it->incapacitated())
204 for (
const config &j : it->abilities_.child_range(tag_name)) {
205 if (affects_side(j,
side(), it->side()) &&
206 it->ability_active(tag_name, j, adjacent[
i]) &&
230 const unit_map& units = get_unit_map();
233 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
235 if (it == units.
end() || it->incapacitated())
244 for(
const config& j : it->abilities_.child_range(tag_name)) {
245 if(affects_side(j,
side(), it->side())
246 && it->ability_active(tag_name, j, adjacent[
i])
269 std::vector<std::string> res;
272 std::string
id = ab.cfg[
"id"];
274 res.push_back(std::move(
id));
295 ab.
cfg[
"name"].t_str(),
297 ab.
cfg[
"description"].t_str() );
306 "female_name_inactive",
"name_inactive");
326 std::vector<std::tuple<std::string, t_string,t_string,t_string>> res;
330 add_ability_tooltip(ab,
gender_, res,
true);
338 std::vector<std::tuple<std::string, t_string,t_string,t_string>> res;
344 if (add_ability_tooltip(ab,
gender_, res, active))
346 active_list.push_back(active);
354 bool illuminates = ability ==
"illuminates";
357 if ( !
unit_filter(
vconfig(*afilter)).set_use_flat_tod(illuminates).matches(*
this, loc) )
362 const unit_map& units = get_unit_map();
366 std::size_t count = 0;
368 ufilt.set_use_flat_tod(illuminates);
377 if (!ufilt(*
unit, *
this))
379 if((*this).id() == (*unit).id())
381 if (
i.has_attribute(
"is_enemy")) {
389 if (
i[
"count"].empty() && count != dirs.size()) {
399 std::size_t count = 0;
401 adj_filter.flatten(illuminates);
409 if(!adj_filter.match(adjacent[
index])) {
414 if (
i[
"count"].empty() && count != dirs.size()) {
426 bool illuminates = ability ==
"illuminates";
428 assert(dir >=0 && dir <= 5);
433 if (
i.has_attribute(
"adjacent")) {
435 if (std::find(dirs.begin(), dirs.end(), direction) == dirs.end()) {
439 if((*this).id() == from.
id()){
442 auto filter =
i.optional_child(
"filter");
444 unit_filter(
vconfig(*filter)).set_use_flat_tod(illuminates).matches(*
this, loc, from) ) {
454 bool affect_self = cfg[
"affect_self"].to_bool(
true);
455 if (!filter || !affect_self)
return affect_self;
461 const std::string filter_tag_name = is_opp ?
"filter_second_weapon" :
"filter_weapon";
469 return weapon->matches_filter(filter);
481 auto ret = std::find(image_list.begin(), image_list.end(), cfg[attribute_name].str());
482 if(ret == image_list.end()){
483 image_list.push_back(cfg[attribute_name].str());
489 std::vector<std::string> image_list;
497 if(!(
sp.cfg)[image_type +
"_image_self"].str().empty() &&
is_active){
502 const unit_map& units = get_unit_map();
507 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
509 if (it == units.
end() || it->incapacitated())
514 if(!(j.cfg)[image_type +
"_image"].str().empty() && affects_side(j.cfg,
side(), it->side()) && it->ability_active(j.key, j.cfg, adjacent[
i]) &&
ability_affects_adjacent(j.key, j.cfg,
i,
loc_, *it))
521 if(image_list.size() >= 2){
522 std::sort(image_list.begin(), image_list.end());
530 callable.
add(
"attacker",
wfl::variant(std::make_shared<wfl::unit_callable>(*att)));
533 callable.
add(
"defender",
wfl::variant(std::make_shared<wfl::unit_callable>(*def)));
540 template<
typename T,
typename TFuncFormula>
541 class get_ability_value_visitor
542 #ifdef USING_BOOST_VARIANT
543 :
public boost::static_visitor<T>
548 get_ability_value_visitor(T def,
const TFuncFormula& formula_handler) : def_(def), formula_handler_(formula_handler) {}
550 T operator()(
const utils::monostate&)
const {
return def_; }
551 T operator()(
bool)
const {
return def_; }
552 T operator()(
int i)
const {
return static_cast<T
>(
i); }
553 T operator()(
unsigned long long u)
const {
return static_cast<T
>(u); }
554 T operator()(
double d)
const {
return static_cast<T
>(
d); }
555 T operator()(
const t_string&)
const {
return def_; }
556 T operator()(
const std::string&
s)
const
558 if(
s.size() >= 2 &&
s[0] ==
'(') {
559 return formula_handler_(
s);
561 return lexical_cast_default<T>(
s, def_);
566 const TFuncFormula& formula_handler_;
569 template<
typename T,
typename TFuncFormula>
572 return v.
apply_visitor(get_ability_value_visitor(def, [&](
const std::string&
s) {
575 const unit_map& units = get_unit_map();
579 if(u_itor == units.
end()) {
584 att->add_formula_context(callable);
587 callable.add(
"student",
wfl::variant(std::make_shared<wfl::unit_callable>(*uptr)));
590 callable.add(
"other",
wfl::variant(std::make_shared<wfl::unit_callable>(*uptr)));
594 lg::log_to_chat() <<
"Formula error in ability or weapon special: " <<
e.type <<
" at " <<
e.filename <<
':' <<
e.line <<
")\n";
595 ERR_WML <<
"Formula error in ability or weapon special: " <<
e.type <<
" at " <<
e.filename <<
':' <<
e.line <<
")";
602 template<
typename TComp>
605 if (
cfgs_.empty() ) {
611 bool only_cumulative =
true;
618 return formula.
evaluate(callable).as_int();
621 if ((*
p.ability_cfg)[
"cumulative"].to_bool()) {
623 if (value < 0) value = -value;
624 if (only_cumulative && !comp(value, abs_max)) {
626 best_loc =
p.teacher_loc;
628 }
else if (only_cumulative || comp(flat, value)) {
629 only_cumulative =
false;
631 best_loc =
p.teacher_loc;
634 return std::pair(flat + stack, best_loc);
637 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;
638 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;
672 std::string tag_name;
688 bool get_special_children(std::vector<special_match>& tag_result,
689 std::vector<special_match>& id_result,
690 const config& parent,
const std::string&
id,
691 bool just_peeking=
false) {
694 if (just_peeking && (
sp.key ==
id ||
sp.cfg[
"id"] ==
id)) {
699 special_match special = {
sp.key, &
sp.cfg };
700 tag_result.push_back(special);
702 if(
sp.cfg[
"id"] ==
id) {
703 special_match special = {
sp.key, &
sp.cfg };
704 id_result.push_back(special);
710 bool get_special_children_id(std::vector<special_match>& id_result,
711 const config& parent,
const std::string&
id,
712 bool just_peeking=
false) {
715 if (just_peeking && (
sp.cfg[
"id"] ==
id)) {
719 if(
sp.cfg[
"id"] ==
id) {
720 special_match special = {
sp.key, &
sp.cfg };
721 id_result.push_back(special);
727 bool get_special_children_tags(std::vector<special_match>& tag_result,
728 const config& parent,
const std::string&
id,
729 bool just_peeking=
false) {
732 if (just_peeking && (
sp.key ==
id)) {
737 special_match special = {
sp.key, &
sp.cfg };
738 tag_result.push_back(special);
755 std::vector<special_match> special_tag_matches;
756 std::vector<special_match> special_id_matches;
757 if(special_id && special_tags){
758 if ( get_special_children(special_tag_matches, special_id_matches,
specials_, special, simple_check) ) {
761 }
else if(special_id && !special_tags){
762 if ( get_special_children_id(special_id_matches,
specials_, special, simple_check) ) {
765 }
else if(!special_id && special_tags){
766 if ( get_special_children_tags(special_tag_matches,
specials_, special, simple_check) ) {
774 for(
const special_match& entry : special_tag_matches) {
781 for(
const special_match& entry : special_id_matches) {
794 std::vector<special_match> special_tag_matches;
795 std::vector<special_match> special_id_matches;
796 if(special_id && special_tags){
797 get_special_children(special_tag_matches, special_id_matches,
other_attack_->specials_, special);
798 }
else if(special_id && !special_tags){
799 get_special_children_id(special_id_matches,
other_attack_->specials_, special);
800 }
else if(!special_id && special_tags){
801 get_special_children_tags(special_tag_matches,
other_attack_->specials_, special);
804 for(
const special_match& entry : special_tag_matches) {
811 for(
const special_match& entry : special_id_matches) {
858 boost::dynamic_bitset<>* active_list)
const
861 std::vector<std::pair<t_string, t_string>> res;
863 active_list->clear();
870 res.emplace_back(
name,
sp.cfg[
"description"].t_str() );
872 active_list->push_back(
true);
875 const t_string&
name =
sp.cfg.get_or(
"name_inactive",
"name").t_str();
877 res.emplace_back(
name,
sp.cfg.get_or(
"description_inactive",
"description").t_str() );
878 active_list->push_back(
false);
893 static void add_name(std::string& temp_string,
bool active,
const std::string name, std::set<std::string>& checking_name)
896 if (!name.empty() && checking_name.count(name) == 0) {
897 checking_name.insert(name);
898 if (!temp_string.empty()) temp_string +=
", ";
919 const std::string&
name =
921 ?
sp.cfg[
"name"].str()
922 :
sp.cfg.get_or(
"name_inactive",
"name").str();
924 if (!res.empty()) res +=
", ";
927 if (!active) res +=
"</span>";
930 std::string temp_string;
931 std::set<std::string> checking_name;
934 if(!temp_string.empty() && !res.empty()) {
935 temp_string =
", \n" + temp_string;
937 }
else if (!temp_string.empty()){
943 static void add_name_list(std::string& temp_string, std::string& weapon_abilities, std::set<std::string>& checking_name,
const std::string from_str)
945 if(!temp_string.empty()){
946 temp_string = from_str.c_str() + temp_string;
947 weapon_abilities += (!weapon_abilities.empty() && !temp_string.empty()) ?
"\n" :
"";
948 weapon_abilities += temp_string;
950 checking_name.clear();
957 std::string temp_string, weapon_abilities;
958 std::set<std::string> checking_name;
960 if((checking_tags.count(
sp.key) != 0)){
962 add_name(temp_string, active,
sp.cfg[
"name"].str(), checking_name);
965 add_name_list(temp_string, weapon_abilities, checking_name,
"");
968 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Owned: "));
972 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Taught: "));
976 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Taught: (by an enemy): "));
981 if((checking_tags.count(
sp.key) != 0)){
983 add_name(temp_string, active,
sp.cfg[
"name"].str(), checking_name);
989 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Used by opponent: "));
991 return weapon_abilities;
995 std::string& temp_string,
1001 std::set<std::string>& checking_name,
1002 const std::set<std::string>& checking_tags,
1007 bool tag_checked = (!checking_tags.empty()) ? (checking_tags.count(
sp.key) != 0) :
true;
1009 add_name(temp_string, active,
sp.cfg[
"name"].str(), checking_name);
1015 std::string& temp_string,
1021 std::set<std::string>& checking_name,
1022 const std::set<std::string>& checking_tags,
1023 const std::string& affect_adjacents,
1026 const unit_map& units = get_unit_map();
1029 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
1031 if (it == units.
end() || it->incapacitated())
1033 if(&*it ==
self.
get())
1036 bool tag_checked = (!checking_tags.empty()) ? (checking_tags.count(
sp.key) != 0) :
true;
1037 bool default_bool = (affect_adjacents ==
"affect_allies") ?
true :
false;
1038 bool affect_allies = (!affect_adjacents.empty()) ?
sp.cfg[affect_adjacents].to_bool(default_bool) :
true;
1039 const bool active = tag_checked &&
check_adj_abilities_impl(self_attack, other_attack,
sp.cfg,
self, *it,
i, self_loc, whom,
sp.key, leader_bool) && affect_allies;
1040 add_name(temp_string, active,
sp.cfg[
"name"].str(), checking_name);
1066 : parent(weapon.shared_from_this())
1068 weapon.
self_ =
self;
1086 : parent(weapon.shared_from_this())
1088 weapon.
self_ =
self;
1106 : parent(weapon.shared_from_this())
1119 : parent(weapon.shared_from_this())
1127 if(was_moved)
return;
1132 parent->is_attacker_ =
false;
1133 parent->other_attack_ =
nullptr;
1134 parent->is_for_listing_ =
false;
1138 : parent(other.parent)
1140 other.was_moved =
true;
1151 unsigned & max_attacks)
const
1156 if ( attacks_value < 0 ) {
1158 ERR_NG <<
"negative number of strikes after applying weapon specials";
1163 if ( !swarm_specials.
empty() ) {
1164 min_attacks = std::max<int>(0, swarm_specials.
highest(
"swarm_attacks_min").first);
1165 max_attacks = std::max<int>(0, swarm_specials.
highest(
"swarm_attacks_max", attacks_value).first);
1167 min_attacks = max_attacks = attacks_value;
1174 std::vector<std::string> type_list;
1175 for(
auto&
i : abil_list) {
1176 if(!(*
i.ability_cfg)[
type].str().empty()){
1177 type_list.push_back((*
i.ability_cfg)[
type].str());
1180 if(type_list.size() >= 2){
1181 std::sort(type_list.begin(), type_list.end());
1182 if(type_list.size() >= 3){
1183 std::unordered_map<std::string, unsigned int> type_count;
1184 for(
const std::string& character : type_list ){
1185 type_count[character]++;
1187 std::sort( std::begin( type_list ) , std::end( type_list ) , [&](
const std::string& rhs ,
const std::string& lhs ){
1188 return type_count[lhs] < type_count[rhs];
1201 if(abil_list.
empty()){
1202 return {
type(),
""};
1205 std::vector<std::string> type_list =
damage_type_list(abil_list,
"replacement_type");
1206 std::vector<std::string> added_type_list =
damage_type_list(abil_list,
"alternative_type");
1207 std::string type_damage, sec_type_damage;
1208 type_damage = !type_list.empty() ? type_list.front() :
type();
1209 sec_type_damage = !added_type_list.empty() ? added_type_list.front() :
"";
1210 if(!sec_type_damage.empty()){
1211 sec_type_damage = type_damage != sec_type_damage ? sec_type_damage:
"";
1213 return {type_damage, sec_type_damage};
1223 return damage_value;
1235 bool special_affects_opponent(
const config& special,
bool is_attacker)
1238 const std::string& apply_to = special[
"apply_to"];
1239 if ( apply_to.empty() )
1241 if ( apply_to ==
"both" )
1243 if ( apply_to ==
"opponent" )
1245 if ( is_attacker && apply_to ==
"defender" )
1247 if ( !is_attacker && apply_to ==
"attacker" )
1257 bool special_affects_self(
const config& special,
bool is_attacker)
1260 const std::string& apply_to = special[
"apply_to"];
1261 if ( apply_to.empty() )
1263 if ( apply_to ==
"both" )
1265 if ( apply_to ==
"self" )
1267 if ( is_attacker && apply_to ==
"attacker" )
1269 if ( !is_attacker && apply_to ==
"defender")
1291 const bool for_listing,
1292 const std::string & child_tag,
const std::string& tag_name)
1294 if (for_listing && !loc.
valid())
1303 if ( !filter_child )
1318 if (
auto filter_weapon = filter_child->optional_child(
"filter_weapon") ) {
1319 if ( !weapon || !weapon->matches_filter(*filter_weapon, tag_name) )
1326 return ufilt.matches(*u, loc);
1328 return ufilt.matches(*u, loc, *u2);
1346 return special_active(*i.ability_cfg, AFFECT_SELF, ability,
"filter_student");
1352 return special_active_impl(other_attack_, shared_from_this(), *i.ability_cfg, AFFECT_OTHER, ability,
"filter_student");
1367 if(!abil_list.
empty() && !overwriters.
empty()){
1383 const std::string& apply_to = special[
"overwrite_specials"];
1384 return (apply_to ==
"one_side" || apply_to ==
"both_sides");
1395 if(overwriters.
empty()){
1400 if(overwriters.
size() >= 2){
1403 auto oi = (*
i.ability_cfg).optional_child(
"overwrite");
1405 if(oi && !oi[
"priority"].empty()){
1406 l = oi[
"priority"].to_double(0);
1408 auto oj = (*j.
ability_cfg).optional_child(
"overwrite");
1410 if(oj && !oj[
"priority"].empty()){
1411 r = oj[
"priority"].to_double(0);
1425 if(overwriters.
empty()){
1429 for(
const auto& j : overwriters) {
1431 bool affect_side = ((*j.ability_cfg)[
"overwrite_specials"] ==
"one_side");
1433 auto overwrite_specials = (*j.ability_cfg).optional_child(
"overwrite");
1434 double priority = overwrite_specials ? overwrite_specials[
"priority"].to_double(0) : 0.00;
1439 bool prior = (priority > 0) ? (has_overwrite_specials && has_overwrite_specials[
"priority"].to_double(0) >= priority) :
true;
1443 bool one_side_overwritable =
true;
1447 if(affect_side && is_overwritable){
1448 if(special_affects_self(*j.ability_cfg,
is_attacker_)){
1449 one_side_overwritable = special_affects_self(cfg,
is_attacker_);
1451 else if(special_affects_opponent(*j.ability_cfg, !
is_attacker_)){
1452 one_side_overwritable = special_affects_opponent(cfg, !
is_attacker_);
1457 bool special_matches =
true;
1458 if(overwrite_specials){
1459 auto overwrite_filter = (*overwrite_specials).optional_child(
"experimental_filter_specials");
1460 if(overwrite_filter && is_overwritable && one_side_overwritable){
1462 special_matches = (*self_).ability_matches_filter(cfg, tag_name, *overwrite_filter);
1470 if(is_overwritable && one_side_overwritable && special_matches){
1489 std::vector<special_match>& id_result,
1490 const config& parent,
const std::string&
id,
1491 bool special_id=
true,
bool special_tags=
true) {
1492 if(special_id && special_tags){
1493 get_special_children(tag_result, id_result, parent,
id);
1494 }
else if(special_id && !special_tags){
1495 get_special_children_id(id_result, parent,
id);
1496 }
else if(!special_id && special_tags){
1497 get_special_children_tags(tag_result, parent,
id);
1503 return (ability_active(tag_name, special, loc) && ability_affects_self(tag_name, special, loc));
1509 return (affects_side(special, side(), from.
side()) && from.
ability_active(tag_name, special, adjacent[dir]) && ability_affects_adjacent(tag_name, special, dir, loc, from));
1514 return (get_self_ability_bool(special, tag_name, loc) && ability_affects_weapon(special, weapon,
false) && ability_affects_weapon(special, opp_weapon,
true));
1519 return (get_adj_ability_bool(special, tag_name, dir, loc, from) && ability_affects_weapon(special, weapon,
false) && ability_affects_weapon(special, opp_weapon,
true));
1529 if(tag_name ==
"leadership" && leader_bool){
1530 if((*u).get_self_ability_bool_weapon(special, tag_name, loc, self_attack, other_attack)) {
1534 if((*u).checking_tags().count(tag_name) != 0){
1535 if((*u).get_self_ability_bool(special, tag_name, loc) &&
special_active_impl(self_attack, other_attack, special, whom, tag_name,
"filter_student")) {
1549 if(tag_name ==
"leadership" && leader_bool){
1550 if((*u).get_adj_ability_bool_weapon(special, tag_name, dir, loc, from, self_attack, other_attack)) {
1554 if((*u).checking_tags().count(tag_name) != 0){
1555 if((*u).get_adj_ability_bool(special, tag_name, dir, loc, from) &&
special_active_impl(self_attack, other_attack, special, whom, tag_name,
"filter_student")) {
1569 const unit_map& units = get_unit_map();
1571 std::vector<special_match> special_tag_matches_self;
1572 std::vector<special_match> special_id_matches_self;
1573 get_ability_children(special_tag_matches_self, special_id_matches_self, (*self_).abilities(), special, special_id , special_tags);
1575 for(
const special_match& entry : special_tag_matches_self) {
1582 for(
const special_match& entry : special_id_matches_self) {
1590 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
1592 if (it == units.
end() || it->incapacitated())
1594 if ( &*it ==
self_.get() )
1597 std::vector<special_match> special_tag_matches_adj;
1598 std::vector<special_match> special_id_matches_adj;
1599 get_ability_children(special_tag_matches_adj, special_id_matches_adj, it->abilities(), special, special_id , special_tags);
1601 for(
const special_match& entry : special_tag_matches_adj) {
1608 for(
const special_match& entry : special_id_matches_adj) {
1618 std::vector<special_match> special_tag_matches_other;
1619 std::vector<special_match> special_id_matches_other;
1620 get_ability_children(special_tag_matches_other, special_id_matches_other, (*other_).abilities(), special, special_id , special_tags);
1622 for(
const special_match& entry : special_tag_matches_other) {
1630 for(
const special_match& entry : special_id_matches_other) {
1638 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
1640 if (it == units.
end() || it->incapacitated())
1642 if ( &*it ==
other_.get() )
1645 std::vector<special_match> special_tag_matches_oadj;
1646 std::vector<special_match> special_id_matches_oadj;
1647 get_ability_children(special_tag_matches_oadj, special_id_matches_oadj, it->abilities(), special, special_id , special_tags);
1649 for(
const special_match& entry : special_tag_matches_oadj) {
1657 for(
const special_match& entry : special_id_matches_oadj) {
1673 if(
range().empty()){
1681 const std::string& filter_self)
const
1701 const std::string& tag_name,
1702 const std::string& filter_self)
1704 assert(self_attack || other_attack);
1705 bool is_attacker = self_attack ? self_attack->is_attacker_ : !other_attack->is_attacker_;
1706 bool is_for_listing = self_attack ? self_attack->is_for_listing_ : other_attack->is_for_listing_;
1712 if ( !special_affects_self(special, is_attacker) )
1716 if ( !special_affects_opponent(special, is_attacker) )
1721 const std::string & active_on = special[
"active_on"];
1722 if ( !active_on.empty() ) {
1723 if ( is_attacker && active_on !=
"offense" )
1725 if ( !is_attacker && active_on !=
"defense" )
1730 const unit_map& units = get_unit_map();
1732 unit_const_ptr self = self_attack ? self_attack->self_ : other_attack->other_;
1733 unit_const_ptr other = self_attack ? self_attack->other_ : other_attack->self_;
1734 map_location self_loc = self_attack ? self_attack->self_loc_ : other_attack->other_loc_;
1735 map_location other_loc = self_attack ? self_attack->other_loc_ : other_attack->self_loc_;
1737 if(
self ==
nullptr) {
1743 if(other ==
nullptr) {
1752 temporary_facing other_facing(other, other_loc.
get_relative_dir(self_loc));
1756 bool whom_is_self = ((whom ==
AFFECT_SELF) || ((whom ==
AFFECT_EITHER) && special_affects_self(special, is_attacker)));
1758 map_location their_loc = whom_is_self ? other_loc : self_loc;
1760 if (tag_name ==
"drains" && them && them->get_state(
"undrainable")) {
1763 if (tag_name ==
"plague" && them &&
1764 (them->get_state(
"unplagueable") ||
1768 if (tag_name ==
"poison" && them &&
1772 if (tag_name ==
"slow" && them &&
1776 if (tag_name ==
"petrifies" && them &&
1777 them->get_state(
"unpetrifiable")) {
1785 const map_location & att_loc = is_attacker ? self_loc : other_loc;
1786 const map_location & def_loc = is_attacker ? other_loc : self_loc;
1792 if (tag_name ==
"firststrike") {
1793 bool whom_is_defender = whom_is_self ? !is_attacker : is_attacker;
1794 if (whom_is_defender && att_weapon && att_weapon->has_special_or_ability(
"firststrike"))
1799 if (!special[
"backstab"].
blank()) {
1800 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.");
1803 if(special[
"backstab"].to_bool()){
1804 const std::string& backstab_formula =
"enemy_of(self, flanker) and not flanker.petrified where flanker = unit_at(direction_from(loc, other.facing))";
1806 if(!special.
has_child(
"filter_opponent")){
1807 filter_child[
"formula"] = backstab_formula;
1810 filter[
"formula"] = backstab_formula;
1814 const config& special_backstab = special[
"backstab"].to_bool() ? cfg : special;
1820 std::string self_tag_name = whom_is_self ? tag_name :
"";
1821 if (!special_unit_matches(
self, other, self_loc, self_attack, special, is_for_listing, filter_self, self_tag_name))
1823 std::string opp_tag_name = !whom_is_self ? tag_name :
"";
1824 if (!special_unit_matches(other,
self, other_loc, other_attack, special_backstab, is_for_listing,
"filter_opponent", opp_tag_name))
1826 std::string att_tag_name = is_attacker ? tag_name :
"";
1827 if (!special_unit_matches(att, def, att_loc, att_weapon, special, is_for_listing,
"filter_attacker", att_tag_name))
1829 std::string def_tag_name = !is_attacker ? tag_name :
"";
1830 if (!special_unit_matches(def, att, def_loc, def_weapon, special, is_for_listing,
"filter_defender", def_tag_name))
1838 std::size_t count = 0;
1848 if (
i.has_attribute(
"is_enemy")) {
1856 if (
i[
"count"].empty() && count != dirs.size()) {
1867 std::size_t count = 0;
1874 if(!adj_filter.match(adjacent[
index])) {
1879 if (
i[
"count"].empty() && count != dirs.size()) {
1905 if (
auto apply_filter = cfg.
optional_child(
"filter_base_value")) {
1912 return (cond_eq.
empty() || def == cond_eq.
to_int()) &&
1927 int value_set = is_cumulable ? std::max(list.
highest(
"value").first, 0) + std::min(list.
lowest(
"value").first, 0) : def;
1928 std::map<std::string,individual_effect> values_add;
1929 std::map<std::string,individual_effect> values_mul;
1930 std::map<std::string,individual_effect> values_div;
1936 const config& cfg = *ability.ability_cfg;
1937 const std::string& effect_id = cfg[cfg[
"id"].
empty() ?
"name" :
"id"];
1945 callable.add(
"base_value", wfl::variant(def));
1946 return formula.evaluate(callable).as_int();
1949 int value_cum = cfg[
"cumulative"].to_bool() ? std::max(def, value) : value;
1952 set_effect_min.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
1953 set_effect_max.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
1956 if(value_cum > set_effect_max.
value) {
1957 set_effect_max.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
1959 if(value_cum < set_effect_min.
value) {
1960 set_effect_min.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
1968 callable.add(
"base_value", wfl::variant(def));
1969 return formula.evaluate(callable).as_int();
1972 if(add_effect == values_add.end() || add > add_effect->second.value) {
1973 values_add[effect_id].set(
ADD, add, ability.ability_cfg, ability.teacher_loc);
1978 callable.add(
"base_value", wfl::variant(def));
1979 return formula.evaluate(callable).as_int();
1982 if(sub_effect == values_add.end() || sub < sub_effect->second.value) {
1983 values_add[effect_id].set(
ADD, sub, ability.ability_cfg, ability.teacher_loc);
1988 callable.add(
"base_value", wfl::variant(def));
1989 return formula.evaluate(callable).as_decimal() / 1000.0 ;
1992 if(mul_effect == values_mul.end() || multiply > mul_effect->second.value) {
1993 values_mul[effect_id].set(
MUL, multiply, ability.ability_cfg, ability.teacher_loc);
1998 callable.add(
"base_value", wfl::variant(def));
1999 return formula.evaluate(callable).as_decimal() / 1000.0 ;
2003 ERR_NG <<
"division by zero with divide= in ability/weapon special " << effect_id;
2007 if(div_effect == values_div.end() || divide > div_effect->second.value) {
2008 values_div[effect_id].set(
DIV, divide, ability.ability_cfg, ability.teacher_loc);
2015 value_set = std::max(set_effect_max.
value, 0) + std::min(set_effect_min.
value, 0);
2016 if(set_effect_max.
value > def) {
2019 if(set_effect_min.
value < def) {
2032 double multiplier = 1.0;
2033 double divisor = 1.0;
2035 for(
const auto& val : values_mul) {
2036 multiplier *= val.second.value/100.0;
2040 for(
const auto& val : values_div) {
2041 divisor *= val.second.value/100.0;
2046 for(
const auto& val : values_add) {
2047 addition += val.second.value;
2051 composite_value_ =
static_cast<int>((value_set + addition) * multiplier / divisor);
static std::vector< std::string > damage_type_list(const unit_ability_list &abil_list, const std::string &type)
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 overwrite_special_affects(const config &special)
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 lg::log_domain log_wml("wml")
static void get_ability_children(std::vector< special_match > &tag_result, std::vector< special_match > &id_result, const config &parent, const std::string &id, bool special_id=true, bool special_tags=true)
Gets the children of parent (which should be the abilities for an attack_type) and places the ones wh...
specials_context_t(const attack_type &weapon, bool attacking)
Initialize weapon specials context for listing.
std::string weapon_specials() const
Returns a comma-separated string of active names for the specials of *this.
bool check_self_abilities(const config &cfg, const std::string &special) const
check_self_abilities : return an boolean value for checking of activities of abilities used like weap...
bool has_special(const std::string &special, bool simple_check=false, bool special_id=true, bool special_tags=true) const
Returns whether or not *this has a special with a tag or id equal to special.
static bool special_active_impl(const_attack_ptr self_attack, const_attack_ptr other_attack, const config &special, AFFECTS whom, const std::string &tag_name, const std::string &filter_self="filter_self")
Returns whether or not the given special is active for the specified unit, based on the current conte...
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
const std::string & type() const
unit_ability_list get_weapon_ability(const std::string &ability) const
Returns list for weapon like abilities for each ability type.
std::string weapon_specials_value(const std::set< std::string > checking_tags) const
unit_ability_list get_specials_and_abilities(const std::string &special) const
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.
bool check_adj_abilities(const config &cfg, const std::string &special, int dir, const unit &from) const
check_adj_abilities : return an boolean value for checking of activities of abilities used like weapo...
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...
static bool check_adj_abilities_impl(const_attack_ptr self_attack, const_attack_ptr other_attack, const config &special, unit_const_ptr u, const unit &from, int dir, const map_location &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 void weapon_specials_impl_adj(std::string &temp_string, unit_const_ptr self, const_attack_ptr self_attack, 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)
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, unit_const_ptr self, const_attack_ptr self_attack, 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.
const t_string & name() const
static bool check_self_abilities_impl(const_attack_ptr self_attack, const_attack_ptr other_attack, const config &special, 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...
int modified_damage() const
Returns the damage per attack of this weapon, considering specials.
void modified_attacks(unsigned &min_attacks, unsigned &max_attacks) const
Calculates the number of attacks this weapon has, considering specials.
bool special_active(const config &special, AFFECTS whom, const std::string &tag_name, const std::string &filter_self="filter_self") const
unit_ability_list get_specials(const std::string &special) const
Returns the currently active specials as an ability list, given the current context (see set_specials...
bool has_special_or_ability(const std::string &special, bool special_id=true, bool special_tags=true) const
used for abilities used like weapon and true specials
bool has_weapon_ability(const std::string &special, bool special_id=true, bool special_tags=true) const
used for abilities used like weapon
std::pair< std::string, std::string > damage_type() const
return a modified damage type and/or add a secondary_type for hybrid use if special is active.
Variant for storing WML attributes.
int to_int(int def=0) const
auto apply_visitor(const V &visitor) const
Visitor support: Applies a visitor to the underlying variant.
bool blank() const
Tests for an attribute that was never set.
bool empty() const
Tests for an attribute that either was never set or was set to "".
A config object defines a single node in a WML file, with access to child nodes.
const attribute_value & get_or(const config_key_type key, const config_key_type default_key) const
Chooses a value.
config & mandatory_child(config_key_type key, int n=0)
Returns the nth child with the given key, or throws an error if there is none.
bool matches(const config &filter) const
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
const_all_children_itors all_children_range() const
In-order iteration over all children.
child_itors child_range(config_key_type key)
config & child_or_add(config_key_type key)
Returns a reference to the first child with the given key.
const attribute_value * get(config_key_type key) const
Returns a pointer to the attribute with the given key or nullptr if it does not exist.
optional_config_impl< config > optional_child(config_key_type key, int n=0)
Euivalent to mandatory_child, but returns an empty optional if the nth child was not found.
config & add_child(config_key_type key)
Abstract class for exposing game data that doesn't depend on the GUI, however which for historical re...
const team & get_team(int side) const
This getter takes a 1-based side number, not a 0-based team number.
const unit_map & get_units() const
const display_context & get_disp_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
int get_composite_value() const
std::vector< individual_effect > effect_list_
void append(const unit_ability_list &other)
Appends the abilities from other to this, ignores other.loc()
std::pair< int, map_location > lowest(const std::string &key, int def=0) const
std::vector< unit_ability > cfgs_
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)
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.
std::string deprecated_message(const std::string &elem_name, DEP_LEVEL level, const version_info &version, const std::string &detail)
Interfaces for manipulating version numbers of engine, add-ons, etc.
static std::string _(const char *str)
bool get_self_ability_bool_weapon(const config &special, const std::string &tag_name, const map_location &loc, const_attack_ptr weapon=nullptr, const_attack_ptr opp_weapon=nullptr) const
Checks whether this unit currently possesses a given ability of leadership type.
bool get_adj_ability_bool_weapon(const config &special, const std::string &tag_name, int dir, const map_location &loc, const unit &from, const_attack_ptr weapon=nullptr, const_attack_ptr opp_weapon=nullptr) const
Checks whether this unit is affected by a given ability of leadership type.
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.
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 ability_affects_adjacent(const std::string &ability, const config &cfg, int dir, const map_location &loc, const unit &from) const
Check if an ability affects adjacent units.
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 ability_affects_weapon(const config &cfg, const_attack_ptr weapon, bool is_opp) const
filters the weapons that condition the use of abilities for combat ([resistance],[leadership] or abil...
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.
bool get_adj_ability_bool(const config &special, const std::string &tag_name, int dir, const map_location &loc, const unit &from) const
Checks whether this unit is affected by a given ability used like weapon.
unit_race::GENDER gender_
std::vector< std::string > get_ability_list() const
Get a list of all abilities by ID.
bool get_self_ability_bool(const config &special, const std::string &tag_name, const map_location &loc) const
Checks whether this unit currently possesses a given ability used like weapon.
const std::string & id() const
Gets this unit's id.
int side() const
The side this unit belongs to.
@ STATE_POISONED
The unit is slowed - it moves slower and does less damage.
const std::vector< std::string > halo_or_icon_abilities(const std::string &image_type) const
New lexcical_cast header.
void get_adjacent_tiles(const map_location &a, map_location *res)
Function which, given a location, will place all adjacent locations in res.
Standard logging facilities (interface).
void set(CURSOR_TYPE type)
Use the default parameter to reset cursors.
const color_t inactive_details_color
const color_t BUTTON_COLOR
std::string span_color(const color_t &color)
Returns a Pango formatting string using the provided color_t object.
static bool is_active(const widget *wgt)
std::stringstream & log_to_chat()
Use this to show WML errors in the ingame chat.
filter_context * filter_con
bool filter_base_matches(const config &cfg, int def)
std::size_t index(const std::string &str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
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.
void sort_if(Container &container, const Predicate &predicate)
Convenience wrapper for using std::sort on a container.
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.
const child_map::key_type & key
Encapsulates the map of the game.
DIRECTION
Valid directions which can be moved in our hexagonal world.
static std::vector< DIRECTION > parse_directions(const std::string &str)
Parse_directions takes a comma-separated list, and filters out any invalid directions.
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