Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Weakpoints (Part 4): Add support for damage and crit multipliers #51770

Merged
merged 1 commit into from
Sep 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion doc/MONSTERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -328,8 +328,10 @@ Weakpoints in the monster's protection.
| --- | ---
| `name` | Name of the weakpoint.
| `coverage` | Base percentage chance of hitting the weakpoint. May be increased by skill level. (e.g. A coverage of 5 means a 5% base chance of hitting the weakpoint)
| `armor_multiplier` | multipler on the monster's base protection when hitting the weakpoint.
| `armor_mult` | multipler on the monster's base protection when hitting the weakpoint.
| `armor_penalty` | a flat penalty to the monster's protection, applied after the multiplier.
| `damage_mult` | multipler on the post-armor damage when hitting the weakpoint.
| `crit_mult` | multipler on the post-armor damage when critically hitting the weakpoint.

## "vision_day", "vision_night"
(integer, optional)
Expand Down
8 changes: 7 additions & 1 deletion src/creature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -850,6 +850,7 @@ struct projectile_attack_results {
double damage_mult = 1.0;
bodypart_id bp_hit;
std::string wp_hit;
bool is_crit = false;

explicit projectile_attack_results( const projectile &proj ) {
max_damage = proj.impact.total_damage();
Expand Down Expand Up @@ -889,12 +890,14 @@ projectile_attack_results Creature::select_body_part_projectile_attack(
ret.damage_mult *= rng_float( 0.95, 1.05 );
ret.damage_mult *= crit_multiplier;
ret.bp_hit = bodypart_id( "head" ); // headshot hits the head, of course
ret.is_crit = true;
} else if( goodhit < accuracy_critical &&
ret.max_damage * crit_multiplier > get_hp_max( bodypart_id( "torso" ) ) ) {
ret.message = _( "Critical!" );
ret.gmtSCTcolor = m_critical;
ret.damage_mult *= rng_float( 0.75, 1.0 );
ret.damage_mult *= crit_multiplier;
ret.is_crit = true;
} else if( goodhit < accuracy_goodhit ) {
ret.message = _( "Good hit!" );
ret.gmtSCTcolor = m_good;
Expand Down Expand Up @@ -1038,6 +1041,9 @@ void Creature::deal_projectile_attack( Creature *source, dealt_projectile_attack

projectile_attack_results hit_selection = select_body_part_projectile_attack( proj, goodhit,
missed_by );
// Create a copy that records whether the attack is a crit.
weakpoint_attack wp_attack_copy = wp_attack;
wp_attack_copy.is_crit = hit_selection.is_crit;

if( print_messages && source != nullptr && !hit_selection.message.empty() && u_see_this ) {
source->add_msg_if_player( m_good, hit_selection.message );
Expand All @@ -1060,7 +1066,7 @@ void Creature::deal_projectile_attack( Creature *source, dealt_projectile_attack
}
}

dealt_dam = deal_damage( source, hit_selection.bp_hit, impact, wp_attack );
dealt_dam = deal_damage( source, hit_selection.bp_hit, impact, wp_attack_copy );
// Force damage instance to match the selected body point
dealt_dam.bp_hit = hit_selection.bp_hit;
// Retrieve the selected weakpoint from the damage instance.
Expand Down
1 change: 1 addition & 0 deletions src/melee.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -710,6 +710,7 @@ bool Character::melee_attack_abstract( Creature &t, bool allow_special,
attack.source = this;
attack.weapon = cur_weapon;
attack.is_melee = true;
attack.is_crit = critical_hit;
attack.wp_skill = melee_weakpoint_skill( *cur_weapon );
t.deal_melee_hit( this, hit_spread, critical_hit, d, dealt_dam, attack );
if( dealt_special_dam.type_damage( damage_type::CUT ) > 0 ||
Expand Down
1 change: 1 addition & 0 deletions src/monster.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1535,6 +1535,7 @@ std::string monster::absorb_hit( const weakpoint_attack &attack, const bodypart_
elem.amount -= std::min( r.get_effective_resist( elem ) +
get_worn_armor_val( elem.type ), elem.amount );
}
wp->apply_to( dam, attack.is_crit );
return wp->name;
}

Expand Down
22 changes: 22 additions & 0 deletions src/weakpoint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ weakpoint_attack::weakpoint_attack() :
source( nullptr ),
weapon( &null_item_reference() ),
is_melee( false ),
is_crit( false ),
wp_skill( 0.0f ) {}


Expand All @@ -63,6 +64,8 @@ weakpoint::weakpoint()
// arrays must be filled manually to avoid UB.
armor_mult.fill( 1.0f );
armor_penalty.fill( 0.0f );
damage_mult.fill( 1.0f );
crit_mult.fill( 1.0f );
}

void weakpoint::load( const JsonObject &jo )
Expand All @@ -76,6 +79,17 @@ void weakpoint::load( const JsonObject &jo )
if( jo.has_object( "armor_penalty" ) ) {
armor_penalty = load_damage_array( jo.get_object( "armor_penalty" ), 0.0f );
}
if( jo.has_object( "damage_mult" ) ) {
damage_mult = load_damage_array( jo.get_object( "damage_mult" ), 1.0f );
}
if( jo.has_object( "crit_mult" ) ) {
crit_mult = load_damage_array( jo.get_object( "crit_mult" ), 1.0f );
} else {
// Default to damage multiplier, if crit multipler is not specified.
crit_mult = damage_mult;
}


// Set the ID to the name, if not provided.
if( id.empty() ) {
id = name;
Expand All @@ -90,6 +104,14 @@ void weakpoint::apply_to( resistances &resistances ) const
}
}

void weakpoint::apply_to( damage_instance &damage, bool is_crit ) const
{
for( auto &elem : damage.damage_units ) {
int idx = static_cast<int>( elem.type );
elem.damage_multiplier *= is_crit ? crit_mult[idx] : damage_mult[idx];
}
}

float weakpoint::hit_chance( const weakpoint_attack & ) const
{
// TODO: scale the hit chance based on the source's skill / stats
Expand Down
11 changes: 11 additions & 0 deletions src/weakpoint.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ struct weakpoint_attack {
// Weather the attack is a melee attack.
bool is_melee;

// Whether the attack a critical hit.
bool is_crit;

// The Creature's skill in hitting weak points.
float wp_skill;

Expand All @@ -41,10 +44,18 @@ struct weakpoint {
std::array<float, static_cast<int>( damage_type::NUM )> armor_mult;
// Flat penalty to armor values. Applied after the multiplier.
std::array<float, static_cast<int>( damage_type::NUM )> armor_penalty;
// Damage multipliers. Applied after armor.
std::array<float, static_cast<int>( damage_type::NUM )> damage_mult;
// Critical damage multiplers. Applied after armor instead of damage_mult, if the attack is a crit.
std::array<float, static_cast<int>( damage_type::NUM )>crit_mult;
// Difficulty to hit the weak point.
float difficulty = -10.0f;

weakpoint();
// Apply the armor multipliers and offsets to a set of resistances.
void apply_to( resistances &resistances ) const;
// Apply the damage multiplers to a set of damage values.
void apply_to( damage_instance &damage, bool is_crit ) const;
// Return the change of the creature hitting the weakpoint.
float hit_chance( const weakpoint_attack &attack ) const;
void load( const JsonObject &jo );
Expand Down