Skip to content

Commit

Permalink
Fix [damage] weapon special produces unexpected damage values
Browse files Browse the repository at this point in the history
  • Loading branch information
newfrenchy83 committed Nov 19, 2024
1 parent 5d3a840 commit 70d8ed7
Show file tree
Hide file tree
Showing 6 changed files with 31 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -178,9 +178,9 @@
# API(s) being tested: [attacks]divide=
##
# Expected end state:
# Bob's attack has 4 strikes after rounding down.
# Bob's attack has 5 strikes after rounding up.
#####
{ONE_CALCULATION_UNIT_TEST divide_float_2dp 10 (divide=2.02) 4}
{ONE_CALCULATION_UNIT_TEST divide_float_2dp 10 (divide=2.02) 5}
#####
# API(s) being tested: [attacks]divide=
##
Expand All @@ -192,16 +192,16 @@
# API(s) being tested: [attacks]
##
# Expected end state:
# Bob's attack is the expected answer: 10 strikes * 2 / 3 = 6 strikes after rounding down.
# Bob's attack is the expected answer: 10 strikes * 2 / 3 = 7 strikes after rounding up.
#####
{ONE_CALCULATION_UNIT_TEST divide_multiply_combined 10 (divide,multiply=3,2) 6}
{ONE_CALCULATION_UNIT_TEST divide_multiply_combined 10 (divide,multiply=3,2) 7}
#####
# API(s) being tested: [attacks]divide=,[attacks]multiply=
##
# Expected end state:
# Bob's attack is the expected answer: 10 strikes * 2 / 3 = 6 strikes after rounding down.
# Bob's attack is the expected answer: 10 strikes * 2 / 3 = 7 strikes after rounding up.
#####
{TWO_CALCULATION_UNIT_TEST divide_multiply_separated 10 (divide=3) (multiply=2) 6}
{TWO_CALCULATION_UNIT_TEST divide_multiply_separated 10 (divide=3) (multiply=2) 7}
#####
# API(s) being tested: [attacks]multiply=
##
Expand All @@ -213,9 +213,9 @@
# API(s) being tested: [attacks]multiply=
##
# Expected end state:
# Bob's attack has 9 strikes, because 3 * 3.334 first rounds 3.334 to 3.33, and then 3 * 3.33 floors to 9.
# Bob's attack has 10 strikes, because 3 * 3.334 first rounds 3.334 to 3.33, and then 3 * 3.33 round up to 10.
#####
{ONE_CALCULATION_UNIT_TEST multiply_float_3dp 3 (multiply=3.334) 9}
{ONE_CALCULATION_UNIT_TEST multiply_float_3dp 3 (multiply=3.334) 10}

