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

Fix displaying of stats for ablative armor #78590

Merged
merged 1 commit into from
Dec 16, 2024
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
2 changes: 1 addition & 1 deletion src/armor_layers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ std::vector<std::string> clothing_properties(
add_folded_name_and_value( props, _( "Encumbrance:" ), string_format( "%3d", encumbrance ),
width );
add_folded_name_and_value( props, _( "Warmth:" ), string_format( "%3d",
worn_item.get_warmth() ), width );
worn_item.get_warmth( used_bp ) ), width );
return props;
}

Expand Down
196 changes: 162 additions & 34 deletions src/item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -955,7 +955,7 @@ bool item::is_frozen_liquid() const
return made_of( phase_id::SOLID ) && made_of_from_type( phase_id::LIQUID );
}

bool item::covers( const sub_bodypart_id &bp ) const
bool item::covers( const sub_bodypart_id &bp, bool check_ablative_armor ) const
{
// if the item has no armor data it doesn't cover that part
const islot_armor *armor = find_armor_data();
Expand All @@ -982,18 +982,18 @@ bool item::covers( const sub_bodypart_id &bp ) const

iterate_covered_sub_body_parts_internal( get_side(), [&]( const sub_bodypart_str_id & covered ) {
does_cover = does_cover || bp == covered;
} );
}, check_ablative_armor );

return does_cover || subpart_cover;
}

bool item::covers( const bodypart_id &bp ) const
bool item::covers( const bodypart_id &bp, bool check_ablative_armor ) const
{
bool does_cover = false;
bool subpart_cover = false;
iterate_covered_body_parts_internal( get_side(), [&]( const bodypart_str_id & covered ) {
does_cover = does_cover || bp == covered;
} );
}, check_ablative_armor );

return does_cover || subpart_cover;
}
Expand Down Expand Up @@ -1111,12 +1111,14 @@ static void iterate_helper_sbp( const item *i, const side s,
}

void item::iterate_covered_sub_body_parts_internal( const side s,
const std::function<void( const sub_bodypart_str_id & )> &cb ) const
const std::function<void( const sub_bodypart_str_id & )> &cb,
bool check_ablative_armor ) const

