35 #include "formula/callable_objects.hpp" 41 #include <boost/dynamic_bitset.hpp> 42 #include <boost/algorithm/string/predicate.hpp> 45 #define ERR_NG LOG_STREAM(err, log_engine) 48 class temporary_facing
58 u_->set_facing(new_dir);
64 u_->set_facing(save_dir_);
129 bool affects_side(
const config& cfg, std::size_t side, std::size_t other_side)
134 if(side == other_side)
135 return cfg[
"affect_allies"].to_bool(
true);
137 return cfg[
"affect_enemies"].to_bool();
139 return cfg[
"affect_allies"].to_bool();
146 for (
const config &
i : this->abilities_.child_range(tag_name)) {
147 if (ability_active(tag_name,
i, loc) &&
148 ability_affects_self(tag_name,
i, loc))
159 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
161 if (it == units.
end() || it->incapacitated())
170 for (
const config &j : it->abilities_.child_range(tag_name)) {
171 if (affects_side(j, side(), it->side()) &&
172 it->ability_active(tag_name, j, adjacent[i]) &&
173 ability_affects_adjacent(tag_name, j, i, loc, *it))
188 for(
const config&
i : this->abilities_.child_range(tag_name)) {
189 if(ability_active(tag_name,
i, loc)
190 && ability_affects_self(tag_name,
i, loc)
191 && ability_affects_weapon(
i, weapon,
false)
192 && ability_affects_weapon(
i, opp_weapon,
true)
203 for(
unsigned i = 0;
i < adjacent.size(); ++
i) {
205 if (it == units.
end() || it->incapacitated())
214 for(
const config& j : it->abilities_.child_range(tag_name)) {
215 if(affects_side(j, side(), it->side())
216 && it->ability_active(tag_name, j, adjacent[i])
217 && ability_affects_adjacent(tag_name, j, i, loc, *it) && ability_affects_weapon(j, weapon,
false)
218 && ability_affects_weapon(j, opp_weapon,
true)
231 std::vector<std::string> res;
234 std::string
id = ab.cfg[
"id"];
236 res.push_back(std::move(
id));
251 const config & cfg,
const std::string & key,
const std::string & default_key)
254 return !value.
blank() ? value : cfg[default_key];
263 const std::string & female_key,
const std::string & default_key)
265 return default_value(cfg,
279 const t_string& name = gender_value(ab.
cfg, gender,
"name",
"female_name",
"name").t_str();
284 ab.
cfg[
"name"].t_str(),
286 ab.
cfg[
"description"].t_str() );
294 gender_value(ab.
cfg, gender,
"name_inactive",
295 "female_name_inactive",
"name_inactive");
297 gender_value(ab.
cfg, gender,
"name",
"female_name",
"name").t_str();
302 default_value(ab.
cfg,
"name_inactive",
"name").t_str(),
304 default_value(ab.
cfg,
"description_inactive",
"description").t_str() );
315 std::vector<std::tuple<std::string, t_string,t_string,t_string>> res;
319 add_ability_tooltip(ab, gender_, res,
true);
327 std::vector<std::tuple<std::string, t_string,t_string,t_string>> res;
332 bool active = ability_active(ab.key, ab.cfg, loc);
333 if (add_ability_tooltip(ab, gender_, res, active))
335 active_list.push_back(active);
343 bool illuminates = ability ==
"illuminates";
357 std::size_t count = 0;
359 ufilt.set_use_flat_tod(illuminates);
366 if (unit == units.
end())
368 if (!ufilt(*unit, *
this))
370 if (
i.has_attribute(
"is_enemy")) {
372 if (
i[
"is_enemy"].to_bool() != dc.
get_team(unit->side()).is_enemy(side_)) {
378 if (
i[
"count"].empty() && count != dirs.size()) {
388 std::size_t count = 0;
390 adj_filter.flatten(illuminates);
398 if(!adj_filter.match(adjacent[
index])) {
403 if (
i[
"count"].empty() && count != dirs.size()) {
415 bool illuminates = ability ==
"illuminates";
417 assert(dir >=0 && dir <= 5);
422 if (
i.has_attribute(
"adjacent")) {
424 if (
std::find(dirs.begin(), dirs.end(), direction) == dirs.end()) {
428 const config &filter =
i.child(
"filter");
430 unit_filter(
vconfig(filter)).set_use_flat_tod(illuminates).matches(*
this, loc, from) ) {
440 bool affect_self = cfg[
"affect_self"].to_bool(
true);
441 if (!filter || !affect_self)
return affect_self;
447 const std::string filter_tag_name = is_opp ?
"filter_second_weapon" :
"filter_weapon";
455 return weapon->matches_filter(filter);
460 return !abilities_.child_range(ability).empty();
466 template<
typename T,
typename TFuncFormula>
467 class get_ability_value_visitor :
public boost::static_visitor<T>
471 get_ability_value_visitor(T def,
const TFuncFormula& formula_handler) : def_(def), formula_handler_(formula_handler) {}
473 T operator()(
const boost::blank&)
const {
return def_; }
474 T operator()(
bool)
const {
return def_; }
475 T operator()(
int i)
const {
return static_cast<T
>(
i); }
476 T operator()(
unsigned long long u)
const {
return static_cast<T
>(u); }
477 T operator()(
double d)
const {
return static_cast<T
>(
d); }
478 T operator()(
const t_string&)
const {
return def_; }
479 T operator()(
const std::string&
s)
const 481 if(s.size() >= 2 && s[0] ==
'(') {
482 return formula_handler_(s);
484 return lexical_cast_default<T>(
s, def_);
489 const TFuncFormula& formula_handler_;
491 template<
typename T,
typename TFuncFormula>
492 get_ability_value_visitor<T, TFuncFormula> make_get_ability_value_visitor(T def,
const TFuncFormula& formula_handler)
494 return get_ability_value_visitor<T, TFuncFormula>(def, formula_handler);
496 template<
typename T,
typename TFuncFormula>
499 return v.
apply_visitor(make_get_ability_value_visitor(def, [&](
const std::string&
s) {
505 auto u_itor = units.
find(sender_loc);
507 if(u_itor == units.
end()) {
511 u_itor = units.
find(receiver_loc);
512 if(u_itor != units.
end()) {
513 callable.
add(
"other",
wfl::variant(std::make_shared<wfl::unit_callable>(*u_itor)));
524 template<
typename TComp>
527 if ( cfgs_.empty() ) {
533 bool only_cumulative =
true;
543 if ((*
p.first)[
"cumulative"].to_bool()) {
545 if (value < 0) value = -value;
546 if (only_cumulative && !comp(value, abs_max)) {
550 }
else if (only_cumulative || comp(flat, value)) {
551 only_cumulative =
false;
556 return std::make_pair(flat + stack, best_loc);
559 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;
560 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;
594 std::string tag_name;
610 bool get_special_children(std::vector<special_match>& tag_result,
611 std::vector<special_match>& id_result,
612 const config& parent,
const std::string&
id,
613 bool just_peeking=
false) {
616 if (just_peeking && (
sp.key ==
id ||
sp.cfg[
"id"] ==
id)) {
621 special_match special = {
sp.key, &
sp.cfg };
622 tag_result.push_back(special);
624 if(
sp.cfg[
"id"] ==
id) {
625 special_match special = {
sp.key, &
sp.cfg };
626 id_result.push_back(special);
632 bool get_special_children_id(std::vector<special_match>& id_result,
633 const config& parent,
const std::string&
id,
634 bool just_peeking=
false) {
637 if (just_peeking && (
sp.cfg[
"id"] ==
id)) {
641 if(
sp.cfg[
"id"] ==
id) {
642 special_match special = {
sp.key, &
sp.cfg };
643 id_result.push_back(special);
649 bool get_special_children_tags(std::vector<special_match>& tag_result,
650 const config& parent,
const std::string&
id,
651 bool just_peeking=
false) {
654 if (just_peeking && (
sp.key ==
id)) {
659 special_match special = {
sp.key, &
sp.cfg };
660 tag_result.push_back(special);
677 std::vector<special_match> special_tag_matches;
678 std::vector<special_match> special_id_matches;
679 if(special_id && special_tags){
680 if ( get_special_children(special_tag_matches, special_id_matches, specials_, special, simple_check) ) {
683 }
else if(special_id && !special_tags){
684 if ( get_special_children_id(special_id_matches, specials_, special, simple_check) ) {
687 }
else if(!special_id && special_tags){
688 if ( get_special_children_tags(special_tag_matches, specials_, special, simple_check) ) {
696 for(
const special_match& entry : special_tag_matches) {
697 if ( special_active(*entry.cfg, AFFECT_SELF, entry.tag_name) ) {
703 for(
const special_match& entry : special_id_matches) {
704 if ( special_active(*entry.cfg, AFFECT_SELF, entry.tag_name) ) {
712 if ( simple_check || !other_attack_ ) {
716 std::vector<special_match> special_tag_matches;
717 std::vector<special_match> special_id_matches;
718 if(special_id && special_tags){
719 get_special_children(special_tag_matches, special_id_matches, other_attack_->specials_, special);
720 }
else if(special_id && !special_tags){
721 get_special_children_id(special_id_matches, other_attack_->specials_, special);
722 }
else if(!special_id && special_tags){
723 get_special_children_tags(special_tag_matches, other_attack_->specials_, special);
726 for(
const special_match& entry : special_tag_matches) {
727 if ( other_attack_->special_active(*entry.cfg, AFFECT_OTHER, entry.tag_name) ) {
733 for(
const special_match& entry : special_id_matches) {
734 if ( other_attack_->special_active(*entry.cfg, AFFECT_OTHER, entry.tag_name) ) {
751 for(
const config&
i : specials_.child_range(special)) {
752 if(special_active(
i, AFFECT_SELF, special)) {
761 for(
const config&
i : other_attack_->specials_.child_range(special)) {
762 if(other_attack_->special_active(
i, AFFECT_OTHER, special)) {
779 boost::dynamic_bitset<>* active_list)
const 782 std::vector<std::pair<t_string, t_string>> res;
784 active_list->clear();
788 if ( !active_list || special_active(
sp.cfg, AFFECT_EITHER,
sp.key) ) {
791 res.emplace_back(name,
sp.cfg[
"description"].t_str() );
793 active_list->push_back(
true);
796 const t_string& name = default_value(
sp.cfg,
"name_inactive",
"name").t_str();
798 res.emplace_back(name, default_value(
sp.cfg,
"description_inactive",
"description").t_str() );
799 active_list->push_back(
false);
820 const bool active = special_active(
sp.cfg, AFFECT_EITHER,
sp.key, is_backstab);
822 const std::string& name =
824 ?
sp.cfg[
"name"].str()
825 : default_value(
sp.cfg,
"name_inactive",
"name").str();
827 if (!res.empty()) res +=
", ";
830 if (only_active && !active) res +=
"</span>";
855 : parent(weapon.shared_from_this())
874 :
parent(weapon.shared_from_this())
893 :
parent(weapon.shared_from_this())
896 weapon.
self_ =
nullptr;
906 :
parent(weapon.shared_from_this())
919 parent->is_attacker_ =
false;
920 parent->other_attack_ =
nullptr;
921 parent->is_for_listing_ =
false;
927 other.was_moved =
true;
938 unsigned & max_attacks)
const 944 if (
combat_ability(
"attacks", attacks_value, is_backstab).second ) {
945 attacks_value =
combat_ability(
"attacks", attacks_value, is_backstab).first;
948 if ( attacks_value < 0 ) {
950 ERR_NG <<
"negative number of strikes after applying weapon specials" << std::endl;
955 if ( !swarm_specials.
empty() ) {
956 min_attacks = std::max<int>(0, swarm_specials.
highest(
"swarm_attacks_min").first);
957 max_attacks = std::max<int>(0, swarm_specials.
highest(
"swarm_attacks_max", attacks_value).first);
959 min_attacks = max_attacks = attacks_value;
971 if (
combat_ability(
"damage", damage_value, is_backstab).second ) {
972 damage_value =
combat_ability(
"damage", damage_value, is_backstab).first;
986 bool special_affects_opponent(
const config& special,
bool is_attacker)
989 const std::string& apply_to = special[
"apply_to"];
990 if ( apply_to.empty() )
992 if ( apply_to ==
"both" )
994 if ( apply_to ==
"opponent" )
996 if ( is_attacker && apply_to ==
"defender" )
998 if ( !is_attacker && apply_to ==
"attacker" )
1008 bool special_affects_self(
const config& special,
bool is_attacker)
1011 const std::string& apply_to = special[
"apply_to"];
1012 if ( apply_to.empty() )
1014 if ( apply_to ==
"both" )
1016 if ( apply_to ==
"self" )
1018 if ( is_attacker && apply_to ==
"attacker" )
1020 if ( !is_attacker && apply_to ==
"defender")
1040 const bool for_listing,
1041 const std::string & child_tag)
1043 if (for_listing && !loc.
valid())
1051 const config & filter_child = filter.
child(child_tag);
1052 if ( !filter_child )
1065 return ufilt.matches(*u, loc);
1069 if (!ufilt.matches(*u, loc, *u2)) {
1074 if (
const config & filter_weapon = filter_child.
child(
"filter_weapon") ) {
1075 if ( !weapon || !weapon->matches_filter(filter_weapon) )
1096 bool include_backstab)
const 1101 if ( !include_backstab )
1102 if ( special[
"backstab"].to_bool() )
1111 if ( !special_affects_opponent(special,
is_attacker_) )
1116 const std::string & active_on = special[
"active_on"];
1117 if ( !active_on.empty() ) {
1131 if(
self ==
nullptr) {
1137 if(other ==
nullptr) {
1149 if (tag_name ==
"drains" && other && other->get_state(
"undrainable")) {
1152 if (tag_name ==
"plague" && other &&
1153 (other->get_state(
"unplagueable") ||
1157 if (tag_name ==
"poison" && other &&
1180 if (!special_unit_matches(att, def, att_loc, att_weapon, special,
is_for_listing_,
"filter_attacker"))
1182 if (!special_unit_matches(def, att, def_loc, def_weapon, special,
is_for_listing_,
"filter_defender"))
1191 std::size_t count = 0;
1199 if (unit == units.
end() || !filter.matches(*unit, adjacent[index], *
self))
1201 if (
i.has_attribute(
"is_enemy")) {
1203 if (
i[
"is_enemy"].to_bool() != dc.
get_team(unit->side()).is_enemy(self->side())) {
1209 if (
i[
"count"].empty() && count != dirs.size()) {
1220 std::size_t count = 0;
1227 if(!adj_filter.match(adjacent[
index])) {
1232 if (
i[
"count"].empty() && count != dirs.size()) {
1258 if (
const config &apply_filter = cfg.
child(
"filter_base_value")) {
1265 return (cond_eq.
empty() || def == cond_eq.
to_int()) &&
1280 int value_set = def;
1281 std::map<std::string,individual_effect> values_add;
1282 std::map<std::string,individual_effect> values_mul;
1283 std::map<std::string,individual_effect> values_div;
1289 const config& cfg = *ability.first;
1290 const std::string& effect_id = cfg[cfg[
"id"].
empty() ?
"name" :
"id"];
1292 if (!cfg[
"backstab"].blank()) {
1296 if (!backstab && cfg[
"backstab"].to_bool())
1307 int value_cum = cfg[
"cumulative"].to_bool() ? std::max(def, value) : value;
1310 set_effect_min.
set(
SET, value_cum, ability.first, ability.second);
1311 set_effect_max.
set(
SET, value_cum, ability.first, ability.second);
1314 if(value_cum > set_effect_max.
value) {
1315 set_effect_max.
set(
SET, value_cum, ability.first, ability.second);
1317 if(value_cum < set_effect_min.
value) {
1318 set_effect_min.
set(
SET, value_cum, ability.first, ability.second);
1329 if(add_effect == values_add.end() || add > add_effect->second.value) {
1330 values_add[effect_id].set(
ADD, add, ability.first, ability.second);
1339 if(sub_effect == values_add.end() || sub < sub_effect->second.value) {
1340 values_add[effect_id].set(
ADD, sub, ability.first, ability.second);
1344 int multiply =
static_cast<int>(get_single_ability_value(*v, static_cast<double>(def), ability.second, list.loc(),[&](
const wfl::formula& formula,
wfl::map_formula_callable& callable) {
1349 if(mul_effect == values_mul.end() || multiply > mul_effect->second.value) {
1350 values_mul[effect_id].set(
MUL, multiply, ability.first, ability.second);
1354 int divide =
static_cast<int>(get_single_ability_value(*v, static_cast<double>(def), ability.second, list.loc(),[&](
const wfl::formula& formula,
wfl::map_formula_callable& callable) {
1360 ERR_NG <<
"division by zero with divide= in ability/weapon special " << effect_id << std::endl;
1364 if(div_effect == values_div.end() || divide > div_effect->second.value) {
1365 values_div[effect_id].set(
DIV, divide, ability.first, ability.second);
1372 value_set = std::max(set_effect_max.
value, 0) + std::min(set_effect_min.
value, 0);
1373 if(set_effect_max.
value > def) {
1376 if(set_effect_min.
value < def) {
1389 double multiplier = 1.0;
1390 double divisor = 1.0;
1392 for(
const auto& val : values_mul) {
1393 multiplier *= val.second.value/100.0;
1397 for(
const auto& val : values_div) {
1398 divisor *= val.second.value/100.0;
1403 for(
const auto& val : values_add) {
1404 addition += val.second.value;
1408 composite_value_ =
static_cast<int>((value_set + addition) * multiplier / divisor);
boost::intrusive_ptr< const unit > unit_const_ptr
bool empty() const
Tests for an attribute that either was never set or was set to "".
std::string weapon_specials(bool only_active=false, bool is_backstab=false) const
Returns a comma-separated string of active names for the specials of *this.
bool ability_affects_weapon(const config &cfg, const_attack_ptr weapon, bool is_opp) const
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
void get_adjacent_tiles(const map_location &a, map_location *res)
Function which, given a location, will place all adjacent locations in res.
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
This class represents a single unit of a specific type.
const color_t inactive_details_color
unit_filter & set_use_flat_tod(bool value)
void emplace_back(T &&... args)
Variant for storing WML attributes.
New lexcical_cast header.
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)
virtual const gamemap & map() const override
void modified_attacks(bool is_backstab, unsigned &min_attacks, unsigned &max_attacks) const
Calculates the number of attacks this weapon has, considering specials.
The unit is poisoned - it loses health each turn.
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.
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.
std::pair< int, bool > combat_ability(const std::string &ability, int abil_value=0, bool backstab_pos=false) const
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.
bool filter_base_matches(const config &cfg, int def)
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).
int as_decimal() const
Returns variant's internal representation of decimal number: ie, 1.234 is represented as 1234...
std::vector< std::pair< int, int > > parse_ranges(const std::string &str)
std::string deprecated_message(const std::string &elem_name, DEP_LEVEL level, const version_info &version, const std::string &detail)
std::pair< const config *, map_location > unit_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.
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
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.
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
std::array< map_location, 6 > adjacent_loc_array_t
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...
Encapsulates the map of the game.
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.
pointer get_shared_ptr() const
This is exactly the same as operator-> but it's slightly more readable, and can replace &*iter syntax...
bool special_active(const config &special, AFFECTS whom, const std::string &tag_name, bool include_backstab=true) const
Returns whether or not the given special is active for the specified unit, based on the current conte...
std::stringstream & wml_error()
Use this logger to send errors due to deprecated WML.
static map_location::DIRECTION s
int modified_damage(bool is_backstab) const
Returns the damage per attack of this weapon, considering specials.
const display_context & get_disp_context() const
int get_composite_value() const
DIRECTION
Valid directions which can be moved in our hexagonal world.
unit_ability_list get_abilities(const std::string &tag_name, const map_location &loc, const_attack_ptr weapon=nullptr, const_attack_ptr opp_weapon=nullptr) const
Gets the unit's active abilities of a particular type if it were on a specified location.
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.
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 find(E event, F functor)
Tests whether an event handler is available.
const_attack_ptr other_attack_
A variable-expanding proxy for the config class.
Standard logging facilities (interface).
V::result_type apply_visitor(const V &visitor) const
Applies a visitor to the underlying variant.
static const map_location & null_location()
bool get_special_bool(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.
Container associating units to locations.
std::vector< std::string > get_ability_list() const
Get a list of all abilities by ID.
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.
std::shared_ptr< const attack_type > const_attack_ptr
std::string::const_iterator iterator
static lg::log_domain log_engine("engine")
std::pair< int, map_location > highest(const std::string &key, int def=0) const