23 #include "formula/callable_objects.hpp"
38 #define ERR_CF LOG_STREAM(err, log_config)
39 #define WRN_CF LOG_STREAM(warn, log_config)
40 #define LOG_CONFIG LOG_STREAM(info, log_config)
41 #define DBG_CF LOG_STREAM(debug, log_config)
44 #define DBG_UT LOG_STREAM(debug, log_unit)
45 #define ERR_UT LOG_STREAM(err, log_unit)
48 #define ERR_WML LOG_STREAM(err, log_wml)
54 , other_attack_(nullptr)
55 , description_(cfg[
"description"].t_str())
59 , range_(cfg[
"range"])
60 , min_range_(cfg[
"min_range"].to_int(1))
61 , max_range_(cfg[
"max_range"].to_int(1))
63 , damage_(cfg[
"damage"].to_int())
64 , num_attacks_(cfg[
"number"].to_int())
65 , attack_weight_(cfg[
"attack_weight"].to_double(1.0))
66 , defense_weight_(cfg[
"defense_weight"].to_double(1.0))
67 , accuracy_(cfg[
"accuracy"].to_int())
68 , movement_used_(cfg[
"movement_used"].to_int(100000))
69 , attacks_used_(cfg[
"attacks_used"].to_int(1))
70 , parry_(cfg[
"parry"].to_int())
71 , specials_(cfg.child_or_empty(
"specials"))
81 icon_ =
"attacks/blank-attack.png";
108 const std::string& filter_min_range =
filter[
"min_range"];
109 const std::string& filter_max_range =
filter[
"max_range"];
110 const std::string& filter_damage =
filter[
"damage"];
111 const std::string& filter_attacks =
filter[
"number"];
112 const std::string& filter_accuracy =
filter[
"accuracy"];
113 const std::string& filter_parry =
filter[
"parry"];
114 const std::string& filter_movement =
filter[
"movement_used"];
115 const std::string& filter_attacks_used =
filter[
"attacks_used"];
120 const std::string filter_formula =
filter[
"formula"];
128 if ( !filter_range.empty() && filter_range.count(attack.
range()) == 0 )
149 if(!filter_alignment.empty() && filter_alignment.count(attack.
alignment_str()) == 0)
152 if ( !filter_name.empty() && filter_name.count(attack.
id()) == 0)
155 if (!filter_type.empty()){
160 if(check_if_recursion ==
"damage_type"){
161 if (filter_type.count(attack.
type()) == 0){
172 if ( !filter_base_type.empty() && filter_base_type.count(attack.
type()) == 0 )
175 if(
filter.has_attribute(
"special")) {
179 if(
filter.has_attribute(
"special") ||
filter.has_attribute(
"special_id") ||
filter.has_attribute(
"special_type")) {
185 if(
filter.has_attribute(
"special_active")) {
189 if(
filter.has_attribute(
"special_active") ||
filter.has_attribute(
"special_id_active") ||
filter.has_attribute(
"special_type_active")) {
197 if(
auto sub_filter_special =
filter.optional_child(
"filter_special")) {
203 if (!filter_formula.empty()) {
212 lg::log_to_chat() <<
"Formula error in weapon filter: " <<
e.type <<
" at " <<
e.filename <<
':' <<
e.line <<
")\n";
213 ERR_WML <<
"Formula error in weapon filter: " <<
e.type <<
" at " <<
e.filename <<
':' <<
e.line <<
")";
232 for(
const auto [key, condition_cfg] :
filter.all_children_view() )
236 matches = matches &&
matches_filter(condition_cfg, check_if_recursion);
239 else if ( key ==
"or" )
240 matches = matches ||
matches_filter(condition_cfg, check_if_recursion);
243 else if ( key ==
"not" )
244 matches = matches && !
matches_filter(condition_cfg, check_if_recursion);
274 const std::string&
set_name = cfg[
"set_name"];
275 const t_string& set_desc = cfg[
"set_description"];
276 const std::string&
set_type = cfg[
"set_type"];
277 const std::string&
set_range = cfg[
"set_range"];
279 const std::string&
set_icon = cfg[
"set_icon"];
280 const std::string& del_specials = cfg[
"remove_specials"];
282 const std::string& increase_min_range = cfg[
"increase_min_range"];
284 const std::string& increase_max_range = cfg[
"increase_max_range"];
287 const std::string& increase_damage = cfg[
"increase_damage"];
288 const std::string&
set_damage = cfg[
"set_damage"];
289 const std::string& increase_attacks = cfg[
"increase_attacks"];
290 const std::string& set_attacks = cfg[
"set_attacks"];
293 const std::string& increase_accuracy = cfg[
"increase_accuracy"];
295 const std::string& increase_parry = cfg[
"increase_parry"];
296 const std::string&
set_parry = cfg[
"set_parry"];
297 const std::string& increase_movement = cfg[
"increase_movement_used"];
298 const std::string& set_movement = cfg[
"set_movement_used"];
299 const std::string& increase_attacks_used = cfg[
"increase_attacks_used"];
308 if(set_desc.
empty() ==
false) {
328 if(del_specials.empty() ==
false) {
329 const std::vector<std::string>& dsl =
utils::split(del_specials);
332 std::vector<std::string>::const_iterator found_id =
333 std::find(dsl.begin(), dsl.end(), cfg[
"id"].str());
334 if (found_id == dsl.end()) {
345 "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.");
348 if(mode !=
"append") {
351 for(
const auto [key, cfg] :
set_specials->all_children_view()) {
360 if(increase_min_range.empty() ==
false) {
368 if(increase_max_range.empty() ==
false) {
372 if(remove_specials) {
383 if(increase_damage.empty() ==
false) {
390 if(set_attacks.empty() ==
false) {
398 if(increase_attacks.empty() ==
false) {
406 if(increase_accuracy.empty() ==
false) {
414 if(increase_parry.empty() ==
false) {
418 if(set_movement.empty() ==
false) {
422 if(increase_movement.empty() ==
false) {
430 if(increase_attacks_used.empty() ==
false) {
462 if(description !=
nullptr) {
463 const std::string& increase_min_range = cfg[
"increase_min_range"];
465 const std::string& increase_max_range = cfg[
"increase_max_range"];
467 const std::string& increase_damage = cfg[
"increase_damage"];
468 const std::string&
set_damage = cfg[
"set_damage"];
469 const std::string& increase_attacks = cfg[
"increase_attacks"];
470 const std::string& set_attacks = cfg[
"set_attacks"];
471 const std::string& increase_accuracy = cfg[
"increase_accuracy"];
473 const std::string& increase_parry = cfg[
"increase_parry"];
474 const std::string&
set_parry = cfg[
"set_parry"];
475 const std::string& increase_movement = cfg[
"increase_movement_used"];
476 const std::string& set_movement = cfg[
"set_movement_used"];
477 const std::string& increase_attacks_used = cfg[
"increase_attacks_used"];
480 std::vector<t_string> desc;
489 if(!increase_min_range.empty()) {
492 "<span color=\"$color\">$number_or_percent</span> min range",
493 {{
"number_or_percent",
utils::print_modifier(increase_min_range)}, {
"color", increase_min_range[0] ==
'-' ?
"#f00" :
"#0f0"}}));
503 if(!increase_max_range.empty()) {
506 "<span color=\"$color\">$number_or_percent</span> max range",
507 {{
"number_or_percent",
utils::print_modifier(increase_max_range)}, {
"color", increase_max_range[0] ==
'-' ?
"#f00" :
"#0f0"}}));
510 if(!increase_damage.empty()) {
513 "<span color=\"$color\">$number_or_percent</span> damage",
514 "<span color=\"$color\">$number_or_percent</span> damage",
516 {{
"number_or_percent",
utils::print_modifier(increase_damage)}, {
"color", increase_damage[0] ==
'-' ?
"#f00" :
"#0f0"}}));
528 if(!increase_attacks.empty()) {
531 "<span color=\"$color\">$number_or_percent</span> strike",
532 "<span color=\"$color\">$number_or_percent</span> strikes",
534 {{
"number_or_percent",
utils::print_modifier(increase_attacks)}, {
"color", increase_attacks[0] ==
'-' ?
"#f00" :
"#0f0"}}));
537 if(!set_attacks.empty()) {
543 {{
"number", set_attacks}}));
553 if(!increase_accuracy.empty()) {
556 "<span color=\"$color\">$number_or_percent|%</span> accuracy",
557 {{
"number_or_percent",
utils::print_modifier(increase_accuracy)}, {
"color", increase_accuracy[0] ==
'-' ?
"#f00" :
"#0f0"}}));
567 if(!increase_parry.empty()) {
570 "<span color=\"$color\">$number_or_percent</span> parry",
571 {{
"number_or_percent",
utils::print_modifier(increase_parry)}, {
"color", increase_parry[0] ==
'-' ?
"#f00" :
"#0f0"}}));
574 if(!set_movement.empty()) {
577 "$number movement point",
578 "$number movement points",
580 {{
"number", set_movement}}));
583 if(!increase_movement.empty()) {
586 "<span color=\"$color\">$number_or_percent</span> movement point",
587 "<span color=\"$color\">$number_or_percent</span> movement points",
589 {{
"number_or_percent",
utils::print_modifier(increase_movement)}, {
"color", increase_movement[0] ==
'-' ?
"#f00" :
"#0f0"}}));
595 "$number attack used",
596 "$number attacks used",
601 if(!increase_attacks_used.empty()) {
604 "<span color=\"$color\">$number_or_percent</span> attack used",
605 "<span color=\"$color\">$number_or_percent</span> attacks used",
607 {{
"number_or_percent",
utils::print_modifier(increase_attacks_used)}, {
"color", increase_attacks_used[0] ==
'-' ?
"#f00" :
"#0f0"}}));
627 : parent(weapon.shared_from_this())
629 parent->open_queries_.emplace_back(&special);
637 attack_type::recursion_guard::operator bool()
const {
646 assert(
this != &other);
657 assert(!parent->open_queries_.empty());
658 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")
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_description() const
bool apply_modification(const config &cfg)
Modifies *this using the specifications in cfg, but only if *this matches cfg viewed as a filter.
bool matches_filter(const config &filter, const std::string &check_if_recursion="") const
Returns whether or not *this matches the given filter.
void set_specials(config value)
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)
bool describe_modification(const config &cfg, std::string *description)
Trimmed down version of apply_modification(), with no modifications actually made.
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,...
bool special_matches_filter(const config &cfg, const std::string &tag_name, const config &filter) const
Filter a list of abilities or weapon specials.
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)
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.
A config object defines a single node in a WML file, with access to child nodes.
all_children_iterator erase(const all_children_iterator &i)
const_all_children_iterator ordered_begin() const
auto all_children_view() const
In-order iteration over all children.
const_all_children_iterator ordered_end() const
optional_config_impl< config > optional_child(config_key_type key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
config & add_child(config_key_type key)
bool as_bool() const
Returns a boolean state of the variant value.
void swap(config &lhs, config &rhs)
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.
New lexcical_cast header.
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 egettext(char const *msgid)
std::set< std::string > split_set(std::string_view s, char sep, const int flags)
int stoi(std::string_view str)
Same interface as std::stoi and meant as a drop in replacement, except:
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::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)
auto * find(Container &container, const Value &value)
Convenience wrapper for using find on a container without needing to comare to end()
std::string print_modifier(const std::string &mod)
Add a "+" or replace the "-" par Unicode minus.
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