{
iterate_helper_sbp( this, s, cb );

//check for ablative armor too
if( is_ablative() ) {
if( check_ablative_armor && is_ablative() ) {
for( const item_pocket *pocket : get_all_ablative_pockets() ) {
if( !pocket->empty() ) {
// get the contained plate
Expand Down Expand Up @@ -1159,12 +1161,13 @@ static void iterate_helper( const item *i, const side s,
}

void item::iterate_covered_body_parts_internal( const side s,
const std::function<void( const bodypart_str_id & )> &cb ) const
const std::function<void( const bodypart_str_id & )> &cb,
bool check_ablative_armor ) const
{
iterate_helper( this, s, cb );

//check for ablative armor too
if( is_ablative() ) {
if( check_ablative_armor && is_ablative() ) {
for( const item_pocket *pocket : get_all_ablative_pockets() ) {
if( !pocket->empty() ) {
// get the contained plate
Expand Down Expand Up @@ -8411,36 +8414,75 @@ int item::get_avg_coverage( const cover_type &type ) const

int item::get_coverage( const bodypart_id &bodypart, const cover_type &type ) const
{
int coverage = 0;
if( const armor_portion_data *portion_data = portion_for_bodypart( bodypart ) ) {
switch( type ) {
case cover_type::COVER_DEFAULT:
return portion_data->coverage;
case cover_type::COVER_MELEE:
return portion_data->cover_melee;
case cover_type::COVER_RANGED:
return portion_data->cover_ranged;
case cover_type::COVER_VITALS:
return portion_data->cover_vitals;
case cover_type::COVER_DEFAULT: {
coverage = portion_data->coverage;
break;
}
case cover_type::COVER_MELEE: {
coverage = portion_data->cover_melee;
break;
}
case cover_type::COVER_RANGED: {
coverage = portion_data->cover_ranged;
break;
}
case cover_type::COVER_VITALS: {
coverage = portion_data->cover_vitals;
break;
}
}
}
return 0;
if( is_ablative() ) {
for( const item_pocket *pocket : contents.get_all_ablative_pockets() ) {
if( !pocket->empty() ) {
// get the contained plate
const item &ablative_armor = pocket->front();
//Rationale for this kind of aggregation is that ablative armor
//can't be bigger than an armor that contains it. But it may cover
//new body parts, e.g. face shield attached to hard hat.
coverage = std::max( coverage, ablative_armor.get_coverage( bodypart ) );
}
}
}
return coverage;
}

int item::get_coverage( const sub_bodypart_id &bodypart, const cover_type &type ) const
{
int coverage = 0;
if( const armor_portion_data *portion_data = portion_for_bodypart( bodypart ) ) {
switch( type ) {
case cover_type::COVER_DEFAULT:
return portion_data->coverage;
case cover_type::COVER_MELEE:
return portion_data->cover_melee;
case cover_type::COVER_RANGED:
return portion_data->cover_ranged;
case cover_type::COVER_VITALS:
return portion_data->cover_vitals;
case cover_type::COVER_DEFAULT: {
coverage = portion_data->coverage;
break;
}
case cover_type::COVER_MELEE: {
coverage = portion_data->cover_melee;
break;
}
case cover_type::COVER_RANGED: {
coverage = portion_data->cover_ranged;
break;
}
case cover_type::COVER_VITALS: {
coverage = portion_data->cover_vitals;
break;
}
}
}
return 0;
if( is_ablative() ) {
for( const item_pocket *pocket : contents.get_all_ablative_pockets() ) {
if( !pocket->empty() ) {
// get the contained plate
const item &ablative_armor = pocket->front();
coverage = std::max( coverage, ablative_armor.get_coverage( bodypart ) );
}
}
}
return coverage;
}

bool item::has_sublocations() const
Expand Down Expand Up @@ -8501,19 +8543,41 @@ float item::get_thickness( const bodypart_id &bp ) const
if( t == nullptr ) {
return is_pet_armor() ? type->pet_armor->thickness : 0.0f;
}
float avg_thickness = 0.0f;

for( const armor_portion_data &data : t->data ) {
if( !data.covers.has_value() ) {
continue;
}
for( const bodypart_str_id &bpid : data.covers.value() ) {
if( bp == bpid ) {
return data.avg_thickness;
avg_thickness = data.avg_thickness;
break;
}
}
if( avg_thickness > 0.0f ) {
break;
}
}
// body part not covered by this armour
return 0.0f;
if( is_ablative() ) {
int ablatives = 0;
float ablative_thickness = 0.0;
for( const item_pocket *pocket : contents.get_all_ablative_pockets() ) {
if( !pocket->empty() ) {
// get the contained plate
const item &ablative_armor = pocket->front();
float tmp_thickness = ablative_armor.get_thickness( bp );
if( tmp_thickness > 0.0f ) {
ablative_thickness += tmp_thickness;
ablatives += 1;
}
}
}
if( ablatives ) {
avg_thickness += ablative_thickness / ablatives;
}
}
return avg_thickness;
}

float item::get_thickness( const sub_bodypart_id &bp ) const
Expand All @@ -8538,7 +8602,8 @@ int item::get_warmth( const bodypart_id &bp ) const
{
double warmth_val = 0.0;
float limb_coverage = 0.0f;
if( !covers( bp ) ) {
bool check_ablative_armor = false;
if( !covers( bp, check_ablative_armor ) ) {
return 0;
}
warmth_val = get_warmth();
Expand All @@ -8551,15 +8616,27 @@ int item::get_warmth( const bodypart_id &bp ) const
limb_coverage = 100;
} else {
for( const sub_bodypart_str_id &sbp : bp->sub_parts ) {
if( !covers( sbp ) ) {
if( !covers( sbp, check_ablative_armor ) ) {
continue;
}

// TODO: handle non 100% sub body part coverages
limb_coverage += sbp->max_coverage;
}
}
return std::round( warmth_val * limb_coverage / 100.0f );
int warmth = std::round( warmth_val * limb_coverage / 100.0f );

if( is_ablative() ) {
for( const item_pocket *pocket : contents.get_all_ablative_pockets() ) {
if( !pocket->empty() ) {
// get the contained plate
const item &ablative_armor = pocket->front();
warmth += ablative_armor.get_warmth( bp );
}
}
}

return warmth;
}

units::volume item::get_pet_armor_max_vol() const
Expand Down Expand Up @@ -8927,6 +9004,19 @@ int item::breathability( const bodypart_id &bp ) const
}
const armor_portion_data *a = portion_for_bodypart( bp );
if( a == nullptr ) {
//This body part might still be covered by attachments of this armor (for example face shield).
//Check their breathability.
if( is_ablative() ) {
for( const item_pocket *pocket : contents.get_all_ablative_pockets() ) {
if( !pocket->empty() ) {
// get the contained plate
const item &ablative_armor = pocket->front();
if( ablative_armor.covers( bp ) ) {
return ablative_armor.breathability( bp );
}
}
}
}
// if it doesn't cover it breathes great
return 100;
}
Expand Down Expand Up @@ -9289,7 +9379,28 @@ std::vector<const part_material *> item::armor_made_of( const bodypart_id &bp )
for( const part_material &m : d.materials ) {
matlist.emplace_back( &m );
}
return matlist;
break;
}
if( !matlist.empty() ) {
break;
}
}
if( is_ablative() ) {
for( const item_pocket *pocket : contents.get_all_ablative_pockets() ) {
if( !pocket->empty() ) {
// get the contained plate
const item &ablative_armor = pocket->front();
auto abl_mat = ablative_armor.armor_made_of( bp );
//It is not clear how to aggregate armor material from different pockets.
//A vest might contain two different plates from different materials.
//But only one plate can be hit at one attack (according to Character::ablative_armor_absorb).
//So for now return here only material for one plate + base clothing materials.
if( !abl_mat.empty() ) {
matlist.insert( std::end( matlist ), std::begin( abl_mat ), std::end( abl_mat ) );
return matlist;
}

}
}
}
return matlist;
Expand All @@ -9313,7 +9424,24 @@ std::vector<const part_material *> item::armor_made_of( const sub_bodypart_id &b
for( const part_material &m : d.materials ) {
matlist.emplace_back( &m );
}
return matlist;
break;
}
if( !matlist.empty() ) {
break;
}
}
if( is_ablative() ) {
for( const item_pocket *pocket : contents.get_all_ablative_pockets() ) {
if( !pocket->empty() ) {
// get the contained plate
const item &ablative_armor = pocket->front();
auto abl_mat = ablative_armor.armor_made_of( bp );
if( !abl_mat.empty() ) {
matlist.insert( std::end( matlist ), std::begin( abl_mat ), std::end( abl_mat ) );
return matlist;
}

}
}
}
return matlist;
Expand Down
10 changes: 6 additions & 4 deletions src/item.h
Original file line number Diff line number Diff line change
Expand Up @@ -2111,11 +2111,11 @@ class item : public visitable
/**
* Whether this item (when worn) covers the given body part.
*/
bool covers( const bodypart_id &bp ) const;
bool covers( const bodypart_id &bp, bool check_ablative_armor = true ) const;
/**
* Whether this item (when worn) covers the given sub body part.
*/
bool covers( const sub_bodypart_id &bp ) const;
bool covers( const sub_bodypart_id &bp, bool check_ablative_armor = true ) const;
// do both items overlap a bodypart at all? returns the side that conflicts via rhs
std::optional<side> covers_overlaps( const item &rhs ) const;
/**
Expand Down Expand Up @@ -3056,9 +3056,11 @@ class item : public visitable
bool process_internal( map &here, Character *carrier, const tripoint_bub_ms &pos, float insulation,
temperature_flag flag, float spoil_modifier, bool watertight_container );
void iterate_covered_body_parts_internal( side s,
const std::function<void( const bodypart_str_id & )> &cb ) const;
const std::function<void( const bodypart_str_id & )> &cb,
bool check_ablative_armor = true ) const;
void iterate_covered_sub_body_parts_internal( side s,
const std::function<void( const sub_bodypart_str_id & )> &cb ) const;
const std::function<void( const sub_bodypart_str_id & )> &cb,
bool check_ablative_armor = true ) const;
/**
* Calculate the thermal energy and temperature change of the item
* @param temp Temperature of surroundings
Expand Down
Loading
Loading