diff --git a/src/damage.cpp b/src/damage.cpp index aa8a75250abfb..7f077ad55e267 100644 --- a/src/damage.cpp +++ b/src/damage.cpp @@ -98,7 +98,7 @@ resistances::resistances() resist_vals.fill( 0 ); } -resistances::resistances( item &armor, bool to_self ) +resistances::resistances( const item &armor, bool to_self ) { // Armors protect, but all items can resist if( to_self || armor.is_armor() ) { diff --git a/src/damage.h b/src/damage.h index 5b9b2a284c785..41c0233701559 100644 --- a/src/damage.h +++ b/src/damage.h @@ -34,7 +34,7 @@ struct damage_unit { float res_mult; float damage_multiplier; - damage_unit( damage_type dt, float a, int rp, float rm, float mul ) : + damage_unit( damage_type dt, float a, int rp = 0, float rm = 1.0f, float mul = 1.0f ) : type( dt ), amount( a ), res_pen( rp ), res_mult( rm ), damage_multiplier( mul ) { } }; @@ -69,7 +69,7 @@ struct resistances { resistances(); // If to_self is true, we want armor's own resistance, not one it provides to wearer - resistances( item &armor, bool to_self = false ); + resistances( const item &armor, bool to_self = false ); resistances( monster &monster ); void set_resist( damage_type dt, float amount ); float type_resist( damage_type dt ) const; diff --git a/src/field.cpp b/src/field.cpp index 64779f276c31c..49365c3f0ca9d 100644 --- a/src/field.cpp +++ b/src/field.cpp @@ -88,7 +88,7 @@ void game::init_fields() { "fd_web", {_("cobwebs"),_("webs"), _("thick webs")}, '}', 2, - {c_white, c_white, c_white}, {true, true, false},{false, false, false}, 0, + {c_white, c_white, c_white}, {true, true, false},{true, true, true}, 0, {0,0,0}, SOLID }, @@ -120,7 +120,7 @@ void game::init_fields() { "fd_sludge", {_("thin sludge trail"), _("sludge trail"), _("thick sludge trail")}, '5', 2, - {c_ltgray, c_dkgray, c_black}, {true, true, true}, {false, false, false}, HOURS(6), + {c_ltgray, c_dkgray, c_black}, {true, true, true}, {true, true, true}, HOURS(6), {0,0,0}, LIQUID }, @@ -151,7 +151,7 @@ void game::init_fields() { "fd_toxic_gas", {_("hazy cloud"),_("toxic gas"),_("thick toxic gas")}, '8', 8, - {c_white, c_ltgreen, c_green}, {true, false, false},{false, true, true}, MINUTES(90), + {c_white, c_ltgreen, c_green}, {true, false, false},{true, true, true}, MINUTES(90), {0,0,0}, GAS }, @@ -207,7 +207,7 @@ void game::init_fields() { "fd_fatigue", {_("odd ripple"), _("swirling air"), _("tear in reality")}, '*', 8, - {c_ltgray, c_dkgray, c_magenta},{true, true, false},{false, false, false}, 0, + {c_ltgray, c_dkgray, c_magenta},{true, true, false},{true, true, true}, 0, {0,0,0}, PNULL }, @@ -348,7 +348,7 @@ void game::init_fields() { "fd_relax_gas", {_("hazy cloud"),_("sedative gas"),_("relaxation gas")}, '.', 8, - { c_white, c_pink, c_cyan }, { true, true, true }, { false, true, true }, MINUTES(50), + { c_white, c_pink, c_cyan }, { true, true, true }, { true, true, true }, MINUTES(50), {0,0,0}, GAS }, @@ -396,7 +396,7 @@ void game::init_fields() { "fd_fungicidal_gas", {_("hazy cloud"),_("fungicidal gas"),_("thick fungicidal gas")}, '8', 8, - {c_white, c_ltgray, c_dkgray}, {true, true, false}, {false, true, true}, MINUTES(90), + {c_white, c_ltgray, c_dkgray}, {true, true, false}, {true, true, true}, MINUTES(90), {0,0,0}, GAS } diff --git a/src/game.cpp b/src/game.cpp index 27f177346f200..af8ec83895d5c 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -12024,6 +12024,24 @@ bool game::walk_move( const tripoint &dest_loc ) !query_yn( _("Really step onto that %s?"), tr.name.c_str() ) ) { return true; } + + if( m.has_flag( "ROUGH", dest_loc ) && !m.has_flag( "ROUGH", u.pos() ) && !boardable && + ( u.get_armor_bash( bp_foot_l ) < 5 || u.get_armor_bash( bp_foot_r ) < 5 ) && + !query_yn( _( "Really step onto that %s?" ), m.name( dest_loc ).c_str() ) ) { + return true; + } else if( m.has_flag( "SHARP", dest_loc ) && !m.has_flag( "SHARP", u.pos() ) && !boardable && + u.dex_cur < 78 ) { + static const std::set< body_part > check = { + bp_eyes, bp_mouth, bp_head, bp_leg_l, bp_leg_r, bp_foot_l, bp_foot_r, bp_arm_l, bp_arm_r, + bp_hand_l, bp_hand_r, bp_torso + }; + if( !std::all_of( check.begin(), check.end(), [this]( body_part bp ) { + return u.immune_to( bp, { DT_CUT, 10 } ); + } ) && !query_yn( _( "Really step onto that %s?" ), m.name( dest_loc ).c_str() ) ) { + return true; + } + } + } } diff --git a/src/item.cpp b/src/item.cpp index 92645f20fd4fc..f9b5bb1362d92 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -2,6 +2,7 @@ #include "advanced_inv.h" #include "player.h" +#include "damage.h" #include "output.h" #include "skill.h" #include "bionics.h" @@ -3168,6 +3169,14 @@ int item::chip_resistance( bool worst ) const return res; } +void item::mitigate_damage( damage_unit &du ) const +{ + const resistances res = resistances( *this ); + const float mitigation = res.get_effective_resist( du ); + du.amount -= mitigation; + du.amount = std::max( 0.0f, du.amount ); +} + int item::damage_resist( damage_type dt, bool to_self ) const { switch( dt ) { diff --git a/src/item.h b/src/item.h index 7d4eb56a380c3..a434a2b58b304 100644 --- a/src/item.h +++ b/src/item.h @@ -16,6 +16,7 @@ #include "string_id.h" #include "line.h" #include "item_location.h" +#include "damage.h" #include "debug.h" class game; @@ -652,6 +653,11 @@ class item : public JsonSerializer, public JsonDeserializer, public visitable= 5; } +float player::bionic_armor_bonus( body_part bp, damage_type dt ) const +{ + float result = 0.0f; + // We only check the passive bionics + if( has_bionic( "bio_carbon" ) ) { + if( dt == DT_BASH ) { + result += 2; + } else if( dt == DT_CUT ) { + result += 4; + } else if( dt == DT_STAB ) { + result += 3.2; + } + } + //all the other bionic armors reduce bash/cut/stab by 3/3/2.4 + // Map body parts to a set of bionics that protect it + // @todo: JSONize passive bionic armor instead of hardcoding it + static const std::map< body_part, std::string > armor_bionics = { + { bp_head, { "bio_armor_head" } }, + { bp_arm_l, { "bio_armor_arms" } }, + { bp_arm_r, { "bio_armor_arms" } }, + { bp_torso, { "bio_armor_torso" } }, + { bp_leg_l, { "bio_armor_legs" } }, + { bp_leg_r, { "bio_armor_legs" } }, + { bp_eyes, { "bio_armor_eyes" } } + }; + auto iter = armor_bionics.find( bp ); + if( iter != armor_bionics.end() ) { + if( has_bionic( iter->second ) ) { + if( dt == DT_BASH || dt == DT_CUT ) { + result += 3; + } else if( dt == DT_STAB ) { + result += 2.4; + } + } + } + return result; +} + +void player::passive_absorb_hit( body_part bp, damage_unit &du ) const +{ + du.amount -= bionic_armor_bonus( bp, du.type ); //Check for passive armor bionics + // >0 check because some mutations provide negative armor + if( du.amount > 0.0f ) { + // Horrible hack warning! + // Get rid of this as soon as CUT and STAB are split + if( du.type == DT_STAB ) { + damage_unit du_copy = du; + du_copy.type = DT_CUT; + du.amount -= 0.8f * mutation_armor( bp, du_copy ); + } else { + du.amount -= mutation_armor( bp, du ); + } + } + du.amount -= mabuff_armor_bonus( du.type ); + du.amount = std::max( 0.0f, du.amount ); +} + void player::absorb_hit(body_part bp, damage_instance &dam) { std::list worn_remains; bool armor_destroyed = false; @@ -12260,7 +12332,7 @@ void player::absorb_hit(body_part bp, damage_instance &dam) { continue; } - // CBMs absorb damage first before hitting armor + // The bio_ads CBM absorbs damage before hitting armor if( has_active_bionic("bio_ads") ) { if( elem.amount > 0 && power_level > 24 ) { if( elem.type == DT_BASH ) { @@ -12327,96 +12399,7 @@ void player::absorb_hit(body_part bp, damage_instance &dam) { } } - // Next, apply reductions from bionics and traits. - if( has_bionic("bio_carbon") ) { - switch (elem.type) { - case DT_BASH: - elem.amount -= 2; - break; - case DT_CUT: - elem.amount -= 4; - break; - case DT_STAB: - elem.amount -= 3.2; - break; - default: - break; - } - } - if( bp == bp_head && has_bionic("bio_armor_head") ) { - switch (elem.type) { - case DT_BASH: - case DT_CUT: - elem.amount -= 3; - break; - case DT_STAB: - elem.amount -= 2.4; - break; - default: - break; - } - } else if( (bp == bp_arm_l || bp == bp_arm_r) && has_bionic("bio_armor_arms") ) { - switch (elem.type) { - case DT_BASH: - case DT_CUT: - elem.amount -= 3; - break; - case DT_STAB: - elem.amount -= 2.4; - break; - default: - break; - } - } else if( bp == bp_torso && has_bionic("bio_armor_torso") ) { - switch (elem.type) { - case DT_BASH: - case DT_CUT: - elem.amount -= 3; - break; - case DT_STAB: - elem.amount -= 2.4; - break; - default: - break; - } - } else if( (bp == bp_leg_l || bp == bp_leg_r) && has_bionic("bio_armor_legs") ) { - switch (elem.type) { - case DT_BASH: - case DT_CUT: - elem.amount -= 3; - break; - case DT_STAB: - elem.amount -= 2.4; - break; - default: - break; - } - } else if( bp == bp_eyes && has_bionic("bio_armor_eyes") ) { - switch (elem.type) { - case DT_BASH: - case DT_CUT: - elem.amount -= 3; - break; - case DT_STAB: - elem.amount -= 2.4; - break; - default: - break; - } - } - - // >0 check because some mutations provide negative armor - if( elem.amount > 0.0f ) { - // Horrible hack warning! - // Get rid of this as soon as CUT and STAB are split - if( elem.type == DT_STAB ) { - damage_unit elem_copy = elem; - elem_copy.type = DT_CUT; - elem.amount -= 0.8f * mutation_armor( bp, elem_copy ); - } else { - elem.amount -= mutation_armor( bp, elem ); - } - } + passive_absorb_hit( bp, elem ); if( elem.type == DT_BASH ) { if( has_trait( "LIGHT_BONES" ) ) { @@ -12427,8 +12410,6 @@ void player::absorb_hit(body_part bp, damage_instance &dam) { } } - elem.amount -= mabuff_armor_bonus( elem.type ); - elem.amount = std::max( elem.amount, 0.0f ); } for( item& remain : worn_remains ) { diff --git a/src/player.h b/src/player.h index a033bbbd664bc..35994a5562ac1 100644 --- a/src/player.h +++ b/src/player.h @@ -514,6 +514,18 @@ class player : public Character, public JsonSerializer, public JsonDeserializer * @return true if the armor was completely destroyed (and the item must be deleted). */ bool armor_absorb( damage_unit &du, item &armor ); + /** + * Check for passive bionics that provide armor, and returns the armor bonus + * This is called from player::passive_absorb_hit + */ + float bionic_armor_bonus( body_part bp, damage_type dt ) const; + /** + * Check for relevant passive, non-clothing that can absorb damage, and reduce @ref du + * Only flat bonuses are checked here. Multiplicative ones are checked in player::absorb_hit + * @ref du.amount will never be reduced below 0 + * This is called from @ref player::absorb_hit + */ + void passive_absorb_hit( body_part bp, damage_unit &du ) const; /** Runs through all bionics and armor on a part and reduces damage through their armor_absorb */ void absorb_hit(body_part bp, damage_instance &dam) override; /** Called after the player has successfully dodged an attack */ @@ -625,6 +637,11 @@ class player : public Character, public JsonSerializer, public JsonDeserializer /** Returns a value used when attempting to intimidate NPC's */ int intimidation() const; + /** + * Returns true if it is impossible for @ref dam to reduce the player's HP on his/her @ref bp + * @warning Only HP is accounted for- not damaged clothing, pain, status effects, etc. + */ + bool immune_to( body_part bp, damage_unit dam ) const; /** Calls Creature::deal_damage and handles damaged effects (waking up, etc.) */ dealt_damage_instance deal_damage(Creature *source, body_part bp, const damage_instance &d) override; /** Actually hurt the player, hurts a body_part directly, no armor reduction */