#undef ONE_CALCULATION_UNIT_TEST
#undef TWO_CALCULATION_UNIT_TEST
12 changes: 6 additions & 6 deletions src/actions/attack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ battle_context_unit_stats::battle_context_unit_stats(nonempty_unit_const_ptr up,
chance_to_hit = std::clamp(cth, 0, 100);

// Compute base damage done with the weapon.
int base_damage = weapon->modified_damage();
double base_damage = weapon->modified_damage();

// Get the damage multiplier applied to the base damage of the weapon.
int damage_multiplier = 100;
Expand All @@ -201,8 +201,8 @@ battle_context_unit_stats::battle_context_unit_stats(nonempty_unit_const_ptr up,
damage_multiplier *= opp.damage_from(*weapon, !attacking, opp_loc, opp_weapon);

// Compute both the normal and slowed damage.
damage = round_damage(base_damage, damage_multiplier, 10000);
slow_damage = round_damage(base_damage, damage_multiplier, 20000);
damage = std::round(round_damage(base_damage, damage_multiplier, 10000));
slow_damage = std::round(round_damage(base_damage, damage_multiplier, 20000));

if(is_slowed) {
damage = slow_damage;
Expand Down Expand Up @@ -315,15 +315,15 @@ battle_context_unit_stats::battle_context_unit_stats(const unit_type* u_type,

chance_to_hit = std::clamp(cth, 0, 100);

int base_damage = weapon->modified_damage();
double base_damage = weapon->modified_damage();
int damage_multiplier = 100;
unit_alignments::type alignment = weapon->alignment().value_or(u_type->alignment());
damage_multiplier
+= generic_combat_modifier(lawful_bonus, alignment, u_type->musthave_status("fearless"), 0);
damage_multiplier *= opp_type->resistance_against(weapon->type(), !attacking);

damage = round_damage(base_damage, damage_multiplier, 10000);
slow_damage = round_damage(base_damage, damage_multiplier, 20000);
damage = std::round(round_damage(base_damage, damage_multiplier, 10000));
slow_damage = std::round(round_damage(base_damage, damage_multiplier, 20000));

if(drains) {
// Compute the drain percent (with 50% as the base for backward compatibility)
Expand Down
4 changes: 2 additions & 2 deletions src/reports.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -781,7 +781,7 @@ static int attack_info(const reports::context& rc, const attack_type &at, config
{
auto ctx = at.specials_context(u.shared_from_this(), hex, u.side() == rc.screen().playing_team().side());
int base_damage = at.damage();
int specials_damage = at.modified_damage();
double specials_damage = at.modified_damage();
int damage_multiplier = 100;
const_attack_ptr weapon = at.shared_from_this();
unit_alignments::type attack_alignment = weapon->alignment().value_or(u.alignment());
Expand All @@ -794,7 +794,7 @@ static int attack_info(const reports::context& rc, const attack_type &at, config
bool slowed = u.get_state(unit::STATE_SLOWED);
int damage_divisor = slowed ? 20000 : 10000;
// Assume no specific resistance (i.e. multiply by 100).
damage = round_damage(specials_damage, damage_multiplier * 100, damage_divisor);
damage = std::round(round_damage(specials_damage, damage_multiplier * 100, damage_divisor));

// Hit points are used to calculate swarm, so they need to be bounded.
unsigned max_hp = u.max_hitpoints();
Expand Down
21 changes: 11 additions & 10 deletions src/units/abilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1371,9 +1371,9 @@ std::set<std::string> attack_type::alternative_damage_types() const
/**
* Returns the damage per attack of this weapon, considering specials.
*/
int attack_type::modified_damage() const
double attack_type::modified_damage() const
{
int damage_value = composite_value(get_specials_and_abilities("damage"), damage());
double damage_value = unit_abilities::effect(get_specials_and_abilities("damage"), damage(), shared_from_this()).get_composite_double_value();
return damage_value;
}

Expand Down Expand Up @@ -2438,8 +2438,8 @@ effect::effect(const unit_ability_list& list, int def, const_attack_ptr att, EFF

individual_effect set_effect_max;
individual_effect set_effect_min;
utils::optional<int> max_value = utils::nullopt;
utils::optional<int> min_value = utils::nullopt;
utils::optional<double> max_value = utils::nullopt;
utils::optional<double> min_value = utils::nullopt;

for (const unit_ability & ability : list) {
const config& cfg = *ability.ability_cfg;
Expand Down Expand Up @@ -2474,10 +2474,10 @@ effect::effect(const unit_ability_list& list, int def, const_attack_ptr att, EFF

if(wham == EFFECT_DEFAULT || wham == EFFECT_CUMULABLE){
if(cfg.has_attribute("max_value")){
max_value = max_value ? std::min(*max_value, cfg["max_value"].to_int()) : cfg["max_value"].to_int();
max_value = max_value ? std::min(*max_value, cfg["max_value"].to_double()) : cfg["max_value"].to_double();
}
if(cfg.has_attribute("min_value")){
min_value = min_value ? std::max(*min_value, cfg["min_value"].to_int()) : cfg["min_value"].to_int();
min_value = min_value ? std::max(*min_value, cfg["min_value"].to_double()) : cfg["min_value"].to_double();
}
}

Expand Down Expand Up @@ -2574,15 +2574,16 @@ effect::effect(const unit_ability_list& list, int def, const_attack_ptr att, EFF
effect_list_.push_back(val.second);
}

composite_value_ = static_cast<int>((value_set + addition + substraction) * multiplier / divisor);
composite_double_value_ = (value_set + addition + substraction) * 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_);
composite_double_value_ = std::clamp(*min_value, *max_value, composite_double_value_);
} else if(max_value && !min_value) {
composite_value_ = std::min(*max_value, composite_value_);
composite_double_value_ = std::min(*max_value, composite_double_value_);
} else if(min_value && !max_value) {
composite_value_ = std::max(*min_value, composite_value_);
composite_double_value_ = std::max(*min_value, composite_double_value_);
}
composite_value_ = std::round(composite_double_value_);
}

} // end namespace unit_abilities
3 changes: 3 additions & 0 deletions src/units/abilities.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,16 @@ class effect

int get_composite_value() const
{ return composite_value_; }
double get_composite_double_value() const
{ return composite_double_value_; }
const_iterator begin() const
{ return effect_list_.begin(); }
const_iterator end() const
{ return effect_list_.end(); }
private:
std::vector<individual_effect> effect_list_;
int composite_value_;
double composite_double_value_;
};


Expand Down
2 changes: 1 addition & 1 deletion src/units/attack_type.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ class attack_type : public std::enable_shared_from_this<attack_type>
std::set<std::string> alternative_damage_types() const;

/** Returns the damage per attack of this weapon, considering specials. */
int modified_damage() const;
double modified_damage() const;

/** Return the special weapon value, considering specials.
* @param abil_list The list of special checked.
Expand Down

0 comments on commit 70d8ed7

Please sign in to comment.