add min_value to [resistance] (#8722)

max_value is used to limit the increase in resistance with the "resistance" capability, but there was no equivalent for its reduction.

To be able to add min_value without redoing the "resistance" checking for the umpteenth time, I prefer to modify effect:: so that the checking of these two attributes is done at the same time as the other numerical attributes and keep the door open to a possible generalization of the proceed
This commit is contained in:
newfrenchy83 2024-04-11 04:48:41 +02:00 committed by GitHub
parent f5c8db7b2f
commit 82499d0785
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 62 additions and 19 deletions

View file

@ -80,6 +80,7 @@
{SIMPLE_KEY apply_to string} {SIMPLE_KEY apply_to string}
{SIMPLE_KEY active_on ability_context} {SIMPLE_KEY active_on ability_context}
{SIMPLE_KEY max_value f_int} {SIMPLE_KEY max_value f_int}
{SIMPLE_KEY min_value f_int}
{FILTER_TAG "filter_weapon" weapon ()} {FILTER_TAG "filter_weapon" weapon ()}
{FILTER_TAG "filter_second_weapon" weapon ()} {FILTER_TAG "filter_second_weapon" weapon ()}
[/tag] [/tag]

View file

@ -0,0 +1,31 @@
# wmllint: no translatables
#####
# API(s) being tested: [resistance]min_value=
##
# Actions:
# Give all units resistance to all damage types with a value of 10 but a min_value of 30
# Attack each other
##
# Expected end state:
# The damage from the attack is reduced by 30%
#####
{COMMON_KEEP_A_B_UNIT_TEST "resistance_min_value" (
[event]
name = start
[modify_unit]
[filter]
[/filter]
[effect]
apply_to = new_ability
[abilities]
{TEST_ABILITY resistance 10 (min_value=30) SELF=yes}
[/abilities]
[/effect]
[/modify_unit]
{ATTACK_AND_VALIDATE 70}
{SUCCEED}
[/event]
)}

View file

@ -1575,7 +1575,7 @@ void attack_unit_and_advance(const map_location& attacker,
int under_leadership(const unit &u, const map_location& loc, const_attack_ptr weapon, const_attack_ptr opp_weapon) int under_leadership(const unit &u, const map_location& loc, const_attack_ptr weapon, const_attack_ptr opp_weapon)
{ {
unit_ability_list abil = u.get_abilities_weapons("leadership", loc, weapon, opp_weapon); unit_ability_list abil = u.get_abilities_weapons("leadership", loc, weapon, opp_weapon);
unit_abilities::effect leader_effect(abil, 0, nullptr, true); unit_abilities::effect leader_effect(abil, 0, nullptr, unit_abilities::EFFECT_CUMULABLE);
return leader_effect.get_composite_value(); return leader_effect.get_composite_value();
} }

View file

@ -1919,18 +1919,20 @@ bool filter_base_matches(const config& cfg, int def)
return true; return true;
} }
effect::effect(const unit_ability_list& list, int def, const_attack_ptr att, bool is_cumulable) : effect::effect(const unit_ability_list& list, int def, const_attack_ptr att, EFFECTS wham) :
effect_list_(), effect_list_(),
composite_value_(0) composite_value_(0)
{ {
int value_set = is_cumulable ? std::max(list.highest("value").first, 0) + std::min(list.lowest("value").first, 0) : def; int value_set = (wham == EFFECT_CUMULABLE) ? std::max(list.highest("value").first, 0) + std::min(list.lowest("value").first, 0) : def;
std::map<std::string,individual_effect> values_add; std::map<std::string,individual_effect> values_add;
std::map<std::string,individual_effect> values_mul; std::map<std::string,individual_effect> values_mul;
std::map<std::string,individual_effect> values_div; std::map<std::string,individual_effect> values_div;
individual_effect set_effect_max; individual_effect set_effect_max;
individual_effect set_effect_min; individual_effect set_effect_min;
std::optional<int> max_value = std::nullopt;
std::optional<int> min_value = std::nullopt;
for (const unit_ability & ability : list) { for (const unit_ability & ability : list) {
const config& cfg = *ability.ability_cfg; const config& cfg = *ability.ability_cfg;
@ -1939,7 +1941,7 @@ effect::effect(const unit_ability_list& list, int def, const_attack_ptr att, boo
if (!filter_base_matches(cfg, def)) if (!filter_base_matches(cfg, def))
continue; continue;
if(!is_cumulable){ if(wham != EFFECT_CUMULABLE){
if (const config::attribute_value *v = cfg.get("value")) { if (const config::attribute_value *v = cfg.get("value")) {
int value = get_single_ability_value(*v, def, ability, list.loc(), att, [&](const wfl::formula& formula, wfl::map_formula_callable& callable) { int value = get_single_ability_value(*v, def, ability, list.loc(), att, [&](const wfl::formula& formula, wfl::map_formula_callable& callable) {
callable.add("base_value", wfl::variant(def)); callable.add("base_value", wfl::variant(def));
@ -1963,6 +1965,15 @@ effect::effect(const unit_ability_list& list, int def, const_attack_ptr att, boo
} }
} }
if(wham == EFFECT_CLAMP_MIN_MAX){
if(cfg.has_attribute("max_value")){
max_value = max_value ? std::min(*max_value, cfg["max_value"].to_int()) : cfg["max_value"].to_int();
}
if(cfg.has_attribute("min_value")){
min_value = min_value ? std::max(*min_value, cfg["min_value"].to_int()) : cfg["min_value"].to_int();
}
}
if (const config::attribute_value *v = cfg.get("add")) { if (const config::attribute_value *v = cfg.get("add")) {
int add = get_single_ability_value(*v, def, ability, list.loc(), att, [&](const wfl::formula& formula, wfl::map_formula_callable& callable) { int add = get_single_ability_value(*v, def, ability, list.loc(), att, [&](const wfl::formula& formula, wfl::map_formula_callable& callable) {
callable.add("base_value", wfl::variant(def)); callable.add("base_value", wfl::variant(def));
@ -2011,7 +2022,7 @@ effect::effect(const unit_ability_list& list, int def, const_attack_ptr att, boo
} }
} }
if(!is_cumulable && set_effect_max.type != NOT_USED) { if((wham != EFFECT_CUMULABLE) && set_effect_max.type != NOT_USED) {
value_set = std::max(set_effect_max.value, 0) + std::min(set_effect_min.value, 0); value_set = std::max(set_effect_max.value, 0) + std::min(set_effect_min.value, 0);
if(set_effect_max.value > def) { if(set_effect_max.value > def) {
effect_list_.push_back(set_effect_max); effect_list_.push_back(set_effect_max);
@ -2049,6 +2060,14 @@ effect::effect(const unit_ability_list& list, int def, const_attack_ptr att, boo
} }
composite_value_ = static_cast<int>((value_set + addition) * multiplier / divisor); composite_value_ = static_cast<int>((value_set + addition) * multiplier / divisor);
//clamp what if min_value < max_value or one attribute only used.
if(max_value && min_value && *min_value < *max_value) {
composite_value_ = std::clamp(*min_value, *max_value, composite_value_);
} else if(max_value && !min_value) {
composite_value_ = std::min(*max_value, composite_value_);
} else if(min_value && !max_value) {
composite_value_ = std::max(*min_value, composite_value_);
}
} }
} // end namespace unit_abilities } // end namespace unit_abilities

View file

@ -25,6 +25,8 @@ bool filter_base_matches(const config& cfg, int def);
enum value_modifier {NOT_USED,SET,ADD,MUL,DIV}; enum value_modifier {NOT_USED,SET,ADD,MUL,DIV};
enum EFFECTS { EFFECT_DEFAULT=1, EFFECT_CUMULABLE=2, EFFECT_CLAMP_MIN_MAX=3 };
struct individual_effect struct individual_effect
{ {
individual_effect() : type(NOT_USED), value(0), ability(nullptr), individual_effect() : type(NOT_USED), value(0), ability(nullptr),
@ -39,7 +41,7 @@ struct individual_effect
class effect class effect
{ {
public: public:
effect(const unit_ability_list& list, int def, const_attack_ptr attacker = const_attack_ptr(), bool is_cumulable = false); effect(const unit_ability_list& list, int def, const_attack_ptr attacker = const_attack_ptr(), EFFECTS wham = EFFECT_DEFAULT);
// Provide read-only access to the effect list: // Provide read-only access to the effect list:
typedef std::vector<individual_effect>::const_iterator iterator; typedef std::vector<individual_effect>::const_iterator iterator;
typedef std::vector<individual_effect>::const_iterator const_iterator; typedef std::vector<individual_effect>::const_iterator const_iterator;

View file

@ -1800,20 +1800,9 @@ int unit::resistance_ability(unit_ability_list resistance_abilities, const std::
}); });
if(!resistance_abilities.empty()) { if(!resistance_abilities.empty()) {
unit_abilities::effect resist_effect(resistance_abilities, 100-res); unit_abilities::effect resist_effect(resistance_abilities, 100-res, nullptr, unit_abilities::EFFECT_CLAMP_MIN_MAX);
unit_ability_list resistance_max_value; res = 100 - resist_effect.get_composite_value();
resistance_max_value.append_if(resistance_abilities, [&](const unit_ability& i) {
return (*i.ability_cfg).has_attribute("max_value");
});
if(!resistance_max_value.empty()){
res = 100 - std::min<int>(
resist_effect.get_composite_value(),
resistance_max_value.highest("max_value").first
);
} else {
res = 100 - resist_effect.get_composite_value();
}
} }
return res; return res;

View file

@ -600,6 +600,7 @@
0 resistance_two_cumulative_mixed_same_id 0 resistance_two_cumulative_mixed_same_id
0 resistance_two_cumulative_mixed_unique_id 0 resistance_two_cumulative_mixed_unique_id
0 resistance_max_value 0 resistance_max_value
0 resistance_min_value
0 resistance_negative_max_value 0 resistance_negative_max_value
0 resistance_apply_to_blade 0 resistance_apply_to_blade
0 resistance_apply_to_non_blade 0 resistance_apply_to_non_blade