39 #include "formula/callable_objects.hpp" 44 #include <boost/dynamic_bitset.hpp> 46 #include <string_view> 49 #define ERR_NG LOG_STREAM(err, log_engine) 52 #define ERR_WML LOG_STREAM(err, log_wml) 55 class temporary_facing
65 u_->set_facing(new_dir);
71 u_->set_facing(save_dir_);
148 const team& get_team(std::size_t side)
168 bool affects_side(
const config& cfg, std::size_t side, std::size_t other_side)
170 const team& side_team = get_team(side);
172 if(side == other_side)
173 return cfg[
"affect_allies"].to_bool(
true);
175 return cfg[
"affect_enemies"].to_bool();
177 return cfg[
"affect_allies"].to_bool();
184 for (
const config &
i : this->abilities_.child_range(tag_name)) {
185 if (ability_active(tag_name,
i, loc) &&
186 ability_affects_self(tag_name,
i, loc))
192 const unit_map& units = get_unit_map();
195 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
197 if (it == units.
end() || it->incapacitated())
206 for (
const config &j : it->abilities_.child_range(tag_name)) {
207 if (affects_side(j, side(), it->side()) &&
208 it->ability_active(tag_name, j, adjacent[i]) &&
209 ability_affects_adjacent(tag_name, j, i, loc, *it))
224 for(
const config&
i : this->abilities_.child_range(tag_name)) {
225 if(ability_active(tag_name,
i, loc)
226 && ability_affects_self(tag_name,
i, loc))
232 const unit_map& units = get_unit_map();
235 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
237 if (it == units.
end() || it->incapacitated())
246 for(
const config& j : it->abilities_.child_range(tag_name)) {
247 if(affects_side(j, side(), it->side())
248 && it->ability_active(tag_name, j, adjacent[i])
249 && ability_affects_adjacent(tag_name, j, i, loc, *it))
264 return !ability_affects_weapon(*i.
ability_cfg, weapon,
false) || !ability_affects_weapon(*i.
ability_cfg, opp_weapon,
true);
271 std::vector<std::string> res;
274 std::string
id = ab.cfg[
"id"];
276 res.push_back(std::move(
id));
291 const config & cfg,
const std::string & key,
const std::string & default_key)
294 return !value.
blank() ? value : cfg[default_key];
303 const std::string & female_key,
const std::string & default_key)
305 return default_value(cfg,
319 const t_string& name = gender_value(ab.
cfg, gender,
"name",
"female_name",
"name").t_str();
324 ab.
cfg[
"name"].t_str(),
326 ab.
cfg[
"description"].t_str() );
334 gender_value(ab.
cfg, gender,
"name_inactive",
335 "female_name_inactive",
"name_inactive");
337 gender_value(ab.
cfg, gender,
"name",
"female_name",
"name").t_str();
342 default_value(ab.
cfg,
"name_inactive",
"name").t_str(),
344 default_value(ab.
cfg,
"description_inactive",
"description").t_str() );
355 std::vector<std::tuple<std::string, t_string,t_string,t_string>> res;
359 add_ability_tooltip(ab, gender_, res,
true);
367 std::vector<std::tuple<std::string, t_string,t_string,t_string>> res;
372 bool active = ability_active(ab.key, ab.cfg, loc);
373 if (add_ability_tooltip(ab, gender_, res, active))
375 active_list.push_back(active);
383 bool illuminates = ability ==
"illuminates";
391 const unit_map& units = get_unit_map();
395 std::size_t count = 0;
397 ufilt.set_use_flat_tod(illuminates);
404 if (unit == units.
end())
406 if (!ufilt(*unit, *
this))
408 if (
i.has_attribute(
"is_enemy")) {
410 if (
i[
"is_enemy"].to_bool() != dc.
get_team(unit->side()).is_enemy(side_)) {
416 if (
i[
"count"].empty() && count != dirs.size()) {
426 std::size_t count = 0;
428 adj_filter.flatten(illuminates);
436 if(!adj_filter.match(adjacent[
index])) {
441 if (
i[
"count"].empty() && count != dirs.size()) {
453 bool illuminates = ability ==
"illuminates";
455 assert(dir >=0 && dir <= 5);
460 if (
i.has_attribute(
"adjacent")) {
462 if (std::find(dirs.begin(), dirs.end(), direction) == dirs.end()) {
466 const config &filter =
i.child(
"filter");
468 unit_filter(
vconfig(filter)).set_use_flat_tod(illuminates).matches(*
this, loc, from) ) {
478 bool affect_self = cfg[
"affect_self"].to_bool(
true);
479 if (!filter || !affect_self)
return affect_self;
485 const std::string filter_tag_name = is_opp ?
"filter_second_weapon" :
"filter_weapon";
493 return weapon->matches_filter(filter);
498 return !abilities_.child_range(ability).empty();
504 callable.
add(
"attacker",
wfl::variant(std::make_shared<wfl::unit_callable>(*att)));
507 callable.
add(
"defender",
wfl::variant(std::make_shared<wfl::unit_callable>(*def)));
514 template<
typename T,
typename TFuncFormula>
515 class get_ability_value_visitor
516 #ifdef USING_BOOST_VARIANT
517 :
public boost::static_visitor<T>
522 get_ability_value_visitor(T def,
const TFuncFormula& formula_handler) : def_(def), formula_handler_(formula_handler) {}
524 T operator()(
const utils::monostate&)
const {
return def_; }
525 T operator()(
bool)
const {
return def_; }
526 T operator()(
int i)
const {
return static_cast<T
>(
i); }
527 T operator()(
unsigned long long u)
const {
return static_cast<T
>(u); }
528 T operator()(
double d)
const {
return static_cast<T
>(
d); }
529 T operator()(
const t_string&)
const {
return def_; }
530 T operator()(
const std::string&
s)
const 532 if(s.size() >= 2 && s[0] ==
'(') {
533 return formula_handler_(s);
535 return lexical_cast_default<T>(
s, def_);
540 const TFuncFormula& formula_handler_;
543 template<
typename T,
typename TFuncFormula>
546 return v.
apply_visitor(get_ability_value_visitor(def, [&](
const std::string&
s) {
549 const unit_map& units = get_unit_map();
553 if(u_itor == units.
end()) {
558 att->add_formula_context(callable);
561 callable.
add(
"student",
wfl::variant(std::make_shared<wfl::unit_callable>(*uptr)));
564 callable.
add(
"other",
wfl::variant(std::make_shared<wfl::unit_callable>(*uptr)));
569 ERR_WML <<
"Formula error in ability or weapon special: " << e.
type <<
" at " << e.
filename <<
':' << e.
line <<
")";
576 template<
typename TComp>
579 if ( cfgs_.empty() ) {
585 bool only_cumulative =
true;
595 if ((*
p.ability_cfg)[
"cumulative"].to_bool()) {
597 if (value < 0) value = -value;
598 if (only_cumulative && !comp(value, abs_max)) {
600 best_loc =
p.teacher_loc;
602 }
else if (only_cumulative || comp(flat, value)) {
603 only_cumulative =
false;
605 best_loc =
p.teacher_loc;
608 return std::pair(flat + stack, best_loc);
611 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;
612 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;
646 std::string tag_name;
662 bool get_special_children(std::vector<special_match>& tag_result,
663 std::vector<special_match>& id_result,
664 const config& parent,
const std::string&
id,
665 bool just_peeking=
false) {
668 if (just_peeking && (
sp.key ==
id ||
sp.cfg[
"id"] ==
id)) {
673 special_match special = {
sp.key, &
sp.cfg };
674 tag_result.push_back(special);
676 if(
sp.cfg[
"id"] ==
id) {
677 special_match special = {
sp.key, &
sp.cfg };
678 id_result.push_back(special);
684 bool get_special_children_id(std::vector<special_match>& id_result,
685 const config& parent,
const std::string&
id,
686 bool just_peeking=
false) {
689 if (just_peeking && (
sp.cfg[
"id"] ==
id)) {
693 if(
sp.cfg[
"id"] ==
id) {
694 special_match special = {
sp.key, &
sp.cfg };
695 id_result.push_back(special);
701 bool get_special_children_tags(std::vector<special_match>& tag_result,
702 const config& parent,
const std::string&
id,
703 bool just_peeking=
false) {
706 if (just_peeking && (
sp.key ==
id)) {
711 special_match special = {
sp.key, &
sp.cfg };
712 tag_result.push_back(special);
729 std::vector<special_match> special_tag_matches;
730 std::vector<special_match> special_id_matches;
731 if(special_id && special_tags){
732 if ( get_special_children(special_tag_matches, special_id_matches, specials_, special, simple_check) ) {
735 }
else if(special_id && !special_tags){
736 if ( get_special_children_id(special_id_matches, specials_, special, simple_check) ) {
739 }
else if(!special_id && special_tags){
740 if ( get_special_children_tags(special_tag_matches, specials_, special, simple_check) ) {
748 for(
const special_match& entry : special_tag_matches) {
749 if ( special_active(*entry.cfg, AFFECT_SELF, entry.tag_name) ) {
755 for(
const special_match& entry : special_id_matches) {
756 if ( special_active(*entry.cfg, AFFECT_SELF, entry.tag_name) ) {
764 if ( simple_check || !other_attack_ ) {
768 std::vector<special_match> special_tag_matches;
769 std::vector<special_match> special_id_matches;
770 if(special_id && special_tags){
771 get_special_children(special_tag_matches, special_id_matches, other_attack_->specials_, special);
772 }
else if(special_id && !special_tags){
773 get_special_children_id(special_id_matches, other_attack_->specials_, special);
774 }
else if(!special_id && special_tags){
775 get_special_children_tags(special_tag_matches, other_attack_->specials_, special);
778 for(
const special_match& entry : special_tag_matches) {
779 if ( other_attack_->special_active(*entry.cfg, AFFECT_OTHER, entry.tag_name) ) {
785 for(
const special_match& entry : special_id_matches) {
786 if ( other_attack_->special_active(*entry.cfg, AFFECT_OTHER, entry.tag_name) ) {
801 const map_location loc = self_ ? self_->get_location() : self_loc_;
804 for(
const config&
i : specials_.child_range(special)) {
805 if(special_active(
i, AFFECT_SELF, special)) {
814 for(
const config&
i : other_attack_->specials_.child_range(special)) {
815 if(other_attack_->special_active(
i, AFFECT_OTHER, special)) {
832 boost::dynamic_bitset<>* active_list)
const 835 std::vector<std::pair<t_string, t_string>> res;
837 active_list->clear();
841 if ( !active_list || special_active(
sp.cfg, AFFECT_EITHER,
sp.key) ) {
844 res.emplace_back(name,
sp.cfg[
"description"].t_str() );
846 active_list->push_back(
true);
849 const t_string& name = default_value(
sp.cfg,
"name_inactive",
"name").t_str();
851 res.emplace_back(name, default_value(
sp.cfg,
"description_inactive",
"description").t_str() );
852 active_list->push_back(
false);
867 static void add_name(std::string& temp_string,
bool active,
const std::string name, std::set<std::string>& checking_name)
870 if (!name.empty() && checking_name.count(name) == 0) {
871 checking_name.insert(name);
872 if (!temp_string.empty()) temp_string +=
", ";
891 const bool active = special_active(
sp.cfg, AFFECT_EITHER,
sp.key);
893 const std::string& name =
895 ?
sp.cfg[
"name"].str()
896 : default_value(
sp.cfg,
"name_inactive",
"name").str();
898 if (!res.empty()) res +=
", ";
901 if (!active) res +=
"</span>";
904 std::string temp_string;
905 std::set<std::string> checking_name;
906 weapon_specials_impl_self(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name);
907 weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name, {},
"affect_allies");
908 if(!temp_string.empty() && !res.empty()) {
909 temp_string =
", \n" + temp_string;
911 }
else if (!temp_string.empty()){
917 static void add_name_list(std::string& temp_string, std::string& weapon_abilities, std::set<std::string>& checking_name,
const std::string from_str)
919 if(!temp_string.empty()){
921 weapon_abilities += (!weapon_abilities.empty() && !temp_string.empty()) ?
"\n" :
"";
922 weapon_abilities += temp_string;
924 checking_name.clear();
931 std::string temp_string, weapon_abilities;
932 std::set<std::string> checking_name;
934 if((checking_tags.count(
sp.key) != 0)){
935 const bool active = special_active(
sp.cfg, AFFECT_SELF,
sp.key);
936 add_name(temp_string, active,
sp.cfg[
"name"].str(), checking_name);
939 add_name_list(temp_string, weapon_abilities, checking_name,
"");
941 weapon_specials_impl_self(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name, checking_tags,
true);
942 add_name_list(temp_string, weapon_abilities, checking_name,
"Owned: ");
944 weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name, checking_tags,
"affect_allies",
true);
945 add_name_list(temp_string, weapon_abilities, checking_name,
"Taught: ");
947 weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name, checking_tags,
"affect_enemies",
true);
948 add_name_list(temp_string, weapon_abilities, checking_name,
"Taught: (by an enemy): ");
953 if((checking_tags.count(
sp.key) != 0)){
954 const bool active = other_attack_->special_active(
sp.cfg, AFFECT_OTHER,
sp.key);
955 add_name(temp_string, active,
sp.cfg[
"name"].str(), checking_name);
959 weapon_specials_impl_self(temp_string, other_, other_attack_, shared_from_this(), other_loc_, AFFECT_OTHER, checking_name, checking_tags);
960 weapon_specials_impl_adj(temp_string, other_, other_attack_, shared_from_this(), other_loc_, AFFECT_OTHER, checking_name, checking_tags);
961 add_name_list(temp_string, weapon_abilities, checking_name,
"Used by opponent: ");
963 return weapon_abilities;
967 std::string& temp_string,
973 std::set<std::string>& checking_name,
974 const std::set<std::string>& checking_tags,
979 bool tag_checked = (!checking_tags.empty()) ? (checking_tags.count(
sp.key) != 0) :
true;
980 const bool active = tag_checked && check_self_abilities_impl(self_attack, other_attack,
sp.cfg,
self, self_loc, whom,
sp.key, leader_bool);
981 add_name(temp_string, active,
sp.cfg[
"name"].str(), checking_name);
987 std::string& temp_string,
993 std::set<std::string>& checking_name,
994 const std::set<std::string>& checking_tags,
995 const std::string& affect_adjacents,
998 const unit_map& units = get_unit_map();
1001 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
1003 if (it == units.
end() || it->incapacitated())
1005 if(&*it ==
self.
get())
1008 bool tag_checked = (!checking_tags.empty()) ? (checking_tags.count(
sp.key) != 0) :
true;
1009 bool default_bool = (affect_adjacents ==
"affect_allies") ?
true :
false;
1010 bool affect_allies = (!affect_adjacents.empty()) ?
sp.cfg[affect_adjacents].to_bool(default_bool) :
true;
1011 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;
1012 add_name(temp_string, active,
sp.cfg[
"name"].str(), checking_name);
1038 : parent(weapon.shared_from_this())
1040 weapon.
self_ =
self;
1058 :
parent(weapon.shared_from_this())
1060 weapon.
self_ =
self;
1078 :
parent(weapon.shared_from_this())
1091 :
parent(weapon.shared_from_this())
1104 parent->is_attacker_ =
false;
1105 parent->other_attack_ =
nullptr;
1106 parent->is_for_listing_ =
false;
1112 other.was_moved =
true;
1123 unsigned & max_attacks)
const 1128 if ( attacks_value < 0 ) {
1130 ERR_NG <<
"negative number of strikes after applying weapon specials";
1135 if ( !swarm_specials.
empty() ) {
1136 min_attacks = std::max<int>(0, swarm_specials.
highest(
"swarm_attacks_min").first);
1137 max_attacks = std::max<int>(0, swarm_specials.
highest(
"swarm_attacks_max", attacks_value).first);
1139 min_attacks = max_attacks = attacks_value;
1150 return damage_value;
1162 bool special_affects_opponent(
const config& special,
bool is_attacker)
1165 const std::string& apply_to = special[
"apply_to"];
1166 if ( apply_to.empty() )
1168 if ( apply_to ==
"both" )
1170 if ( apply_to ==
"opponent" )
1172 if ( is_attacker && apply_to ==
"defender" )
1174 if ( !is_attacker && apply_to ==
"attacker" )
1184 bool special_affects_self(
const config& special,
bool is_attacker)
1187 const std::string& apply_to = special[
"apply_to"];
1188 if ( apply_to.empty() )
1190 if ( apply_to ==
"both" )
1192 if ( apply_to ==
"self" )
1194 if ( is_attacker && apply_to ==
"attacker" )
1196 if ( !is_attacker && apply_to ==
"defender")
1217 const bool for_listing,
1218 const std::string & child_tag)
1220 if (for_listing && !loc.
valid())
1228 const config & filter_child = filter.
child(child_tag);
1229 if ( !filter_child )
1244 if (
const config & filter_weapon = filter_child.
child(
"filter_weapon") ) {
1245 if ( !weapon || !weapon->matches_filter(filter_weapon) )
1252 return ufilt.matches(*u, loc);
1254 return ufilt.matches(*u, loc, *u2);
1288 if(!abil_list.
empty()){
1292 if(!spe_list.
empty()){
1294 if(special ==
"plague" && !spe_list.
empty()){
1297 abil_list.
append(spe_list);
1309 const std::string& apply_to = special[
"overwrite_specials"];
1310 return (apply_to ==
"one_side" || apply_to ==
"both_sides");
1322 if(overwriters.
empty()){
1326 for(
const auto&
i : overwriters) {
1327 bool affect_side = ((*
i.ability_cfg)[
"overwrite_specials"] ==
"one_side");
1330 bool one_side_overwritable =
true;
1331 if(affect_side && is_overwritable){
1339 return (is_overwritable && one_side_overwritable);
1357 std::vector<special_match>& id_result,
1359 bool special_id=
true,
bool special_tags=
true) {
1360 if(special_id && special_tags){
1361 get_special_children(tag_result, id_result, parent,
id);
1362 }
else if(special_id && !special_tags){
1363 get_special_children_id(id_result, parent,
id);
1364 }
else if(!special_id && special_tags){
1365 get_special_children_tags(tag_result, parent,
id);
1371 return (ability_active(tag_name, special, loc) && ability_affects_self(tag_name, special, loc));
1377 return (affects_side(special, side(), from.
side()) && from.
ability_active(tag_name, special, adjacent[dir]) && ability_affects_adjacent(tag_name, special, dir, loc, from));
1382 return (get_self_ability_bool(special, tag_name, loc) && ability_affects_weapon(special, weapon,
false) && ability_affects_weapon(special, opp_weapon,
true));
1387 return (get_adj_ability_bool(special, tag_name, dir, loc, from) && ability_affects_weapon(special, weapon,
false) && ability_affects_weapon(special, opp_weapon,
true));
1397 if(tag_name ==
"leadership" && leader_bool){
1398 if((*u).get_self_ability_bool_weapon(special, tag_name, loc, self_attack, other_attack)) {
1402 if((*u).checking_tags().count(tag_name) != 0){
1403 if((*u).get_self_ability_bool(special, tag_name, loc) &&
special_active_impl(self_attack, other_attack, special, whom, tag_name,
"filter_student")) {
1417 if(tag_name ==
"leadership" && leader_bool){
1418 if((*u).get_adj_ability_bool_weapon(special, tag_name, dir, loc, from, self_attack, other_attack)) {
1422 if((*u).checking_tags().count(tag_name) != 0){
1423 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")) {
1437 const unit_map& units = get_unit_map();
1439 std::vector<special_match> special_tag_matches_self;
1440 std::vector<special_match> special_id_matches_self;
1441 get_ability_children(special_tag_matches_self, special_id_matches_self, (*self_).abilities(), special, special_id , special_tags);
1443 for(
const special_match& entry : special_tag_matches_self) {
1450 for(
const special_match& entry : special_id_matches_self) {
1458 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
1460 if (it == units.
end() || it->incapacitated())
1462 if ( &*it ==
self_.get() )
1465 std::vector<special_match> special_tag_matches_adj;
1466 std::vector<special_match> special_id_matches_adj;
1467 get_ability_children(special_tag_matches_adj, special_id_matches_adj, it->abilities(), special, special_id , special_tags);
1469 for(
const special_match& entry : special_tag_matches_adj) {
1476 for(
const special_match& entry : special_id_matches_adj) {
1486 std::vector<special_match> special_tag_matches_other;
1487 std::vector<special_match> special_id_matches_other;
1488 get_ability_children(special_tag_matches_other, special_id_matches_other, (*other_).abilities(), special, special_id , special_tags);
1490 for(
const special_match& entry : special_tag_matches_other) {
1498 for(
const special_match& entry : special_id_matches_other) {
1506 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
1508 if (it == units.
end() || it->incapacitated())
1510 if ( &*it ==
other_.get() )
1513 std::vector<special_match> special_tag_matches_oadj;
1514 std::vector<special_match> special_id_matches_oadj;
1515 get_ability_children(special_tag_matches_oadj, special_id_matches_oadj, it->abilities(), special, special_id , special_tags);
1517 for(
const special_match& entry : special_tag_matches_oadj) {
1525 for(
const special_match& entry : special_id_matches_oadj) {
1543 const std::string& filter_self)
const 1563 const std::string& tag_name,
1564 const std::string& filter_self)
1566 assert(self_attack || other_attack);
1567 bool is_attacker = self_attack ? self_attack->is_attacker_ : !other_attack->is_attacker_;
1568 bool is_for_listing = self_attack ? self_attack->is_for_listing_ : other_attack->is_for_listing_;
1574 if ( !special_affects_self(special, is_attacker) )
1578 if ( !special_affects_opponent(special, is_attacker) )
1583 const std::string & active_on = special[
"active_on"];
1584 if ( !active_on.empty() ) {
1585 if ( is_attacker && active_on !=
"offense" )
1587 if ( !is_attacker && active_on !=
"defense" )
1592 const unit_map& units = get_unit_map();
1594 unit_const_ptr self = self_attack ? self_attack->self_ : other_attack->other_;
1595 unit_const_ptr other = self_attack ? self_attack->other_ : other_attack->self_;
1596 map_location self_loc = self_attack ? self_attack->self_loc_ : other_attack->other_loc_;
1597 map_location other_loc = self_attack ? self_attack->other_loc_ : other_attack->self_loc_;
1599 if(
self ==
nullptr) {
1605 if(other ==
nullptr) {
1614 temporary_facing other_facing(other, other_loc.
get_relative_dir(self_loc));
1618 bool whom_is_self = ((whom ==
AFFECT_SELF) || ((whom ==
AFFECT_EITHER) && special_affects_self(special, is_attacker)));
1620 map_location their_loc = whom_is_self ? other_loc : self_loc;
1622 if (tag_name ==
"drains" && them && them->get_state(
"undrainable")) {
1625 if (tag_name ==
"plague" && them &&
1626 (them->get_state(
"unplagueable") ||
1630 if (tag_name ==
"poison" && them &&
1634 if (tag_name ==
"slow" && them &&
1638 if (tag_name ==
"petrifies" && them &&
1639 them->get_state(
"unpetrifiable")) {
1647 const map_location & att_loc = is_attacker ? self_loc : other_loc;
1648 const map_location & def_loc = is_attacker ? other_loc : self_loc;
1654 if (tag_name ==
"firststrike") {
1655 bool whom_is_defender = whom_is_self ? !is_attacker : is_attacker;
1656 if (whom_is_defender && att_weapon && att_weapon->has_special_or_ability(
"firststrike"))
1661 if (!special[
"backstab"].
blank()) {
1662 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.");
1665 if(special[
"backstab"].to_bool()){
1666 const std::string& backstab_formula =
"enemy_of(self, flanker) and not flanker.petrified where flanker = unit_at(direction_from(loc, other.facing))";
1668 if(!special.
has_child(
"filter_opponent")){
1669 filter_child[
"formula"] = backstab_formula;
1672 filter[
"formula"] = backstab_formula;
1676 const config& special_backstab = special[
"backstab"].to_bool() ? cfg : special;
1679 if (!special_unit_matches(
self, other, self_loc, self_attack, special, is_for_listing, filter_self))
1681 if (!special_unit_matches(other,
self, other_loc, other_attack, special_backstab, is_for_listing,
"filter_opponent"))
1683 if (!special_unit_matches(att, def, att_loc, att_weapon, special, is_for_listing,
"filter_attacker"))
1685 if (!special_unit_matches(def, att, def_loc, def_weapon, special, is_for_listing,
"filter_defender"))
1693 std::size_t count = 0;
1701 if (unit == units.
end() || !filter.matches(*unit, adjacent[index], *
self))
1703 if (
i.has_attribute(
"is_enemy")) {
1705 if (
i[
"is_enemy"].to_bool() != dc.
get_team(unit->side()).is_enemy(self->side())) {
1711 if (
i[
"count"].empty() && count != dirs.size()) {
1722 std::size_t count = 0;
1729 if(!adj_filter.match(adjacent[
index])) {
1734 if (
i[
"count"].empty() && count != dirs.size()) {
1760 if (
const config &apply_filter = cfg.
child(
"filter_base_value")) {
1767 return (cond_eq.
empty() || def == cond_eq.
to_int()) &&
1782 int value_set = is_cumulable ? std::max(list.
highest(
"value").first, 0) + std::min(list.
lowest(
"value").first, 0) : def;
1783 std::map<std::string,individual_effect> values_add;
1784 std::map<std::string,individual_effect> values_mul;
1785 std::map<std::string,individual_effect> values_div;
1791 const config& cfg = *ability.ability_cfg;
1792 const std::string& effect_id = cfg[cfg[
"id"].
empty() ?
"name" :
"id"];
1801 return formula.evaluate(callable).as_int();
1804 int value_cum = cfg[
"cumulative"].to_bool() ? std::max(def, value) : value;
1807 set_effect_min.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
1808 set_effect_max.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
1811 if(value_cum > set_effect_max.
value) {
1812 set_effect_max.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
1814 if(value_cum < set_effect_min.
value) {
1815 set_effect_min.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
1824 return formula.evaluate(callable).as_int();
1827 if(add_effect == values_add.end() || add > add_effect->second.value) {
1828 values_add[effect_id].set(
ADD, add, ability.ability_cfg, ability.teacher_loc);
1834 return formula.evaluate(callable).as_int();
1837 if(sub_effect == values_add.end() || sub < sub_effect->second.value) {
1838 values_add[effect_id].set(
ADD, sub, ability.ability_cfg, ability.teacher_loc);
1842 int multiply =
static_cast<int>(get_single_ability_value(*v, static_cast<double>(def), ability, list.loc(), att, [&](
const wfl::formula& formula,
wfl::map_formula_callable& callable) {
1844 return formula.evaluate(callable).as_decimal() / 1000.0 ;
1847 if(mul_effect == values_mul.end() || multiply > mul_effect->second.value) {
1848 values_mul[effect_id].set(
MUL, multiply, ability.ability_cfg, ability.teacher_loc);
1852 int divide =
static_cast<int>(get_single_ability_value(*v, static_cast<double>(def), ability, list.loc(), att, [&](
const wfl::formula& formula,
wfl::map_formula_callable& callable) {
1854 return formula.evaluate(callable).as_decimal() / 1000.0 ;
1858 ERR_NG <<
"division by zero with divide= in ability/weapon special " << effect_id;
1862 if(div_effect == values_div.end() || divide > div_effect->second.value) {
1863 values_div[effect_id].set(
DIV, divide, ability.ability_cfg, ability.teacher_loc);
1870 value_set = std::max(set_effect_max.
value, 0) + std::min(set_effect_min.
value, 0);
1871 if(set_effect_max.
value > def) {
1874 if(set_effect_min.
value < def) {
1887 double multiplier = 1.0;
1888 double divisor = 1.0;
1890 for(
const auto& val : values_mul) {
1891 multiplier *= val.second.value/100.0;
1895 for(
const auto& val : values_div) {
1896 divisor *= val.second.value/100.0;
1901 for(
const auto& val : values_add) {
1902 addition += val.second.value;
1906 composite_value_ =
static_cast<int>((value_set + addition) * multiplier / divisor);
int composite_value(const unit_ability_list &abil_list, int base_value) const
Return the special weapon value, considering specials.
const color_t BUTTON_COLOR
bool empty() const
Tests for an attribute that either was never set or was set to "".
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...
std::vector< individual_effect > effect_list_
config & child(config_key_type key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
static display * get_singleton()
Returns the display object if a display object exists.
const_all_children_itors all_children_range() const
In-order iteration over all children.
const team & get_team(int side) const
virtual const display_context & get_disp_context() const =0
const config * ability_cfg
The contents of the ability tag, never nullptr.
void get_adjacent_tiles(const map_location &a, map_location *res)
Function which, given a location, will place all adjacent locations in res.
virtual const unit_map & units() const override
void set(CURSOR_TYPE type)
Use the default parameter to reset cursors.
DIRECTION get_relative_dir(const map_location &loc, map_location::RELATIVE_DIR_MODE mode) const
std::string weapon_specials_value(const std::set< std::string > checking_tags) const
This class represents a single unit of a specific type.
const color_t inactive_details_color
Interfaces for manipulating version numbers of engine, add-ons, etc.
static void add_name(std::string &temp_string, bool active, const std::string name, std::set< std::string > &checking_name)
unit_filter & set_use_flat_tod(bool value)
void emplace_back(T &&... args)
iterator erase(const iterator &erase_it)
Variant for storing WML attributes.
New lexcical_cast header.
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.
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
void set(value_modifier t, int val, const config *abil, const map_location &l)
child_itors child_range(config_key_type key)
map_location student_loc
Used by the formula in the ability.
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 ...
virtual const gamemap & map() const override
The unit is slowed - it moves slower and does less damage.
const unit_map & get_units() const
bool has_ability_type(const std::string &ability) const
Check if the unit has an ability of a specific type.
config & child_or_add(config_key_type key)
Returns a reference to the first child with the given key.
const std::string & type() const
bool matches(const unit &u, const map_location &loc) const
Determine if *this matches filter at a specified location.
int to_int(int def=0) const
static std::vector< DIRECTION > parse_directions(const std::string &str)
Parse_directions takes a comma-separated list, and filters out any invalid directions.
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.
std::shared_ptr< unit > unit_ptr
std::string dsgettext(const char *domainname, const char *msgid)
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.
A single unit type that the player may recruit.
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...
bool filter_base_matches(const config &cfg, int def)
std::vector< unit_ability >::iterator iterator
unit_ptr find_unit_ptr(const T &val)
std::shared_ptr< const unit > unit_const_ptr
static lg::log_domain log_wml("wml")
std::string span_color(const color_t &color)
Returns a Pango formatting string using the provided color_t object.
This class stores all the data for a single 'side' (in game nomenclature).
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...
Data typedef for unit_ability_list.
map_location teacher_loc
The location of the teacher, that is the unit who owns the ability tags (different from student becau...
std::vector< std::pair< int, int > > parse_ranges(const std::string &str)
std::string weapon_specials() const
Returns a comma-separated string of active names for the specials of *this.
std::string deprecated_message(const std::string &elem_name, DEP_LEVEL level, const version_info &version, const std::string &detail)
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.
filter_context * filter_con
specials_context_t(const attack_type &weapon, bool attacking)
Initialize weapon specials context for listing.
bool blank() const
Tests for an attribute that was never set.
bool is_enemy(int n) const
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
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.
int modified_damage() const
Returns the damage per attack of this weapon, considering specials.
map_display and display: classes which take care of displaying the map and game-data on the screen...
std::pair< int, map_location > get_extremum(const std::string &key, int def, const TComp &comp) const
static void add_name_list(std::string &temp_string, std::string &weapon_abilities, std::set< std::string > &checking_name, const std::string from_str)
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 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.
Encapsulates the map of the game.
std::pair< int, map_location > lowest(const std::string &key, int def=0) const
unit_iterator find(std::size_t id)
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.
void erase_if(Container &container, const Predicate &predicate)
Convenience wrapper for using std::remove_if on a container.
unit_ability_list overwrite_special_checking(const std::string &ability, unit_ability_list input, unit_ability_list overwriters, const std::string &filter_self, bool is_special) const
Filter a list of abilities or weapon specials, removing any entries that are overridden by the overwr...
pointer get_shared_ptr() const
This is exactly the same as operator-> but it's slightly more readable, and can replace &*iter syntax...
bool has_weapon_ability(const std::string &special, bool special_id=true, bool special_tags=true) const
used for abilities used like weapon
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.
unit_ability_list get_weapon_ability(const std::string &ability) const
Returns list for weapon like abilities for each ability type.
static map_location::DIRECTION s
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 display_context & get_disp_context() const
int get_composite_value() const
void append(const unit_ability_list &other)
Appends the abilities from other to this, ignores other.loc()
DIRECTION
Valid directions which can be moved in our hexagonal world.
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.
config & add_child(config_key_type key)
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...
void add_formula_context(wfl::map_formula_callable &) const
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 is_village(const map_location &loc) const
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...
static bool overwrite_special_affects(const config &special)
unit_ability_list get_specials_and_abilities(const std::string &special) const
Returns list who contains get_weapon_ability and get_specials list for each ability type...
void modified_attacks(unsigned &min_attacks, unsigned &max_attacks) const
Calculates the number of attacks this weapon has, considering specials.
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)
const_attack_ptr other_attack_
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, ignores other.loc().
A variable-expanding proxy for the config class.
Standard logging facilities (interface).
auto apply_visitor(const V &visitor) const
Visitor support: Applies a visitor to the underlying variant.
static const map_location & null_location()
Container associating units to locations.
int side() const
The side this unit belongs to.
std::vector< std::string > get_ability_list() const
Get a list of all abilities by ID.
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_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
std::shared_ptr< const attack_type > parent
bool ability_active(const std::string &ability, const config &cfg, const map_location &loc) const
Check if an ability is active.
A config object defines a single node in a WML file, with access to child nodes.
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...
std::shared_ptr< const attack_type > const_attack_ptr
std::string::const_iterator iterator
static lg::log_domain log_engine("engine")
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.
std::pair< int, map_location > highest(const std::string &key, int def=0) const
std::stringstream & log_to_chat()
Use this to show WML errors in the ingame chat.
bool special_active(const config &special, AFFECTS whom, const std::string &tag_name, const std::string &filter_self="filter_self") const