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;
1173 std::map<std::string, unsigned int> type_count;
1174 unsigned int max = 0;
1175 for(
auto&
i : damage_type_list) {
1177 if(
c.has_attribute(
"replacement_type")) {
1178 std::string
type =
c[
"replacement_type"].str();
1179 unsigned int count = ++type_count[
type];
1186 if (type_count.empty())
return "";
1188 std::vector<std::string> type_list;
1189 for(
auto&
i : type_count){
1190 if(
i.second == max){
1191 type_list.push_back(
i.first);
1195 if(type_list.empty())
return "";
1197 return type_list.front();
1202 std::map<std::string, int> type_res;
1203 int max_res = std::numeric_limits<int>::min();
1204 for(
auto&
i : damage_type_list) {
1206 if(
c.has_attribute(
"alternative_type")) {
1207 std::string
type =
c[
"alternative_type"].str();
1208 if(type_res.count(
type) == 0){
1210 max_res = std::max(max_res, type_res[
type]);
1215 if (type_res.empty())
return "";
1217 std::vector<std::string> type_list;
1218 for(
auto&
i : type_res){
1219 if(
i.second == max_res){
1220 type_list.push_back(
i.first);
1223 if(type_list.empty())
return "";
1225 return type_list.front();
1230 bool is_alternative = (key_name ==
"alternative_type");
1231 if(is_alternative &&
other_){
1233 }
else if(!is_alternative){
1248 if(damage_type_list.
empty()){
1249 return {
type(),
""};
1254 resistance_list = (*other_).get_abilities_weapons(
"resistance",
other_loc_,
other_attack_, shared_from_this());
1256 std::string replacement_type =
select_damage_type(damage_type_list,
"replacement_type", resistance_list);
1257 std::string alternative_type =
select_damage_type(damage_type_list,
"alternative_type", resistance_list);
1258 std::string type_damage = replacement_type.empty() ?
type() : replacement_type;
1259 if(!alternative_type.empty() && type_damage != alternative_type){
1260 return {type_damage, alternative_type};
1262 return {type_damage,
""};
1268 if(damage_type_list.
empty()){
1271 std::set<std::string> damage_types;
1272 for(
auto&
i : damage_type_list) {
1274 if(
c.has_attribute(
"alternative_type")){
1275 damage_types.insert(
c[
"alternative_type"].str());
1279 return damage_types;
1289 return damage_value;
1301 bool special_affects_opponent(
const config& special,
bool is_attacker)
1304 const std::string& apply_to = special[
"apply_to"];
1305 if ( apply_to.empty() )
1307 if ( apply_to ==
"both" )
1309 if ( apply_to ==
"opponent" )
1311 if ( is_attacker && apply_to ==
"defender" )
1313 if ( !is_attacker && apply_to ==
"attacker" )
1323 bool special_affects_self(
const config& special,
bool is_attacker)
1326 const std::string& apply_to = special[
"apply_to"];
1327 if ( apply_to.empty() )
1329 if ( apply_to ==
"both" )
1331 if ( apply_to ==
"self" )
1333 if ( is_attacker && apply_to ==
"attacker" )
1335 if ( !is_attacker && apply_to ==
"defender")
1357 const bool for_listing,
1358 const std::string & child_tag,
const std::string& tag_name)
1360 if (for_listing && !loc.
valid())
1369 if ( !filter_child )
1384 if (
auto filter_weapon = filter_child->optional_child(
"filter_weapon") ) {
1385 if ( !weapon || !weapon->matches_filter(*filter_weapon, tag_name) )
1392 return ufilt.matches(*u, loc);
1394 return ufilt.matches(*u, loc, *u2);
1412 return special_active(*i.ability_cfg, AFFECT_SELF, ability,
"filter_student");
1418 return special_active_impl(other_attack_, shared_from_this(), *i.ability_cfg, AFFECT_OTHER, ability,
"filter_student");
1433 if(!abil_list.
empty() && !overwriters.
empty()){
1449 const std::string& apply_to = special[
"overwrite_specials"];
1450 return (apply_to ==
"one_side" || apply_to ==
"both_sides");
1461 if(overwriters.
empty()){
1466 if(overwriters.
size() >= 2){
1469 auto oi = (*
i.ability_cfg).optional_child(
"overwrite");
1471 if(oi && !oi[
"priority"].empty()){
1472 l = oi[
"priority"].to_double(0);
1474 auto oj = (*j.
ability_cfg).optional_child(
"overwrite");
1476 if(oj && !oj[
"priority"].empty()){
1477 r = oj[
"priority"].to_double(0);
1491 if(overwriters.
empty()){
1495 for(
const auto& j : overwriters) {
1497 bool affect_side = ((*j.ability_cfg)[
"overwrite_specials"] ==
"one_side");
1499 auto overwrite_specials = (*j.ability_cfg).optional_child(
"overwrite");
1500 double priority = overwrite_specials ? overwrite_specials[
"priority"].to_double(0) : 0.00;
1505 bool prior = (priority > 0) ? (has_overwrite_specials && has_overwrite_specials[
"priority"].to_double(0) >= priority) :
true;
1509 bool one_side_overwritable =
true;
1513 if(affect_side && is_overwritable){
1514 if(special_affects_self(*j.ability_cfg,
is_attacker_)){
1515 one_side_overwritable = special_affects_self(cfg,
is_attacker_);
1517 else if(special_affects_opponent(*j.ability_cfg, !
is_attacker_)){
1518 one_side_overwritable = special_affects_opponent(cfg, !
is_attacker_);
1523 bool special_matches =
true;
1524 if(overwrite_specials){
1525 auto overwrite_filter = (*overwrite_specials).optional_child(
"experimental_filter_specials");
1526 if(overwrite_filter && is_overwritable && one_side_overwritable){
1528 special_matches = (*self_).ability_matches_filter(cfg, tag_name, *overwrite_filter);
1536 if(is_overwritable && one_side_overwritable && special_matches){
1555 std::vector<special_match>& id_result,
1556 const config& parent,
const std::string&
id,
1557 bool special_id=
true,
bool special_tags=
true) {
1558 if(special_id && special_tags){
1559 get_special_children(tag_result, id_result, parent,
id);
1560 }
else if(special_id && !special_tags){
1561 get_special_children_id(id_result, parent,
id);
1562 }
else if(!special_id && special_tags){
1563 get_special_children_tags(tag_result, parent,
id);
1569 return (ability_active(tag_name, special, loc) && ability_affects_self(tag_name, special, loc));
1575 return (affects_side(special, side(), from.
side()) && from.
ability_active(tag_name, special, adjacent[dir]) && ability_affects_adjacent(tag_name, special, dir, loc, from));
1580 return (get_self_ability_bool(special, tag_name, loc) && ability_affects_weapon(special, weapon,
false) && ability_affects_weapon(special, opp_weapon,
true));
1585 return (get_adj_ability_bool(special, tag_name, dir, loc, from) && ability_affects_weapon(special, weapon,
false) && ability_affects_weapon(special, opp_weapon,
true));
1595 if(tag_name ==
"leadership" && leader_bool){
1596 if((*u).get_self_ability_bool_weapon(special, tag_name, loc, self_attack, other_attack)) {
1600 if((*u).checking_tags().count(tag_name) != 0){
1601 if((*u).get_self_ability_bool(special, tag_name, loc) &&
special_active_impl(self_attack, other_attack, special, whom, tag_name,
"filter_student")) {
1615 if(tag_name ==
"leadership" && leader_bool){
1616 if((*u).get_adj_ability_bool_weapon(special, tag_name, dir, loc, from, self_attack, other_attack)) {
1620 if((*u).checking_tags().count(tag_name) != 0){
1621 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")) {
1635 const unit_map& units = get_unit_map();
1637 std::vector<special_match> special_tag_matches_self;
1638 std::vector<special_match> special_id_matches_self;
1639 get_ability_children(special_tag_matches_self, special_id_matches_self, (*self_).abilities(), special, special_id , special_tags);
1641 for(
const special_match& entry : special_tag_matches_self) {
1648 for(
const special_match& entry : special_id_matches_self) {
1656 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
1658 if (it == units.
end() || it->incapacitated())
1660 if ( &*it ==
self_.get() )
1663 std::vector<special_match> special_tag_matches_adj;
1664 std::vector<special_match> special_id_matches_adj;
1665 get_ability_children(special_tag_matches_adj, special_id_matches_adj, it->abilities(), special, special_id , special_tags);
1667 for(
const special_match& entry : special_tag_matches_adj) {
1674 for(
const special_match& entry : special_id_matches_adj) {
1684 std::vector<special_match> special_tag_matches_other;
1685 std::vector<special_match> special_id_matches_other;
1686 get_ability_children(special_tag_matches_other, special_id_matches_other, (*other_).abilities(), special, special_id , special_tags);
1688 for(
const special_match& entry : special_tag_matches_other) {
1696 for(
const special_match& entry : special_id_matches_other) {
1704 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
1706 if (it == units.
end() || it->incapacitated())
1708 if ( &*it ==
other_.get() )
1711 std::vector<special_match> special_tag_matches_oadj;
1712 std::vector<special_match> special_id_matches_oadj;
1713 get_ability_children(special_tag_matches_oadj, special_id_matches_oadj, it->abilities(), special, special_id , special_tags);
1715 for(
const special_match& entry : special_tag_matches_oadj) {
1723 for(
const special_match& entry : special_id_matches_oadj) {
1739 if(
range().empty()){
1747 const std::string& filter_self)
const
1767 const std::string& tag_name,
1768 const std::string& filter_self)
1770 assert(self_attack || other_attack);
1771 bool is_attacker = self_attack ? self_attack->is_attacker_ : !other_attack->is_attacker_;
1772 bool is_for_listing = self_attack ? self_attack->is_for_listing_ : other_attack->is_for_listing_;
1778 if ( !special_affects_self(special, is_attacker) )
1782 if ( !special_affects_opponent(special, is_attacker) )
1787 const std::string & active_on = special[
"active_on"];
1788 if ( !active_on.empty() ) {
1789 if ( is_attacker && active_on !=
"offense" )
1791 if ( !is_attacker && active_on !=
"defense" )
1796 const unit_map& units = get_unit_map();
1798 unit_const_ptr self = self_attack ? self_attack->self_ : other_attack->other_;
1799 unit_const_ptr other = self_attack ? self_attack->other_ : other_attack->self_;
1800 map_location self_loc = self_attack ? self_attack->self_loc_ : other_attack->other_loc_;
1801 map_location other_loc = self_attack ? self_attack->other_loc_ : other_attack->self_loc_;
1803 if(
self ==
nullptr) {
1809 if(other ==
nullptr) {
1818 temporary_facing other_facing(other, other_loc.
get_relative_dir(self_loc));
1822 bool whom_is_self = ((whom ==
AFFECT_SELF) || ((whom ==
AFFECT_EITHER) && special_affects_self(special, is_attacker)));
1824 map_location their_loc = whom_is_self ? other_loc : self_loc;
1826 if (tag_name ==
"drains" && them && them->get_state(
"undrainable")) {
1829 if (tag_name ==
"plague" && them &&
1830 (them->get_state(
"unplagueable") ||
1834 if (tag_name ==
"poison" && them &&
1838 if (tag_name ==
"slow" && them &&
1842 if (tag_name ==
"petrifies" && them &&
1843 them->get_state(
"unpetrifiable")) {
1851 const map_location & att_loc = is_attacker ? self_loc : other_loc;
1852 const map_location & def_loc = is_attacker ? other_loc : self_loc;
1858 if (tag_name ==
"firststrike") {
1859 bool whom_is_defender = whom_is_self ? !is_attacker : is_attacker;
1860 if (whom_is_defender && att_weapon && att_weapon->has_special_or_ability(
"firststrike"))
1865 if (!special[
"backstab"].
blank()) {
1866 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.");
1869 if(special[
"backstab"].to_bool()){
1870 const std::string& backstab_formula =
"enemy_of(self, flanker) and not flanker.petrified where flanker = unit_at(direction_from(loc, other.facing))";
1872 if(!special.
has_child(
"filter_opponent")){
1873 filter_child[
"formula"] = backstab_formula;
1876 filter[
"formula"] = backstab_formula;
1880 const config& special_backstab = special[
"backstab"].to_bool() ? cfg : special;
1886 std::string self_tag_name = whom_is_self ? tag_name :
"";
1887 if (!special_unit_matches(
self, other, self_loc, self_attack, special, is_for_listing, filter_self, self_tag_name))
1889 std::string opp_tag_name = !whom_is_self ? tag_name :
"";
1890 if (!special_unit_matches(other,
self, other_loc, other_attack, special_backstab, is_for_listing,
"filter_opponent", opp_tag_name))
1892 std::string att_tag_name = is_attacker ? tag_name :
"";
1893 if (!special_unit_matches(att, def, att_loc, att_weapon, special, is_for_listing,
"filter_attacker", att_tag_name))
1895 std::string def_tag_name = !is_attacker ? tag_name :
"";
1896 if (!special_unit_matches(def, att, def_loc, def_weapon, special, is_for_listing,
"filter_defender", def_tag_name))
1904 std::size_t count = 0;
1914 if (
i.has_attribute(
"is_enemy")) {
1922 if (
i[
"count"].empty() && count != dirs.size()) {
1933 std::size_t count = 0;
1940 if(!adj_filter.match(adjacent[
index])) {
1945 if (
i[
"count"].empty() && count != dirs.size()) {
1971 if (
auto apply_filter = cfg.
optional_child(
"filter_base_value")) {
1978 return (cond_eq.
empty() || def == cond_eq.
to_int()) &&
1994 std::map<std::string,individual_effect> values_add;
1995 std::map<std::string,individual_effect> values_mul;
1996 std::map<std::string,individual_effect> values_div;
2000 utils::optional<int> max_value = utils::nullopt;
2001 utils::optional<int> min_value = utils::nullopt;
2004 const config& cfg = *ability.ability_cfg;
2005 const std::string& effect_id = cfg[cfg[
"id"].
empty() ?
"name" :
"id"];
2013 callable.add(
"base_value", wfl::variant(def));
2014 return formula.evaluate(callable).as_int();
2017 int value_cum = cfg[
"cumulative"].to_bool() ? std::max(def, value) : value;
2020 set_effect_min.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2021 set_effect_max.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2024 if(value_cum > set_effect_max.
value) {
2025 set_effect_max.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2027 if(value_cum < set_effect_min.
value) {
2028 set_effect_min.
set(
SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2036 max_value = max_value ? std::min(*max_value, cfg[
"max_value"].to_int()) : cfg[
"max_value"].to_int();
2039 min_value = min_value ? std::max(*min_value, cfg[
"min_value"].to_int()) : cfg[
"min_value"].to_int();
2045 callable.add(
"base_value", wfl::variant(def));
2046 return formula.evaluate(callable).as_int();
2049 if(add_effect == values_add.end() || add > add_effect->second.value) {
2050 values_add[effect_id].set(
ADD, add, ability.ability_cfg, ability.teacher_loc);
2055 callable.add(
"base_value", wfl::variant(def));
2056 return formula.evaluate(callable).as_int();
2059 if(sub_effect == values_add.end() || sub < sub_effect->second.value) {
2060 values_add[effect_id].set(
ADD, sub, ability.ability_cfg, ability.teacher_loc);
2065 callable.add(
"base_value", wfl::variant(def));
2066 return formula.evaluate(callable).as_decimal() / 1000.0 ;
2069 if(mul_effect == values_mul.end() || multiply > mul_effect->second.value) {
2070 values_mul[effect_id].set(
MUL, multiply, ability.ability_cfg, ability.teacher_loc);
2075 callable.add(
"base_value", wfl::variant(def));
2076 return formula.evaluate(callable).as_decimal() / 1000.0 ;
2080 ERR_NG <<
"division by zero with divide= in ability/weapon special " << effect_id;
2084 if(div_effect == values_div.end() || divide > div_effect->second.value) {
2085 values_div[effect_id].set(
DIV, divide, ability.ability_cfg, ability.teacher_loc);
2092 value_set = std::max(set_effect_max.
value, 0) + std::min(set_effect_min.
value, 0);
2093 if(set_effect_max.
value > def) {
2096 if(set_effect_min.
value < def) {
2109 double multiplier = 1.0;
2110 double divisor = 1.0;
2112 for(
const auto& val : values_mul) {
2113 multiplier *= val.second.value/100.0;
2117 for(
const auto& val : values_div) {
2118 divisor *= val.second.value/100.0;
2123 for(
const auto& val : values_add) {
2124 addition += val.second.value;
2128 composite_value_ =
static_cast<int>((value_set + addition) * multiplier / divisor);
2130 if(max_value && min_value && *min_value < *max_value) {
2132 }
else if(max_value && !min_value) {
2134 }
else if(min_value && !max_value) {
static std::string select_replacement_type(const unit_ability_list &damage_type_list)
static void add_name(std::string &temp_string, bool active, const std::string name, std::set< std::string > &checking_name)
static std::string select_alternative_type(const unit_ability_list &damage_type_list, unit_ability_list resistance_list, const unit &u)
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
std::set< std::string > alternative_damage_types() const
bool attack_empty() const
Returns true if this is a dummy attack_type, for example the placeholder that the unit_attack dialog ...
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...
std::string select_damage_type(const unit_ability_list &damage_type_list, const std::string &key_name, unit_ability_list resistance_list) const
Select best damage type based on frequency count for replacement_type and based on highest damage for...
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.
bool has_attribute(config_key_type key) const
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)
map_display and display: classes which take care of displaying the map and game-data on the screen.
Interfaces for manipulating version numbers of engine, add-ons, etc.
static std::string _(const char *str)
bool 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.
int resistance_value(unit_ability_list resistance_list, const std::string &damage_name) const
For the provided list of resistance abilities, determine the damage resistance based on which are act...
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