39 #define ERR_CF LOG_STREAM(err, log_config)
40 #define WRN_CF LOG_STREAM(warn, log_config)
41 #define LOG_CONFIG LOG_STREAM(info, log_config)
42 #define DBG_CF LOG_STREAM(debug, log_config)
45 #define DBG_UT LOG_STREAM(debug, log_unit)
46 #define ERR_UT LOG_STREAM(err, log_unit)
49 #define ERR_WML LOG_STREAM(err, log_wml)
53 : tag_(std::move(
tag))
54 , id_(
cfg[
"id"].str())
55 , cfg_(std::move(
cfg))
63 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.");
65 if (
cfg[
"backstab"].to_bool()) {
66 const std::string& backstab_formula =
"enemy_of(self, flanker) and not flanker.petrified where flanker = unit_at(direction_from(loc, other.facing))";
68 config& filter_opponent2 = filter_opponent.
empty() ? filter_opponent : filter_opponent.
add_child(
"and");
69 filter_opponent2[
"formula"] = backstab_formula;
73 std::string filter_teacher = inside_attack ?
"filter_self" :
"filter";
92 if (filter_adjacent[
"count"].empty()) {
101 if (filter_adjacent[
"count"].empty()) {
129 if (p_ab->tag() ==
tag) {
140 res.push_back(std::make_shared<unit_ability_t>(*p_ab));
148 for (
const auto& item : abilities) {
149 item->write(abilities_cfg);
151 return abilities_cfg;
163 , is_attacker_(false)
164 , other_attack_(nullptr)
165 , description_(
cfg[
"description"].t_str())
169 , range_(
cfg[
"range"])
170 , min_range_(
cfg[
"min_range"].to_int(1))
171 , max_range_(
cfg[
"max_range"].to_int(1))
173 , damage_(
cfg[
"damage"].to_int())
174 , num_attacks_(
cfg[
"number"].to_int())
175 , attack_weight_(
cfg[
"attack_weight"].to_double(1.0))
176 , defense_weight_(
cfg[
"defense_weight"].to_double(1.0))
177 , accuracy_(
cfg[
"accuracy"].to_int())
178 , movement_used_(
cfg[
"movement_used"].to_int(100000))
179 , attacks_used_(
cfg[
"attacks_used"].to_int(1))
180 , parry_(
cfg[
"parry"].to_int())
194 icon_ =
"attacks/blank-attack.png";
204 std::ostringstream
s;
238 const std::string& filter_min_range =
filter[
"min_range"];
239 const std::string& filter_max_range =
filter[
"max_range"];
240 const std::string& filter_damage =
filter[
"damage"];
241 const std::string& filter_attacks =
filter[
"number"];
242 const std::string& filter_accuracy =
filter[
"accuracy"];
243 const std::string& filter_parry =
filter[
"parry"];
244 const std::string& filter_movement =
filter[
"movement_used"];
245 const std::string& filter_attacks_used =
filter[
"attacks_used"];
250 const std::string filter_formula =
filter[
"formula"];
258 if ( !filter_range.empty() && filter_range.count(attack.
range()) == 0 )
279 if(!filter_alignment.empty() && filter_alignment.count(attack.
alignment_str()) == 0)
282 if ( !filter_name.empty() && filter_name.count(attack.
id()) == 0)
285 if (!filter_type.empty()){
290 if(check_if_recursion ==
"damage_type"){
291 if (filter_type.count(attack.
type()) == 0){
302 if ( !filter_base_type.empty() && filter_base_type.count(attack.
type()) == 0 )
305 if(
filter.has_attribute(
"special")) {
309 if(
filter.has_attribute(
"special") ||
filter.has_attribute(
"special_id") ||
filter.has_attribute(
"special_type")) {
315 if(
filter.has_attribute(
"special_active")) {
319 if(
filter.has_attribute(
"special_active") ||
filter.has_attribute(
"special_id_active") ||
filter.has_attribute(
"special_type_active")) {
327 if(
auto sub_filter_special =
filter.optional_child(
"filter_special")) {
333 if (!filter_formula.empty()) {
342 lg::log_to_chat() <<
"Formula error in weapon filter: " <<
e.type <<
" at " <<
e.filename <<
':' <<
e.line <<
")\n";
343 ERR_WML <<
"Formula error in weapon filter: " <<
e.type <<
" at " <<
e.filename <<
':' <<
e.line <<
")";
362 for(
const auto [key, condition_cfg] :
filter.all_children_view() )
366 matches = matches &&
matches_filter(condition_cfg, check_if_recursion);
369 else if ( key ==
"or" )
370 matches = matches ||
matches_filter(condition_cfg, check_if_recursion);
373 else if ( key ==
"not" )
374 matches = matches && !
matches_filter(condition_cfg, check_if_recursion);
396 const t_string& set_desc =
cfg[
"set_description"].t_str();
429 if(set_desc.
empty() ==
false) {
449 if(del_specials.
empty() ==
false) {
450 const std::vector<std::string>& dsl =
utils::split(del_specials);
454 new_specials.emplace_back(std::move(p_ab));
461 const std::string &mode = set_specials[
"mode"];
464 "The mode defaults to 'replace', but should often be 'append' instead. The default may change in a future version, or the attribute may become mandatory.");
467 if(mode !=
"append") {
477 for(
const auto [key,
cfg] : set_specials->all_children_view()) {
486 if(increase_min_range.
empty() ==
false) {
494 if(increase_max_range.
empty() ==
false) {
498 if(remove_specials) {
509 if(increase_damage.
empty() ==
false) {
516 if(set_attacks.
empty() ==
false) {
524 if(increase_attacks.
empty() ==
false) {
532 if(increase_accuracy.
empty() ==
false) {
540 if(increase_parry.
empty() ==
false) {
544 if(set_movement.
empty() ==
false) {
548 if(increase_movement.
empty() ==
false) {
556 if(increase_attacks_used.
empty() ==
false) {
589 return {{
"number_or_percent",
utils::print_modifier(attr)}, {
"color", attr.to_int() < 0 ?
"#f00" :
"#0f0"}};
592 std::vector<t_string> desc;
601 if(!increase_min_range.
empty()) {
604 "<span color=\"$color\">$number_or_percent</span> min range",
605 format_modifier(increase_min_range)));
615 if(!increase_max_range.
empty()) {
618 "<span color=\"$color\">$number_or_percent</span> max range",
619 format_modifier(increase_max_range)));
622 if(!increase_damage.
empty()) {
625 "<span color=\"$color\">$number_or_percent</span> damage",
626 "<span color=\"$color\">$number_or_percent</span> damage",
628 format_modifier(increase_damage)));
637 {{
"number", set_damage.str()}}));
640 if(!increase_attacks.empty()) {
643 "<span color=\"$color\">$number_or_percent</span> strike",
644 "<span color=\"$color\">$number_or_percent</span> strikes",
645 increase_attacks.to_int(),
646 format_modifier(increase_attacks)));
649 if(!set_attacks.empty()) {
654 set_attacks.to_int(),
655 {{
"number", set_attacks.str()}}));
658 if(!set_accuracy.empty()) {
662 {{
"number", set_accuracy.str()}}));
665 if(!increase_accuracy.empty()) {
668 "<span color=\"$color\">$number_or_percent|%</span> accuracy",
669 format_modifier(increase_accuracy)));
672 if(!set_parry.empty()) {
676 {{
"number", set_parry.str()}}));
679 if(!increase_parry.empty()) {
682 "<span color=\"$color\">$number_or_percent</span> parry",
683 format_modifier(increase_parry)));
686 if(!set_movement.empty()) {
689 "$number movement point",
690 "$number movement points",
691 set_movement.to_int(),
692 {{
"number", set_movement.str()}}));
695 if(!increase_movement.empty()) {
698 "<span color=\"$color\">$number_or_percent</span> movement point",
699 "<span color=\"$color\">$number_or_percent</span> movement points",
700 increase_movement.to_int(),
701 format_modifier(increase_movement)));
704 if(!set_attacks_used.empty()) {
707 "$number attack used",
708 "$number attacks used",
709 set_attacks_used.to_int(),
710 {{
"number", set_attacks_used.str()}}));
713 if(!increase_attacks_used.empty()) {
716 "<span color=\"$color\">$number_or_percent</span> attack used",
717 "<span color=\"$color\">$number_or_percent</span> attacks used",
718 increase_attacks_used.to_int(),
719 format_modifier(increase_attacks_used)));
736 : parent(weapon.shared_from_this())
738 parent->open_queries_.emplace_back(&special);
746 attack_type::recursion_guard::operator bool()
const {
755 assert(
this != &other);
766 assert(!parent->open_queries_.empty());
767 parent->open_queries_.pop_back();
static lg::log_domain log_unit("unit")
static lg::log_domain log_wml("wml")
static bool matches_simple_filter(const attack_type &attack, const config &filter, const std::string &check_if_recursion)
Returns whether or not *this matches the given filter, ignoring the complexities introduced by [and],...
static lg::log_domain log_config("config")
std::vector< ability_ptr > ability_vector
Helper similar to std::unique_lock for detecting when calculations such as has_special have entered i...
std::shared_ptr< const attack_type > parent
recursion_guard()
Construct an empty instance, only useful for extending the lifetime of a recursion_guard returned fro...
recursion_guard & operator=(recursion_guard &&) noexcept
std::string alignment_str() const
Returns alignment specified by alignment() for filtering when exist.
void set_min_range(int value)
const std::string & range() const
void set_attacks_used(int value)
int movement_used() const
bool has_special_or_ability_with_filter(const config &filter) const
check if special matche
void set_accuracy(int value)
const std::string & type() const
std::string accuracy_parry_tooltip() const
std::string accuracy_parry_description() const
bool special_matches_filter(const unit_ability_t &ab, const config &filter) const
Filter a list of abilities or weapon specials.
void apply_effect(const config &cfg)
Applies effect modifications described by cfg.
bool matches_filter(const config &filter, const std::string &check_if_recursion="") const
Returns whether or not *this matches the given filter.
config specials_cfg() const
void set_defense_weight(double value)
void set_changed(bool value)
recursion_guard update_variables_recursion(const config &special) const
Tests which might otherwise cause infinite recursion should call this, check that the returned object...
bool has_filter_special_or_ability(const config &filter, bool simple_check=false) const
check if special matche
void set_parry(int value)
void set_attack_weight(double value)
void set_damage(int value)
const std::string & id() const
void set_icon(const std::string &value)
std::vector< const config * > open_queries_
While processing a recursive match, all the filters that are currently being checked,...
void remove_special_by_filter(const config &filter)
remove special if matche condition
void set_max_range(int value)
utils::optional< unit_alignments::type > alignment_
attack_type(const config &cfg)
void set_type(const std::string &value)
void write(config &cfg) const
void set_range(const std::string &value)
static std::string describe_effect(const config &cfg)
Generates a description of the effect specified by cfg, if applicable.
void set_attack_alignment(const std::string &value)
void set_name(const t_string &value)
std::pair< std::string, int > effective_damage_type() const
The type of attack used and the resistance value that does the most damage.
Variant for storing WML attributes.
int to_int(int def=0) const
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.
void remove_attribute(std::string_view key)
config & add_child(std::string_view key)
optional_config_impl< config > optional_child(std::string_view key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
config & child_or_add(std::string_view key)
Returns a reference to the first child with the given key.
auto all_children_view() const
In-order iteration over all children.
child_itors child_range(std::string_view key)
const_all_children_itors all_children_range() const
In-order iteration over all children.
bool has_child(std::string_view key) const
Determine whether a config has a child or not.
void remove_children(std::string_view key, const std::function< bool(const config &)> &p={})
Removes all children with tag key for which p returns true.
const std::string & tag() const
static config vector_to_cfg(const ability_vector &abilities)
const config & cfg() const
static void parse_vector(const config &abilities_cfg, ability_vector &res, bool inside_attack)
static ability_vector filter_tag(const ability_vector &vec, const std::string &tag)
unit_ability_t(std::string tag, config cfg, bool inside_attack)
static ability_ptr create(std::string tag, config cfg, bool inside_attack)
static void do_compat_fixes(config &cfg, bool inside_attack)
static ability_vector clone(const ability_vector &vec)
void write(config &abilities_cfg)
static ability_vector cfg_to_vector(const config &abilities_cfg, bool inside_attack)
static config add_registry_entries(const config &base_cfg, const std::string ®istry_name, const std::map< std::string, config > ®istry)
const std::map< std::string, config > & specials() const
Represents version numbers.
bool as_bool() const
Returns a boolean state of the variant value.
void swap(config &lhs, config &rhs) noexcept
Implement non-member swap function for std::swap (calls config::swap).
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)
std::string tooltip
Shown when hovering over an entry in the filter's drop-down list.
Standard logging facilities (interface).
General math utility functions.
bool in_ranges(const Cmp c, const std::vector< std::pair< Cmp, Cmp >> &ranges)
std::stringstream & log_to_chat()
Use this to show WML errors in the ingame chat.
std::string bold(Args &&... data)
Applies bold Pango markup to the input.
std::string tag(std::string_view tag, Args &&... data)
Wraps the given data in the specified tag.
std::string egettext(char const *msgid)
std::set< std::string > split_set(std::string_view s, char sep, const int flags)
std::vector< std::pair< int, int > > parse_ranges_int(const std::string &str)
Handles a comma-separated list of inputs to parse_range.
bool contains(const Container &container, const Value &value)
Returns true iff value is found in container.
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...
int apply_modifier(const int number, const std::string &amount, const int minimum)
std::string format_conjunct_list(const t_string &empty, const std::vector< t_string > &elems)
Format a conjunctive list.
std::map< std::string, t_string > string_map
std::string signed_percent(int val)
Convert into a percentage (using the Unicode "−" and +0% convention.
std::vector< std::string > split(const config_attribute_value &val)
std::string print_modifier(const std::string &mod)
Add a "+" or replace the "-" par Unicode minus.
std::shared_ptr< unit_ability_t > ability_ptr
static std::vector< direction > parse_directions(const std::string &str)
Parse_directions takes a comma-separated list, and filters out any invalid directions.
The base template for associating string values with enum values.
static constexpr utils::optional< enum_type > get_enum(const std::string_view value)
Converts a string into its enum equivalent.
static map_location::direction s
unit_type_data unit_types