Skip to content

Commit

Permalink
Weakpoints (part 2): Let the player know if they hit a weakpoint (#51372
Browse files Browse the repository at this point in the history
)

* Plumb hit source into the weakpoint hit_chance calculation

* Make absorb_hit return the name of the weakpoint, if any

* Add weakpoint printing to ranged attacks

* Add message to non-tech melee attacks

* Run astyle

* style json

* Add messages for weakspots and melee techniques.

* Add translator comments

* Convert string_format to positional and modify translator comments.

* Adjust weak point technique message

* Adjust technique message again.
  • Loading branch information
Joshua-Chin authored Sep 5, 2021
1 parent 1217ecb commit 0138041
Show file tree
Hide file tree
Showing 12 changed files with 71 additions and 32 deletions.
5 changes: 4 additions & 1 deletion data/json/monsters/misc.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@
"melee_cut": 0,
"vision_day": 1,
"armor_bullet": 100,
"weakpoints": [ { "name": "knee", "armor_mult": { "bullet": 0.5 }, "armor_penalty": { "bullet": 25 }, "coverage": 50 } ],
"weakpoints": [
{ "name": "the knee", "armor_mult": { "bullet": 0.5 }, "armor_penalty": { "bullet": 25 }, "coverage": 25 },
{ "name": "the bugs", "armor_mult": { "bullet": 0.0 }, "armor_penalty": { "bullet": 0 }, "coverage": 25 }
],
"harvest": "exempt",
"death_function": { "corpse_type": "NO_CORPSE", "message": "The %s melts away." },
"regenerates": 50,
Expand Down
3 changes: 2 additions & 1 deletion src/character.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6781,7 +6781,7 @@ static void armor_enchantment_adjust( Character &guy, damage_unit &du )
du.amount = std::max( 0.0f, du.amount );
}

void Character::absorb_hit( const bodypart_id &bp, damage_instance &dam )
std::string Character::absorb_hit( Creature *, const bodypart_id &bp, damage_instance &dam )
{
std::list<item> worn_remains;
bool armor_destroyed = false;
Expand Down Expand Up @@ -6886,6 +6886,7 @@ void Character::absorb_hit( const bodypart_id &bp, damage_instance &dam )
if( armor_destroyed ) {
drop_invalid_inventory();
}
return {};
}

