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();
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]) &&
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])
271 std::vector<std::string> res;
274 std::string
id = ab.cfg[
"id"];
276 res.push_back(std::move(
id));
297 ab.
cfg[
"name"].t_str(),
299 ab.
cfg[
"description"].t_str() );
308 "female_name_inactive",
"name_inactive");
328 std::vector<std::tuple<std::string, t_string,t_string,t_string>> res;
332 add_ability_tooltip(ab,
gender_, res,
true);
340 std::vector<std::tuple<std::string, t_string,t_string,t_string>> res;
346 if (add_ability_tooltip(ab,
gender_, res, active))
348 active_list.push_back(active);
356 bool illuminates = ability ==
"illuminates";
359 if ( !
unit_filter(
vconfig(*afilter)).set_use_flat_tod(illuminates).matches(*
this, loc) )
364 const unit_map& units = get_unit_map();
368 std::size_t count = 0;
370 ufilt.set_use_flat_tod(illuminates);
379 if (!ufilt(*
unit, *
this))
381 if((*this).id() == (*unit).id())
383 if (
i.has_attribute(
"is_enemy")) {
391 if (
i[
"count"].empty() && count != dirs.size()) {
401 std::size_t count = 0;
403 adj_filter.flatten(illuminates);
411 if(!adj_filter.match(adjacent[
index])) {
416 if (
i[
"count"].empty() && count != dirs.size()) {
428 bool illuminates = ability ==
"illuminates";
430 assert(dir >=0 && dir <= 5);
435 if (
i.has_attribute(
"adjacent")) {
437 if (std::find(dirs.begin(), dirs.end(), direction) == dirs.end()) {
441 if((*this).id() == from.
id()){
444 auto filter =
i.optional_child(
"filter");
446 unit_filter(
vconfig(*filter)).set_use_flat_tod(illuminates).matches(*
this, loc, from) ) {
456 bool affect_self = cfg[
"affect_self"].to_bool(
true);
457 if (!filter || !affect_self)
return affect_self;
463 const std::string filter_tag_name = is_opp ?
"filter_second_weapon" :
"filter_weapon";
471 return weapon->matches_filter(filter);
482 callable.
add(
"attacker",
wfl::variant(std::make_shared<wfl::unit_callable>(*att)));
485 callable.
add(
"defender",
wfl::variant(std::make_shared<wfl::unit_callable>(*def)));
492 template<
typename T,
typename TFuncFormula>
493 class get_ability_value_visitor
494 #ifdef USING_BOOST_VARIANT
495 :
public boost::static_visitor<T>
500 get_ability_value_visitor(T def,
const TFuncFormula& formula_handler) : def_(def), formula_handler_(formula_handler) {}
502 T operator()(
const utils::monostate&)
const {
return def_; }
503 T operator()(
bool)
const {
return def_; }
504 T operator()(
int i)
const {
return static_cast<T
>(
i); }
505 T operator()(
unsigned long long u)
const {
return static_cast<T
>(u); }
506 T operator()(
double d)
const {
return static_cast<T
>(
d); }
507 T operator()(
const t_string&)
const {
return def_; }
508 T operator()(
const std::string&
s)
const
510 if(
s.size() >= 2 &&
s[0] ==
'(') {
511 return formula_handler_(
s);
513 return lexical_cast_default<T>(
s, def_);
518 const TFuncFormula& formula_handler_;
521 template<
typename T,
typename TFuncFormula>
524 return v.
apply_visitor(get_ability_value_visitor(def, [&](
const std::string&
s) {
527 const unit_map& units = get_unit_map();
531 if(u_itor == units.
end()) {
536 att->add_formula_context(callable);
539 callable.add(
"student",
wfl::variant(std::make_shared<wfl::unit_callable>(*uptr)));
542 callable.add(
"other",
wfl::variant(std::make_shared<wfl::unit_callable>(*uptr)));
546 lg::log_to_chat() <<
"Formula error in ability or weapon special: " <<
e.type <<
" at " <<
e.filename <<
':' <<
e.line <<
")\n";
547 ERR_WML <<
"Formula error in ability or weapon special: " <<
e.type <<
" at " <<
e.filename <<
':' <<
e.line <<
")";
554 template<
typename TComp>
557 if (
cfgs_.empty() ) {
563 bool only_cumulative =
true;
570 return formula.
evaluate(callable).as_int();
573 if ((*
p.ability_cfg)[
"cumulative"].to_bool()) {
575 if (value < 0) value = -value;
576 if (only_cumulative && !comp(value, abs_max)) {
578 best_loc =
p.teacher_loc;
580 }
else if (only_cumulative || comp(flat, value)) {
581 only_cumulative =
false;
583 best_loc =
p.teacher_loc;
586 return std::pair(flat + stack, best_loc);
589 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;
590 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;
624 std::string tag_name;
640 bool get_special_children(std::vector<special_match>& tag_result,
641 std::vector<special_match>& id_result,
642 const config& parent,
const std::string&
id,
643 bool just_peeking=
false) {
646 if (just_peeking && (
sp.key ==
id ||
sp.cfg[
"id"] ==
id)) {
651 special_match special = {
sp.key, &
sp.cfg };
652 tag_result.push_back(special);
654 if(
sp.cfg[
"id"] ==
id) {
655 special_match special = {
sp.key, &
sp.cfg };
656 id_result.push_back(special);
662 bool get_special_children_id(std::vector<special_match>& id_result,
663 const config& parent,
const std::string&
id,
664 bool just_peeking=
false) {
667 if (just_peeking && (
sp.cfg[
"id"] ==
id)) {
671 if(
sp.cfg[
"id"] ==
id) {
672 special_match special = {
sp.key, &
sp.cfg };
673 id_result.push_back(special);
679 bool get_special_children_tags(std::vector<special_match>& tag_result,
680 const config& parent,
const std::string&
id,
681 bool just_peeking=
false) {
684 if (just_peeking && (
sp.key ==
id)) {
689 special_match special = {
sp.key, &
sp.cfg };
690 tag_result.push_back(special);
707 std::vector<special_match> special_tag_matches;
708 std::vector<special_match> special_id_matches;
709 if(special_id && special_tags){
710 if ( get_special_children(special_tag_matches, special_id_matches,
specials_, special, simple_check) ) {
713 }
else if(special_id && !special_tags){
714 if ( get_special_children_id(special_id_matches,
specials_, special, simple_check) ) {
717 }
else if(!special_id && special_tags){
718 if ( get_special_children_tags(special_tag_matches,
specials_, special, simple_check) ) {
726 for(
const special_match& entry : special_tag_matches) {
733 for(
const special_match& entry : special_id_matches) {
746 std::vector<special_match> special_tag_matches;
747 std::vector<special_match> special_id_matches;
748 if(special_id && special_tags){
749 get_special_children(special_tag_matches, special_id_matches,
other_attack_->specials_, special);
750 }
else if(special_id && !special_tags){
751 get_special_children_id(special_id_matches,
other_attack_->specials_, special);
752 }
else if(!special_id && special_tags){
753 get_special_children_tags(special_tag_matches,
other_attack_->specials_, special);
756 for(
const special_match& entry : special_tag_matches) {
763 for(
const special_match& entry : special_id_matches) {
810 boost::dynamic_bitset<>* active_list)
const
813 std::vector<std::pair<t_string, t_string>> res;
815 active_list->clear();
822 res.emplace_back(
name,
sp.cfg[
"description"].t_str() );
824 active_list->push_back(
true);
827 const t_string&
name =
sp.cfg.get_or(
"name_inactive",
"name").t_str();
829 res.emplace_back(
name,
sp.cfg.get_or(
"description_inactive",
"description").t_str() );
830 active_list->push_back(
false);
845 static void add_name(std::string& temp_string,
bool active,
const std::string name, std::set<std::string>& checking_name)
848 if (!name.empty() && checking_name.count(name) == 0) {
849 checking_name.insert(name);
850 if (!temp_string.empty()) temp_string +=
", ";
871 const std::string&
name =
873 ?
sp.cfg[
"name"].str()
874 :
sp.cfg.get_or(
"name_inactive",
"name").str();
876 if (!res.empty()) res +=
", ";
879 if (!active) res +=
"</span>";
882 std::string temp_string;
883 std::set<std::string> checking_name;
886 if(!temp_string.empty() && !res.empty()) {
887 temp_string =
", \n" + temp_string;
889 }
else if (!temp_string.empty()){
895 static void add_name_list(std::string& temp_string, std::string& weapon_abilities, std::set<std::string>& checking_name,
const std::string from_str)
897 if(!temp_string.empty()){
898 temp_string = from_str.c_str() + temp_string;
899 weapon_abilities += (!weapon_abilities.empty() && !temp_string.empty()) ?
"\n" :
"";
900 weapon_abilities += temp_string;
902 checking_name.clear();
909 std::string temp_string, weapon_abilities;
910 std::set<std::string> checking_name;
912 if((checking_tags.count(
sp.key) != 0)){
914 add_name(temp_string, active,
sp.cfg[
"name"].str(), checking_name);
917 add_name_list(temp_string, weapon_abilities, checking_name,
"");
920 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Owned: "));
923 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Taught: "));
926 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Taught: (by an enemy): "));
931 if((checking_tags.count(
sp.key) != 0)){
933 add_name(temp_string, active,
sp.cfg[
"name"].str(), checking_name);
939 add_name_list(temp_string, weapon_abilities, checking_name,
_(
"Used by opponent: "));
941 return weapon_abilities;
945 std::string& temp_string,
951 std::set<std::string>& checking_name,
952 const std::set<std::string>& checking_tags,
957 bool tag_checked = (!checking_tags.empty()) ? (checking_tags.count(
sp.key) != 0) :
true;
959 add_name(temp_string, active,
sp.cfg[
"name"].str(), checking_name);
965 std::string& temp_string,
971 std::set<std::string>& checking_name,
972 const std::set<std::string>& checking_tags,
973 const std::string& affect_adjacents,
976 const unit_map& units = get_unit_map();
979 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
981 if (it == units.
end() || it->incapacitated())
983 if(&*it ==
self.
get())
986 bool tag_checked = (!checking_tags.empty()) ? (checking_tags.count(
sp.key) != 0) :
true;
987 bool default_bool = (affect_adjacents ==
"affect_allies") ?
true :
false;
988 bool affect_allies = (!affect_adjacents.empty()) ?
sp.cfg[affect_adjacents].to_bool(default_bool) :
true;
989 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;
990 add_name(temp_string, active,
sp.cfg[
"name"].str(), checking_name);
1016 : parent(weapon.shared_from_this())
1018 weapon.
self_ =
self;
1036 : parent(weapon.shared_from_this())
1038 weapon.
self_ =
self;
1056 : parent(weapon.shared_from_this())
1069 : parent(weapon.shared_from_this())
1077 if(was_moved)
return;
1082 parent->is_attacker_ =
false;
1083 parent->other_attack_ =
nullptr;
1084 parent->is_for_listing_ =
false;
1088 : parent(other.parent)
1090 other.was_moved =
true;
1101 unsigned & max_attacks)
const
1106 if ( attacks_value < 0 ) {
1108 ERR_NG <<
"negative number of strikes after applying weapon specials";
1113 if ( !swarm_specials.
empty() ) {
1114 min_attacks = std::max<int>(0, swarm_specials.
highest(
"swarm_attacks_min").first);
1115 max_attacks = std::max<int>(0, swarm_specials.
highest(
"swarm_attacks_max", attacks_value).first);
1117 min_attacks = max_attacks = attacks_value;
1128 return damage_value;
1140 bool special_affects_opponent(
const config& special,
bool is_attacker)
1143 const std::string& apply_to = special[
"apply_to"];
1144 if ( apply_to.empty() )
1146 if ( apply_to ==
"both" )
1148 if ( apply_to ==
"opponent" )
1150 if ( is_attacker && apply_to ==
"defender" )
1152 if ( !is_attacker && apply_to ==
"attacker" )
1162 bool special_affects_self(
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 ==
"self" )
1172 if ( is_attacker && apply_to ==
"attacker" )
1174 if ( !is_attacker && apply_to ==
"defender")
1195 const bool for_listing,
1196 const std::string & child_tag)
1198 if (for_listing && !loc.
valid())
1207 if ( !filter_child )
1222 if (
auto filter_weapon = filter_child->optional_child(
"filter_weapon") ) {
1223 if ( !weapon || !weapon->matches_filter(*filter_weapon) )
1230 return ufilt.matches(*u, loc);
1232 return ufilt.matches(*u, loc, *u2);
1250 return special_active(*i.ability_cfg, AFFECT_SELF, ability,
"filter_student");
1256 return special_active_impl(other_attack_, shared_from_this(), *i.ability_cfg, AFFECT_OTHER, ability,
"filter_student");
1266 if(!abil_list.
empty()){
1270 if(!spe_list.
empty()){
1272 if(special ==
"plague" && !spe_list.
empty()){
1275 abil_list.
append(spe_list);
1287 const std::string& apply_to = special[
"overwrite_specials"];
1288 return (apply_to ==
"one_side" || apply_to ==
"both_sides");
1300 if(overwriters.
empty()){
1304 for(
const auto&
i : overwriters) {
1305 bool affect_side = ((*
i.ability_cfg)[
"overwrite_specials"] ==
"one_side");
1308 bool one_side_overwritable =
true;
1309 if(affect_side && is_overwritable){
1313 else if(special_affects_opponent(*
i.ability_cfg, !
is_attacker_)){
1317 return (is_overwritable && one_side_overwritable);
1335 std::vector<special_match>& id_result,
1336 const config& parent,
const std::string&
id,
1337 bool special_id=
true,
bool special_tags=
true) {
1338 if(special_id && special_tags){
1339 get_special_children(tag_result, id_result, parent,
id);
1340 }
else if(special_id && !special_tags){
1341 get_special_children_id(id_result, parent,
id);
1342 }
else if(!special_id && special_tags){
1343 get_special_children_tags(tag_result, parent,
id);
1349 return (ability_active(tag_name, special, loc) && ability_affects_self(tag_name, special, loc));
1355 return (affects_side(special, side(), from.
side()) && from.
ability_active(tag_name, special, adjacent[dir]) && ability_affects_adjacent(tag_name, special, dir, loc, from));
1360 return (get_self_ability_bool(special, tag_name, loc) && ability_affects_weapon(special, weapon,
false) && ability_affects_weapon(special, opp_weapon,
true));
1365 return (get_adj_ability_bool(special, tag_name, dir, loc, from) && ability_affects_weapon(special, weapon,
false) && ability_affects_weapon(special, opp_weapon,
true));
1375 if(tag_name ==
"leadership" && leader_bool){
1376 if((*u).get_self_ability_bool_weapon(special, tag_name, loc, self_attack, other_attack)) {
1380 if((*u).checking_tags().count(tag_name) != 0){
1381 if((*u).get_self_ability_bool(special, tag_name, loc) &&
special_active_impl(self_attack, other_attack, special, whom, tag_name,
"filter_student")) {
1395 if(tag_name ==
"leadership" && leader_bool){
1396 if((*u).get_adj_ability_bool_weapon(special, tag_name, dir, loc, from, self_attack, other_attack)) {
1400 if((*u).checking_tags().count(tag_name) != 0){
1401 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")) {
1415 const unit_map& units = get_unit_map();
1417 std::vector<special_match> special_tag_matches_self;
1418 std::vector<special_match> special_id_matches_self;
1419 get_ability_children(special_tag_matches_self, special_id_matches_self, (*self_).abilities(), special, special_id , special_tags);
1421 for(
const special_match& entry : special_tag_matches_self) {
1428 for(
const special_match& entry : special_id_matches_self) {
1436 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
1438 if (it == units.
end() || it->incapacitated())
1440 if ( &*it ==
self_.get() )
1443 std::vector<special_match> special_tag_matches_adj;
1444 std::vector<special_match> special_id_matches_adj;
1445 get_ability_children(special_tag_matches_adj, special_id_matches_adj, it->abilities(), special, special_id , special_tags);
1447 for(
const special_match& entry : special_tag_matches_adj) {
1454 for(
const special_match& entry : special_id_matches_adj) {
1464 std::vector<special_match> special_tag_matches_other;
1465 std::vector<special_match> special_id_matches_other;
1466 get_ability_children(special_tag_matches_other, special_id_matches_other, (*other_).abilities(), special, special_id , special_tags);
1468 for(
const special_match& entry : special_tag_matches_other) {
1476 for(
const special_match& entry : special_id_matches_other) {
1484 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
1486 if (it == units.
end() || it->incapacitated())
1488 if ( &*it ==
other_.get() )
1491 std::vector<special_match> special_tag_matches_oadj;
1492 std::vector<special_match> special_id_matches_oadj;
1493 get_ability_children(special_tag_matches_oadj, special_id_matches_oadj, it->abilities(), special, special_id , special_tags);
1495 for(
const special_match& entry : special_tag_matches_oadj) {
1503 for(
const special_match& entry : special_id_matches_oadj) {
1519 if(
range().empty()){
1527 const std::string& filter_self)
const
1547 const std::string& tag_name,
1548 const std::string& filter_self)
1550 assert(self_attack || other_attack);
1551 bool is_attacker = self_attack ? self_attack->is_attacker_ : !other_attack->is_attacker_;
1552 bool is_for_listing = self_attack ? self_attack->is_for_listing_ : other_attack->is_for_listing_;
1558 if ( !special_affects_self(special, is_attacker) )
1562 if ( !special_affects_opponent(special, is_attacker) )
1567 const std::string & active_on = special[
"active_on"];
1568 if ( !active_on.empty() ) {
1569 if ( is_attacker && active_on !=
"offense" )
1571 if ( !is_attacker && active_on !=
"defense" )
1576 const unit_map& units = get_unit_map();
1578 unit_const_ptr self = self_attack ? self_attack->self_ : other_attack->other_;
1579 unit_const_ptr other = self_attack ? self_attack->other_ : other_attack->self_;
1580 map_location self_loc = self_attack ? self_attack->self_loc_ : other_attack->other_loc_;
1581 map_location other_loc = self_attack ? self_attack->other_loc_ : other_attack->self_loc_;
1583 if(
self ==
nullptr) {
1589 if(other ==
nullptr) {
1598 temporary_facing other_facing(other, other_loc.
get_relative_dir(self_loc));
1602 bool whom_is_self = ((whom ==
AFFECT_SELF) || ((whom ==
AFFECT_EITHER) && special_affects_self(special, is_attacker)));
1604 map_location their_loc = whom_is_self ? other_loc : self_loc;
1606 if (tag_name ==
"drains" && them && them->get_state(
"undrainable")) {
1609 if (tag_name ==
"plague" && them &&
1610 (them->get_state(
"unplagueable") ||
1614 if (tag_name ==
"poison" && them &&
1618 if (tag_name ==
"slow" && them &&
1622 if (tag_name ==
"petrifies" && them &&
1623 them->get_state(
"unpetrifiable")) {
1631 const map_location & att_loc = is_attacker ? self_loc : other_loc;
1632 const map_location & def_loc = is_attacker ? other_loc : self_loc;
1638 if (tag_name ==
"firststrike") {
1639 bool whom_is_defender = whom_is_self ? !is_attacker : is_attacker;
1640 if (whom_is_defender && att_weapon && att_weapon->has_special_or_ability(
"firststrike"))
1645 if (!special[
"backstab"].
blank()) {
1646 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.");
1649 if(special[
"backstab"].to_bool()){
1650 const std::string& backstab_formula =
"enemy_of(self, flanker) and not flanker.petrified where flanker = unit_at(direction_from(loc, other.facing))";
1652 if(!special.
has_child(
"filter_opponent")){
1653 filter_child[
"formula"] = backstab_formula;
1656 filter[
"formula"] = backstab_formula;
1660 const config& special_backstab = special[
"backstab"].to_bool() ? cfg : special;
1663 if (!special_unit_matches(
self, other, self_loc, self_attack, special, is_for_listing, filter_self))
1665 if (!special_unit_matches(other,
self, other_loc, other_attack, special_backstab, is_for_listing,
"filter_opponent"))
1667 if (!special_unit_matches(att, def, att_loc, att_weapon, special, is_for_listing,
"filter_attacker"))
1669 if (!special_unit_matches(def, att, def_loc, def_weapon, special, is_for_listing,
"filter_defender"))
1677 std::size_t count = 0;
1687 if (
i.has_attribute(
"is_enemy")) {
1695 if (
i[
"count"].empty() && count != dirs.size()) {
1706 std::size_t count = 0;
1713 if(!adj_filter.match(adjacent[
index])) {
1718 if (
i[
"count"].empty() && count != dirs.size()) {
1744 if (
auto apply_filter = cfg.
optional_child(
"filter_base_value")) {
1751 return (cond_eq.
empty() || def == cond_eq.
to_int()) &&
1766 int value_set = is_cumulable ? std::max(list.
highest(
"value").first, 0) + std::min(list.
lowest(
"value").first, 0) : def;
1767 std::map<std::string,individual_effect> values_add;
1768 std::map<std::string,individual_effect> values_mul;
1769 std::map<std::string,individual_effect> values_div;
1775 const config& cfg = *ability.ability_cfg;
1776 const std::string& effect_id = cfg[cfg[
"id"].
empty() ?
"name" :
"id"];
1784 callable.add(
"base_value", wfl::variant(def));
1785 return formula.evaluate(callable).as_int();
1788 int value_cum = cfg[
"cumulative"].to_bool() ? std::max(def, value) : value;
1791 set_effect_min.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
1792 set_effect_max.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
1795 if(value_cum > set_effect_max.
value) {
1796 set_effect_max.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
1798 if(value_cum < set_effect_min.
value) {
1799 set_effect_min.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
1807 callable.add(
"base_value", wfl::variant(def));
1808 return formula.evaluate(callable).as_int();
1811 if(add_effect == values_add.end() || add > add_effect->second.value) {
1812 values_add[effect_id].set(
ADD, add, ability.ability_cfg, ability.teacher_loc);
1817 callable.add(
"base_value", wfl::variant(def));
1818 return formula.evaluate(callable).as_int();
1821 if(sub_effect == values_add.end() || sub < sub_effect->second.value) {
1822 values_add[effect_id].set(
ADD, sub, ability.ability_cfg, ability.teacher_loc);
1827 callable.add(
"base_value", wfl::variant(def));
1828 return formula.evaluate(callable).as_decimal() / 1000.0 ;
1831 if(mul_effect == values_mul.end() || multiply > mul_effect->second.value) {
1832 values_mul[effect_id].set(
MUL, multiply, ability.ability_cfg, ability.teacher_loc);
1837 callable.add(
"base_value", wfl::variant(def));
1838 return formula.evaluate(callable).as_decimal() / 1000.0 ;
1842 ERR_NG <<
"division by zero with divide= in ability/weapon special " << effect_id;
1846 if(div_effect == values_div.end() || divide > div_effect->second.value) {
1847 values_div[effect_id].set(
DIV, divide, ability.ability_cfg, ability.teacher_loc);
1854 value_set = std::max(set_effect_max.
value, 0) + std::min(set_effect_min.
value, 0);
1855 if(set_effect_max.
value > def) {
1858 if(set_effect_min.
value < def) {
1871 double multiplier = 1.0;
1872 double divisor = 1.0;
1874 for(
const auto& val : values_mul) {
1875 multiplier *= val.second.value/100.0;
1879 for(
const auto& val : values_div) {
1880 divisor *= val.second.value/100.0;
1885 for(
const auto& val : values_add) {
1886 addition += val.second.value;
1890 composite_value_ =
static_cast<int>((value_set + addition) * multiplier / divisor);
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 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
unit_ability_list overwrite_special_checking(unit_ability_list input, unit_ability_list overwriters, bool is_special) const
Filter a list of abilities or weapon specials, removing any entries that are overridden by the overwr...
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
Returns list who contains get_weapon_ability and get_specials list for each ability type.
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 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
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()
iterator erase(const iterator &erase_it)
std::pair< int, map_location > lowest(const std::string &key, int def=0) const
std::vector< unit_ability > cfgs_
const map_location & loc() const
std::vector< unit_ability >::iterator iterator
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.
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.
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.
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