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)
68 constexpr
unsigned int ATTACK_RECURSION_LIMIT = 4;
75 other_attack_(nullptr),
76 description_(cfg[
"description"].t_str()),
81 min_range_(cfg[
"min_range"].to_int(1)),
82 max_range_(cfg[
"max_range"].to_int(1)),
84 damage_(cfg[
"damage"]),
85 num_attacks_(cfg[
"number"]),
86 attack_weight_(cfg[
"attack_weight"].to_double(1.0)),
87 defense_weight_(cfg[
"defense_weight"].to_double(1.0)),
88 accuracy_(cfg[
"accuracy"]),
89 movement_used_(cfg[
"movement_used"].to_int(100000)),
90 attacks_used_(cfg[
"attacks_used"].to_int(1)),
92 specials_(cfg.child_or_empty(
"specials")),
102 icon_ =
"attacks/blank-attack.png";
104 if(cfg.
has_attribute(
"alignment") && (cfg[
"alignment"] ==
"neutral" || cfg[
"alignment"] ==
"lawful" || cfg[
"alignment"] ==
"chaotic" || cfg[
"alignment"] ==
"liminal")){
123 std::ostringstream
s;
144 static std::vector<std::tuple<std::string, std::string>> already_shown;
146 auto identifier = std::tuple<std::string, std::string>{attack.
id(), filter.
debug()};
151 std::string_view filter_text_view = std::get<1>(identifier);
153 ERR_UT <<
"Recursion limit reached for weapon '" << attack.
id()
154 <<
"' while checking filter '" << filter_text_view <<
"'";
158 if(already_shown.size() > 100) {
159 already_shown.clear();
161 already_shown.push_back(std::move(identifier));
168 bool matches_simple_filter(
const attack_type& attack,
const config& filter,
const std::string& check_if_recursion)
173 show_recursion_warning(attack, filter);
177 const std::set<std::string> filter_range =
utils::split_set(filter[
"range"].str());
178 const std::string& filter_min_range = filter[
"min_range"];
179 const std::string& filter_max_range = filter[
"max_range"];
180 const std::string& filter_damage = filter[
"damage"];
181 const std::string& filter_attacks = filter[
"number"];
182 const std::string& filter_accuracy = filter[
"accuracy"];
183 const std::string& filter_parry = filter[
"parry"];
184 const std::string& filter_movement = filter[
"movement_used"];
185 const std::string& filter_attacks_used = filter[
"attacks_used"];
186 const std::set<std::string> filter_alignment =
utils::split_set(filter[
"alignment"].str());
187 const std::set<std::string> filter_name =
utils::split_set(filter[
"name"].str());
188 const std::set<std::string> filter_type =
utils::split_set(filter[
"type"].str());
189 const std::vector<std::string> filter_special =
utils::split(filter[
"special"]);
190 const std::vector<std::string> filter_special_id =
utils::split(filter[
"special_id"]);
191 const std::vector<std::string> filter_special_type =
utils::split(filter[
"special_type"]);
192 const std::vector<std::string> filter_special_active =
utils::split(filter[
"special_active"]);
193 const std::vector<std::string> filter_special_id_active =
utils::split(filter[
"special_id_active"]);
194 const std::vector<std::string> filter_special_type_active =
utils::split(filter[
"special_type_active"]);
195 const std::string filter_formula = filter[
"formula"];
203 if ( !filter_range.empty() && filter_range.count(attack.
range()) == 0 )
224 if(!filter_alignment.empty() && filter_alignment.count(attack.
alignment_str()) == 0)
227 if ( !filter_name.empty() && filter_name.count(attack.
id()) == 0)
230 if (!filter_type.empty()){
235 if(check_if_recursion ==
"damage_type"){
236 if (filter_type.count(attack.
type()) == 0){
241 std::pair<std::string, std::string> damage_type = attack.
damage_type();
242 if (filter_type.count(damage_type.first) == 0 && filter_type.count(damage_type.second) == 0){
248 if(!filter_special.empty()) {
251 for(
auto& special : filter_special) {
261 if(!filter_special_id.empty()) {
263 for(
auto& special : filter_special_id) {
264 if(attack.
has_special(special,
true,
true,
false)) {
274 if(!filter_special_active.empty()) {
277 for(
auto& special : filter_special_active) {
287 if(!filter_special_id_active.empty()) {
289 for(
auto& special : filter_special_id_active) {
299 if(!filter_special_type.empty()) {
301 for(
auto& special : filter_special_type) {
311 if(!filter_special_type_active.empty()) {
313 for(
auto& special : filter_special_type_active) {
324 if (!filter_formula.empty()) {
328 if(!form.evaluate(callable).as_bool()) {
332 lg::log_to_chat() <<
"Formula error in weapon filter: " <<
e.type <<
" at " <<
e.filename <<
':' <<
e.line <<
")\n";
333 ERR_WML <<
"Formula error in weapon filter: " <<
e.type <<
" at " <<
e.filename <<
':' <<
e.line <<
")";
350 bool matches = matches_simple_filter(*
this, filter, check_if_recursion);
357 matches = matches &&
matches_filter(condition_cfg, check_if_recursion);
360 else if ( key ==
"or" )
361 matches = matches ||
matches_filter(condition_cfg, check_if_recursion);
364 else if ( key ==
"not" )
365 matches = matches && !
matches_filter(condition_cfg, check_if_recursion);
383 const std::string&
set_name = cfg[
"set_name"];
384 const t_string& set_desc = cfg[
"set_description"];
385 const std::string&
set_type = cfg[
"set_type"];
386 const std::string&
set_range = cfg[
"set_range"];
388 const std::string&
set_icon = cfg[
"set_icon"];
389 const std::string& del_specials = cfg[
"remove_specials"];
391 const std::string& increase_min_range = cfg[
"increase_min_range"];
393 const std::string& increase_max_range = cfg[
"increase_max_range"];
395 const std::string& increase_damage = cfg[
"increase_damage"];
396 const std::string&
set_damage = cfg[
"set_damage"];
397 const std::string& increase_attacks = cfg[
"increase_attacks"];
398 const std::string& set_attacks = cfg[
"set_attacks"];
401 const std::string& increase_accuracy = cfg[
"increase_accuracy"];
403 const std::string& increase_parry = cfg[
"increase_parry"];
404 const std::string&
set_parry = cfg[
"set_parry"];
405 const std::string& increase_movement = cfg[
"increase_movement_used"];
406 const std::string& set_movement = cfg[
"set_movement_used"];
407 const std::string& increase_attacks_used = cfg[
"increase_attacks_used"];
416 if(set_desc.
empty() ==
false) {
436 if(del_specials.empty() ==
false) {
437 const std::vector<std::string>& dsl =
utils::split(del_specials);
440 std::vector<std::string>::const_iterator found_id =
441 std::find(dsl.begin(), dsl.end(), cfg[
"id"].str());
442 if (found_id == dsl.end()) {
453 "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.");
456 if(mode !=
"append") {
459 for(
const auto [key, cfg] :
set_specials->all_children_range()) {
468 if(increase_min_range.empty() ==
false) {
476 if(increase_max_range.empty() ==
false) {
487 if(increase_damage.empty() ==
false) {
494 if(set_attacks.empty() ==
false) {
502 if(increase_attacks.empty() ==
false) {
510 if(increase_accuracy.empty() ==
false) {
518 if(increase_parry.empty() ==
false) {
522 if(set_movement.empty() ==
false) {
526 if(increase_movement.empty() ==
false) {
534 if(increase_attacks_used.empty() ==
false) {
566 if(description !=
nullptr) {
567 const std::string& increase_min_range = cfg[
"increase_min_range"];
569 const std::string& increase_max_range = cfg[
"increase_max_range"];
571 const std::string& increase_damage = cfg[
"increase_damage"];
572 const std::string&
set_damage = cfg[
"set_damage"];
573 const std::string& increase_attacks = cfg[
"increase_attacks"];
574 const std::string& set_attacks = cfg[
"set_attacks"];
575 const std::string& increase_accuracy = cfg[
"increase_accuracy"];
577 const std::string& increase_parry = cfg[
"increase_parry"];
578 const std::string&
set_parry = cfg[
"set_parry"];
579 const std::string& increase_movement = cfg[
"increase_movement_used"];
580 const std::string& set_movement = cfg[
"set_movement_used"];
581 const std::string& increase_attacks_used = cfg[
"increase_attacks_used"];
584 std::vector<t_string> desc;
593 if(!increase_min_range.empty()) {
596 "<span color=\"$color\">$number_or_percent</span> min range",
597 {{
"number_or_percent",
utils::print_modifier(increase_min_range)}, {
"color", increase_min_range[0] ==
'-' ?
"#f00" :
"#0f0"}}));
607 if(!increase_max_range.empty()) {
610 "<span color=\"$color\">$number_or_percent</span> max range",
611 {{
"number_or_percent",
utils::print_modifier(increase_max_range)}, {
"color", increase_max_range[0] ==
'-' ?
"#f00" :
"#0f0"}}));
614 if(!increase_damage.empty()) {
617 "<span color=\"$color\">$number_or_percent</span> damage",
618 "<span color=\"$color\">$number_or_percent</span> damage",
619 std::stoi(increase_damage),
620 {{
"number_or_percent",
utils::print_modifier(increase_damage)}, {
"color", increase_damage[0] ==
'-' ?
"#f00" :
"#0f0"}}));
632 if(!increase_attacks.empty()) {
635 "<span color=\"$color\">$number_or_percent</span> strike",
636 "<span color=\"$color\">$number_or_percent</span> strikes",
637 std::stoi(increase_attacks),
638 {{
"number_or_percent",
utils::print_modifier(increase_attacks)}, {
"color", increase_attacks[0] ==
'-' ?
"#f00" :
"#0f0"}}));
641 if(!set_attacks.empty()) {
646 std::stoi(set_attacks),
647 {{
"number", set_attacks}}));
657 if(!increase_accuracy.empty()) {
660 "<span color=\"$color\">$number_or_percent|%</span> accuracy",
661 {{
"number_or_percent",
utils::print_modifier(increase_accuracy)}, {
"color", increase_accuracy[0] ==
'-' ?
"#f00" :
"#0f0"}}));
671 if(!increase_parry.empty()) {
674 "<span color=\"$color\">$number_or_percent</span> parry",
675 {{
"number_or_percent",
utils::print_modifier(increase_parry)}, {
"color", increase_parry[0] ==
'-' ?
"#f00" :
"#0f0"}}));
678 if(!set_movement.empty()) {
681 "$number movement point",
682 "$number movement points",
683 std::stoi(set_movement),
684 {{
"number", set_movement}}));
687 if(!increase_movement.empty()) {
690 "<span color=\"$color\">$number_or_percent</span> movement point",
691 "<span color=\"$color\">$number_or_percent</span> movement points",
692 std::stoi(increase_movement),
693 {{
"number_or_percent",
utils::print_modifier(increase_movement)}, {
"color", increase_movement[0] ==
'-' ?
"#f00" :
"#0f0"}}));
699 "$number attack used",
700 "$number attacks used",
705 if(!increase_attacks_used.empty()) {
708 "<span color=\"$color\">$number_or_percent</span> attack used",
709 "<span color=\"$color\">$number_or_percent</span> attacks used",
710 std::stoi(increase_attacks_used),
711 {{
"number_or_percent",
utils::print_modifier(increase_attacks_used)}, {
"color", increase_attacks_used[0] ==
'-' ?
"#f00" :
"#0f0"}}));
731 : parent(weapon.shared_from_this())
741 attack_type::recursion_guard::operator bool()
const {
750 assert(
this != &other);
759 assert(parent->num_recursion_ > 0);
760 parent->num_recursion_--;
static lg::log_domain log_unit("unit")
static lg::log_domain log_wml("wml")
static lg::log_domain log_config("config")
Helper similar to std::unique_lock for detecting when calculations such as has_special have entered i...
recursion_guard & operator=(recursion_guard &&)
recursion_guard()
Construct an empty instance, only useful for extending the lifetime of a recursion_guard returned fro...
std::string alignment_str() const
Returns alignment specified by alignment() for filtering.
void set_min_range(int value)
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.
recursion_guard update_variables_recursion() const
Tests which might otherwise cause infinite recursion should call this, check that the returned object...
const std::string & range() const
void set_attacks_used(int value)
int movement_used() const
void set_accuracy(int value)
const std::string & type() const
std::string accuracy_parry_description() const
unit_alignments::type alignment() const
Returns alignment specified by alignment_str_ variable If empty or not valid returns the unit's align...
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.
std::string alignment_str_
void set_specials(config value)
void set_defense_weight(double value)
void set_changed(bool value)
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)
unsigned int num_recursion_
Number of instances of recursion_guard that are currently allocated permission to recurse.
void set_max_range(int value)
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)
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
void set_name(const t_string &value)
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.
A config object defines a single node in a WML file, with access to child nodes.
bool has_attribute(config_key_type key) const
const_all_children_itors all_children_range() const
In-order iteration over all children.
std::string debug() 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)
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)
void trim(std::string_view &s)
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::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.
static std::string get_string(enum_type key)
Converts a enum to its string equivalent.
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