bool Character::armor_absorb( damage_unit &du, item &armor, const bodypart_id &bp )
Expand Down
2 changes: 1 addition & 1 deletion src/character.h
Original file line number Diff line number Diff line change
Expand Up @@ -996,7 +996,7 @@ class Character : public Creature, public visitable
*/
void passive_absorb_hit( const bodypart_id &bp, damage_unit &du ) const;
/** Runs through all bionics and armor on a part and reduces damage through their armor_absorb */
void absorb_hit( const bodypart_id &bp, damage_instance &dam ) override;
std::string absorb_hit( Creature *source, const bodypart_id &bp, damage_instance &dam ) override;
/**
* Reduces and mutates du, prints messages about armor taking damage.
* @return true if the armor was completely destroyed (and the item must be deleted).
Expand Down
31 changes: 23 additions & 8 deletions src/creature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -826,6 +826,7 @@ struct projectile_attack_results {
game_message_type gmtSCTcolor = m_neutral;
double damage_mult = 1.0;
bodypart_id bp_hit;
std::string wp_hit;

explicit projectile_attack_results( const projectile &proj ) {
max_damage = proj.impact.total_damage();
Expand Down Expand Up @@ -928,14 +929,25 @@ void Creature::messaging_projectile_attack( const Creature *source,
} else {
SCT.removeCreatureHP();
}

//~ %1$s: creature name, %2$d: damage value
add_msg( m_good, _( "You hit %1$s for %2$d damage." ),
disp_name(), total_damage );
if( hit_selection.wp_hit.empty() ) {
//~ %1$s: creature name, %2$d: damage value
add_msg( m_good, _( "You hit %1$s for %2$d damage." ),
disp_name(), total_damage );
} else {
//~ %1$s: creature name, %2$s: weakpoint hit, %3$d: damage value
add_msg( m_good, _( "You hit %1$s in %2$s for %3$d damage." ),
disp_name(), hit_selection.wp_hit, total_damage );
}
} else if( u_see_this ) {
//~ 1$ - shooter, 2$ - target
add_msg( _( "%1$s shoots %2$s." ),
source->disp_name(), disp_name() );
if( hit_selection.wp_hit.empty() ) {
//~ 1$ - shooter, 2$ - target
add_msg( _( "%1$s shoots %2$s." ),
source->disp_name(), disp_name() );
} else {
//~ 1$ - shooter, 2$ - target, 3$ - weakpoint
add_msg( _( "%1$s shoots %2$s in %3$s." ),
source->disp_name(), disp_name(), hit_selection.wp_hit );
}
}
}
}
Expand Down Expand Up @@ -1026,7 +1038,10 @@ void Creature::deal_projectile_attack( Creature *source, dealt_projectile_attack
}

dealt_dam = deal_damage( source, hit_selection.bp_hit, impact );
// 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.
hit_selection.wp_hit = dealt_dam.wp_hit;

proj.apply_effects_damage( *this, source, dealt_dam, goodhit < accuracy_critical );

Expand All @@ -1050,7 +1065,7 @@ dealt_damage_instance Creature::deal_damage( Creature *source, bodypart_id bp,
damage_instance d = dam; // copy, since we will mutate in absorb_hit

dealt_damage_instance dealt_dams;
absorb_hit( bp, d );
dealt_dams.wp_hit = absorb_hit( source, bp, d );

// Add up all the damage units dealt
for( const auto &it : d.damage_units ) {
Expand Down
4 changes: 2 additions & 2 deletions src/creature.h
Original file line number Diff line number Diff line change
Expand Up @@ -391,8 +391,8 @@ class Creature : public viewer
damage_instance &dam ) = 0;

// handles armor absorption (including clothing damage etc)
// of damage instance. mutates &dam
virtual void absorb_hit( const bodypart_id &bp, damage_instance &dam ) = 0;
// of damage instance. returns name of weakpoint hit, if any. mutates &dam.
virtual std::string absorb_hit( Creature *source, const bodypart_id &bp, damage_instance &dam ) = 0;

// TODO: this is just a shim so knockbacks work
void knock_back_from( const tripoint &p );
Expand Down
1 change: 1 addition & 0 deletions src/damage.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ class damage_over_time_data
struct dealt_damage_instance {
std::array<int, static_cast<int>( damage_type::NUM )> dealt_dams;
bodypart_id bp_hit;
std::string wp_hit;

dealt_damage_instance();
void set_damage( damage_type dt, int amount );
Expand Down
6 changes: 4 additions & 2 deletions src/item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1649,7 +1649,8 @@ double item::effective_dps( const Character &guy, Creature &mon ) const
damage_instance base_damage;
guy.roll_all_damage( crit, base_damage, true, *this );
damage_instance dealt_damage = base_damage;
temp_mon->absorb_hit( bodypart_id( "torso" ), dealt_damage );
// TODO: Modify DPS calculation to consider weakpoints.
temp_mon->absorb_hit( nullptr, bodypart_id( "torso" ), dealt_damage );
dealt_damage_instance dealt_dams;
for( const damage_unit &dmg_unit : dealt_damage.damage_units ) {
int cur_damage = 0;
Expand All @@ -1672,7 +1673,8 @@ double item::effective_dps( const Character &guy, Creature &mon ) const
for( damage_unit &dmg_unit : dealt_rs_damage.damage_units ) {
dmg_unit.damage_multiplier *= 0.66;
}
temp_rs_mon->absorb_hit( bodypart_id( "torso" ), dealt_rs_damage );
// TODO: Modify DPS calculation to consider weakpoints.
temp_rs_mon->absorb_hit( nullptr, bodypart_id( "torso" ), dealt_rs_damage );
dealt_damage_instance rs_dealt_dams;
for( const damage_unit &dmg_unit : dealt_rs_damage.damage_units ) {
int cur_damage = 0;
Expand Down
27 changes: 21 additions & 6 deletions src/melee.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ static const efftype_id effect_amigara( "amigara" );
static const species_id species_HUMAN( "HUMAN" );

static void player_hit_message( Character *attacker, const std::string &message,
Creature &t, int dam, bool crit = false );
Creature &t, int dam, bool crit = false, bool technique = false, std::string wp_hit = {} );
static int stumble( Character &u, const item &weap );
static std::string melee_message( const ma_technique &tec, Character &p,
const dealt_damage_instance &ddi );
Expand Down Expand Up @@ -763,7 +763,8 @@ bool Character::melee_attack_abstract( Creature &t, bool allow_special,
// Treat monster as seen if we see it before or after the attack
if( seen || player_character.sees( t ) ) {
std::string message = melee_message( technique, *this, dealt_dam );
player_hit_message( this, message, t, dam, critical_hit );
player_hit_message( this, message, t, dam, critical_hit, technique.id != tec_none,
dealt_dam.wp_hit );
} else {
add_msg_player_or_npc( m_good, _( "You hit something." ),
_( "<npcname> hits something." ) );
Expand Down Expand Up @@ -2369,20 +2370,27 @@ std::string melee_message( const ma_technique &tec, Character &p, const dealt_da
index = cutting ? 8 : 5;
}

std::string message;
if( dominant_type == damage_type::STAB ) {
return npc ? _( npc_stab[index] ) : _( player_stab[index] );
message = npc ? _( npc_stab[index] ) : _( player_stab[index] );
} else if( dominant_type == damage_type::CUT ) {
return npc ? _( npc_cut[index] ) : _( player_cut[index] );
message = npc ? _( npc_cut[index] ) : _( player_cut[index] );
} else if( dominant_type == damage_type::BASH ) {
return npc ? _( npc_bash[index] ) : _( player_bash[index] );
message = npc ? _( npc_bash[index] ) : _( player_bash[index] );
}
if( ddi.wp_hit.empty() ) {
return message;
} else {
//~ %1$s: "Somone hit something", %2$s: the weakpoint that was hit
return string_format( _( "%1$s in %2$s" ), message, ddi.wp_hit );
}

return _( "The bugs attack %s" );
}

// display the hit message for an attack
void player_hit_message( Character *attacker, const std::string &message,
Creature &t, int dam, bool crit )
Creature &t, int dam, bool crit, bool technique, std::string wp_hit )
{
std::string msg;
game_message_type msgtype = m_good;
Expand All @@ -2404,6 +2412,10 @@ void player_hit_message( Character *attacker, const std::string &message,
if( attacker->is_npc() && !player_character.has_trait( trait_DEBUG_NIGHTVISION ) ) {
//~ NPC hits something (critical)
msg = string_format( _( "%s. Critical!" ), message );
} else if( technique && !wp_hit.empty() ) {
//~ %1$s: "someone hits something", %2$d: damage dealt, %3$s: the weakpoint hit
msg = string_format( _( "%1$s for %2$d damage, and hit it in %3$s. Critical!" ), message, dam,
wp_hit );
} else {
//~ someone hits something for %d damage (critical)
msg = string_format( _( "%s for %d damage. Critical!" ), message, dam );
Expand All @@ -2414,6 +2426,9 @@ void player_hit_message( Character *attacker, const std::string &message,
if( attacker->is_npc() && !player_character.has_trait( trait_DEBUG_NIGHTVISION ) ) {
//~ NPC hits something
msg = string_format( _( "%s." ), message );
} else if( technique && !wp_hit.empty() ) {
//~ %1$s: "someone hits something", %2$d: damage dealt, %3$s: the weakpoint hit
msg = string_format( _( "%1$s for %2$d damage, and hit it in %3$s." ), message, dam, wp_hit );
} else {
//~ someone hits something for %d damage
msg = string_format( _( "%s for %d damage." ), message, dam );
Expand Down
11 changes: 6 additions & 5 deletions src/monster.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1502,22 +1502,23 @@ bool monster::block_hit( Creature *, bodypart_id &, damage_instance & )
return false;
}

void monster::absorb_hit( const bodypart_id &, damage_instance &dam )
std::string monster::absorb_hit( Creature *source, const bodypart_id &, damage_instance &dam )
{
resistances r = resistances( *this );
const weakpoint *weakpoint = type->weakpoints.select_weakpoint();
weakpoint->apply_to( r );
const weakpoint *wp = type->weakpoints.select_weakpoint( source );
wp->apply_to( r );
for( auto &elem : dam.damage_units ) {
add_msg_debug( debugmode::DF_MONSTER, "Dam Type: %s :: Ar Pen: %.1f :: Armor Mult: %.1f",
name_by_dt( elem.type ), elem.res_pen, elem.res_mult );
add_msg_debug( debugmode::DF_MONSTER,
"Weakpoint: %s :: Armor Mult: %.1f :: Armor Penalty: %.1f :: Resist: %.1f",
weakpoint->id, weakpoint->armor_mult[static_cast<int>( elem.type )],
weakpoint->armor_penalty[static_cast<int>( elem.type )],
wp->id, wp->armor_mult[static_cast<int>( elem.type )],
wp->armor_penalty[static_cast<int>( elem.type )],
r.get_effective_resist( elem ) );
elem.amount -= std::min( r.get_effective_resist( elem ) +
get_worn_armor_val( elem.type ), elem.amount );
}
return wp->name;
}

bool monster::melee_attack( Creature &target )
Expand Down
2 changes: 1 addition & 1 deletion src/monster.h
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ class monster : public Creature
void make_bleed( const effect_source &source, const bodypart_id &bp, time_duration duration,
int intensity = 1, bool permanent = false, bool force = false, bool defferred = false ) override;

void absorb_hit( const bodypart_id &bp, damage_instance &dam ) override;
std::string absorb_hit( Creature *source, const bodypart_id &bp, damage_instance &dam ) override;
bool block_hit( Creature *source, bodypart_id &bp_hit, damage_instance &d ) override;
bool melee_attack( Creature &target );
bool melee_attack( Creature &target, float accuracy );
Expand Down
7 changes: 4 additions & 3 deletions src/weakpoint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,17 @@ void weakpoint::apply_to( resistances &resistances ) const
}
}

float weakpoint::hit_chance() const
float weakpoint::hit_chance( Creature * ) const
{
// TODO: scale the hit chance based on the source's skill / stats
return coverage;
}

const weakpoint *weakpoints::select_weakpoint() const
const weakpoint *weakpoints::select_weakpoint( Creature *source ) const
{
float idx = rng_float( 0.0f, 100.0f );
for( const weakpoint &weakpoint : weakpoint_list ) {
float hit_chance = weakpoint.hit_chance( );
float hit_chance = weakpoint.hit_chance( source );
if( idx < hit_chance ) {
return &weakpoint;
}
Expand Down
4 changes: 2 additions & 2 deletions src/weakpoint.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ struct weakpoint {
// Apply the armor multipliers and offsets to a set of resistances.
void apply_to( resistances &resistances ) const;
// Return the change of the creature hitting the weakpoint.
float hit_chance( ) const;
float hit_chance( Creature *source ) const;
void load( const JsonObject &jo );
};

Expand All @@ -39,7 +39,7 @@ struct weakpoints {
weakpoint default_weakpoint;

// Selects a weakpoint to hit.
const weakpoint *select_weakpoint( ) const;
const weakpoint *select_weakpoint( Creature *source ) const;

void clear();
void load( const JsonArray &ja );
Expand Down

0 comments on commit 0138041

Please sign in to comment.