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

Improve performance of Character::update_bodytemp #50735

Merged
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
212 changes: 157 additions & 55 deletions src/character.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4824,7 +4824,7 @@ bool Character::in_climate_control()
if( w.active && w.is_power_armor() ) {
return true;
}
if( worn_with_flag( flag_CLIMATE_CONTROL ) ) {
if( w.has_flag( flag_CLIMATE_CONTROL ) ) {
return true;
}
}
Expand All @@ -4850,15 +4850,33 @@ bool Character::in_climate_control()
return regulated_area;
}

int Character::get_wind_resistance( const bodypart_id &bp ) const
std::map<bodypart_id, int> Character::get_wind_resistance( const std::map <bodypart_id,
std::vector<const item *>> &clothing_map ) const
{
int coverage = 0;
float totalExposed = 1.0f;
int totalCoverage = 0;
int penalty = 100;

for( const item &i : worn ) {
if( i.covers( bp ) ) {
std::map<bodypart_id, int> ret;
for( const bodypart_id &bp : get_all_body_parts() ) {
ret.emplace( bp, 0 );
}

// Your shell provides complete wind protection if you're inside it
if( has_active_mutation( trait_SHELL2 ) ) {
for( std::pair<const bodypart_id, int> &this_bp : ret ) {
this_bp.second = 100;
}
return ret;
}

for( const std::pair<const bodypart_id, std::vector<const item *>> &on_bp : clothing_map ) {
const bodypart_id &bp = on_bp.first;

int coverage = 0;
float totalExposed = 1.0f;
int totalCoverage = 0;
int penalty = 100;

for( const item *it : on_bp.second ) {
const item &i = *it;
if( i.made_of( material_id( "leather" ) ) || i.made_of( material_id( "plastic" ) ) ||
i.made_of( material_id( "bone" ) ) ||
i.made_of( material_id( "chitin" ) ) || i.made_of( material_id( "nomex" ) ) ) {
Expand All @@ -4874,17 +4892,11 @@ int Character::get_wind_resistance( const bodypart_id &bp ) const
coverage = std::max( 0, i.get_coverage( bp ) - penalty );
totalExposed *= ( 1.0 - coverage / 100.0 ); // Coverage is between 0 and 1?
}
}

// Your shell provides complete wind protection if you're inside it
if( has_active_mutation( trait_SHELL2 ) ) {
totalCoverage = 100;
return totalCoverage;
ret[bp] = totalCoverage = 100 - totalExposed * 100;
}

totalCoverage = 100 - totalExposed * 100;

return totalCoverage;
return ret;
}

void layer_details::reset()
Expand Down Expand Up @@ -6785,6 +6797,23 @@ void Character::update_bodytemp()
const int mutation_heat_bonus = mutation_heat_high - mutation_heat_low;

const int h_radiation = get_heat_radiation( pos(), false );

std::map<bodypart_id, std::vector<const item *>> clothing_map;
for( const bodypart_id &bp : get_all_body_parts() ) {
clothing_map.emplace( bp, std::vector<const item *>() );
}
for( const item &it : worn ) {
for( const bodypart_str_id &covered : it.get_covered_body_parts() ) {
clothing_map[covered.id()].emplace_back( &it );
}
}

std::map<bodypart_id, int> warmth_per_bp = warmth( clothing_map );
std::map<bodypart_id, int> bonus_warmth_per_bp = bonus_item_warmth( clothing_map );
std::map<bodypart_id, int> wind_res_per_bp = get_wind_resistance( clothing_map );
// We might not use this at all, so leave it empty
// If we do need to use it, we'll initialize it (once) there
std::map<bodypart_id, int> fire_armor_per_bp;
// Current temperature and converging temperature calculations
for( const bodypart_id &bp : get_all_body_parts() ) {

Expand All @@ -6802,13 +6831,13 @@ void Character::update_bodytemp()
get_part_temp_cur( bp ) );
// Produces a smooth curve between 30.0 and 60.0.
double homeostasis_adjustment = 30.0 * ( 1.0 + scaled_temperature );
int clothing_warmth_adjustment = static_cast<int>( homeostasis_adjustment * warmth( bp ) );
int clothing_warmth_adjusted_bonus = static_cast<int>( homeostasis_adjustment * bonus_item_warmth(
bp ) );
int clothing_warmth_adjustment = static_cast<int>( homeostasis_adjustment * warmth_per_bp[bp] );
int clothing_warmth_adjusted_bonus = static_cast<int>( homeostasis_adjustment *
bonus_warmth_per_bp[bp] );
// WINDCHILL

bp_windpower = static_cast<int>( static_cast<float>( bp_windpower ) * ( 1 - get_wind_resistance(
bp ) / 100.0 ) );
bp_windpower = static_cast<int>( static_cast<float>( bp_windpower ) *
( 1 - wind_res_per_bp[bp] / 100.0 ) );
// Calculate windchill
int windchill = get_local_windchill( player_local_temp,
get_local_humidity( weather.humidity, get_weather().weather_id, sheltered ),
Expand Down Expand Up @@ -6854,7 +6883,13 @@ void Character::update_bodytemp()
// BLISTERS : Skin gets blisters from intense heat exposure.
// Fire protection protects from blisters.
// Heatsinks give near-immunity.
if( blister_count - get_armor_fire( bp ) - ( has_heatsink ? 20 : 0 ) > 0 ) {
if( has_heatsink ) {
blister_count -= 20;
}
if( fire_armor_per_bp.empty() && blister_count > 0 ) {
fire_armor_per_bp = get_armor_fire( clothing_map );
}
if( blister_count - fire_armor_per_bp[bp] > 0 ) {
add_effect( effect_blisters, 1_turns, bp );
if( pyromania ) {
add_morale( MORALE_PYROMANIA_NEARFIRE, 10, 10, 1_hours,
Expand Down Expand Up @@ -6998,7 +7033,7 @@ void Character::update_bodytemp()
remove_effect( effect_hot_speed, bp );
}

update_frostbite( bp, bp_windpower );
update_frostbite( bp, bp_windpower, warmth_per_bp );

// Warn the player if condition worsens
if( temp_before > BODYTEMP_FREEZING && temp_after < BODYTEMP_FREEZING ) {
Expand Down Expand Up @@ -7060,7 +7095,8 @@ void Character::update_bodytemp()
}
}

void Character::update_frostbite( const bodypart_id &bp, const int FBwindPower )
void Character::update_frostbite( const bodypart_id &bp, const int FBwindPower,
const std::map<bodypart_id, int> &warmth_per_bp )
{
// FROSTBITE - only occurs to hands, feet, face
/**
Expand Down Expand Up @@ -7099,7 +7135,7 @@ void Character::update_frostbite( const bodypart_id &bp, const int FBwindPower )
int wetness_percentage = 100 * get_part_wetness_percentage( bp ); // 0 - 100
// Warmth gives a slight buff to temperature resistance
// Wetness gives a heavy nerf to temperature resistance
double adjusted_warmth = warmth( bp ) - wetness_percentage;
double adjusted_warmth = warmth_per_bp.at( bp ) - wetness_percentage;
int Ftemperature = static_cast<int>( player_local_temp + 0.2 * adjusted_warmth );

int intense = get_effect_int( effect_frostbite, bp );
Expand Down Expand Up @@ -8669,6 +8705,7 @@ int Character::get_armor_bullet( bodypart_id bp ) const
return get_armor_bullet_base( bp ) + armor_bullet_bonus;
}

// TODO: Reduce duplication with below function
int Character::get_armor_type( damage_type dt, bodypart_id bp ) const
{
switch( dt ) {
Expand Down Expand Up @@ -8707,6 +8744,56 @@ int Character::get_armor_type( damage_type dt, bodypart_id bp ) const
return 0;
}

std::map<bodypart_id, int> Character::get_all_armor_type( damage_type dt,
const std::map<bodypart_id, std::vector<const item *>> &clothing_map ) const
{
std::map<bodypart_id, int> ret;
for( const bodypart_id &bp : get_all_body_parts() ) {
ret.emplace( bp, 0 );
}

for( std::pair<const bodypart_id, int> &per_bp : ret ) {
const bodypart_id &bp = per_bp.first;
switch( dt ) {
case damage_type::PURE:
case damage_type::BIOLOGICAL:
// Characters cannot resist this
return ret;
/* BASH, CUT, STAB, and BULLET don't benefit from the clothing_map optimization */
// TODO: Fix that
case damage_type::BASH:
per_bp.second += get_armor_bash( bp );
break;
case damage_type::CUT:
per_bp.second += get_armor_cut( bp );
break;
case damage_type::STAB:
per_bp.second += get_armor_cut( bp ) * 0.8f;
break;
case damage_type::BULLET:
per_bp.second += get_armor_bullet( bp );
break;
case damage_type::ACID:
case damage_type::HEAT:
case damage_type::COLD:
case damage_type::ELECTRIC: {
for( const item *it : clothing_map.at( bp ) ) {
per_bp.second += it->damage_resist( dt );
}

per_bp.second += mutation_armor( bp, dt );
break;
}
case damage_type::NONE:
case damage_type::NUM:
debugmsg( "Invalid damage type: %d", dt );
return ret;
}
}

return ret;
}

int Character::get_armor_bash_base( bodypart_id bp ) const
{
float ret = 0;
Expand Down Expand Up @@ -10002,9 +10089,10 @@ float Character::bionic_armor_bonus( const bodypart_id &bp, damage_type dt ) con
return result;
}

int Character::get_armor_fire( const bodypart_id &bp ) const
std::map<bodypart_id, int> Character::get_armor_fire( const
std::map<bodypart_id, std::vector<const item *>> &clothing_map ) const
{
return get_armor_type( damage_type::HEAT, bp );
return get_all_armor_type( damage_type::HEAT, clothing_map );
}

void Character::did_hit( Creature &target )
Expand Down Expand Up @@ -11327,55 +11415,69 @@ std::string Character::is_snuggling() const
return "nothing";
}

int Character::warmth( const bodypart_id &bp ) const
std::map<bodypart_id, int> Character::warmth( const std::map<bodypart_id, std::vector<const item *>>
&clothing_map ) const
{
int ret = 0;
double warmth = 0.0;
std::map<bodypart_id, int> ret;
for( const bodypart_id &bp : get_all_body_parts() ) {
ret.emplace( bp, 0 );
}

for( const item &i : worn ) {
if( i.covers( bp ) ) {
warmth = i.get_warmth();
for( const std::pair<const bodypart_id, std::vector<const item *>> &on_bp : clothing_map ) {
const bodypart_id &bp = on_bp.first;

double warmth = 0.0;
const int wetness_pct = get_part_wetness_percentage( bp );

for( const item *it : on_bp.second ) {
warmth = it->get_warmth();
// Wool items do not lose their warmth due to being wet.
// Warmth is reduced by 0 - 66% based on wetness.
if( !i.made_of( material_id( "wool" ) ) ) {
warmth *= 1.0 - 0.66 * get_part_wetness_percentage( bp );
if( !it->made_of( material_id( "wool" ) ) ) {
warmth *= 1.0 - 0.66 * wetness_pct;
}
ret += std::round( warmth );
ret[bp] += std::round( warmth );
}
ret[bp] += get_effect_int( effect_heating_bionic, bp );
}
ret += get_effect_int( effect_heating_bionic, bp );
return ret;
}

static int bestwarmth( const std::list< item > &its, const flag_id &flag )
static int bestwarmth( const std::vector<const item *> &its, const flag_id &flag )
{
int best = 0;
for( const item &w : its ) {
if( w.has_flag( flag ) && w.get_warmth() > best ) {
best = w.get_warmth();
for( const item *w : its ) {
if( w->has_flag( flag ) && w->get_warmth() > best ) {
best = w->get_warmth();
}
}
return best;
}

int Character::bonus_item_warmth( const bodypart_id &bp ) const
std::map<bodypart_id, int> Character::bonus_item_warmth( const
std::map<bodypart_id, std::vector<const item *>> &clothing_map ) const
{
int ret = 0;

// If the player is not wielding anything big, check if hands can be put in pockets
if( ( bp == body_part_hand_l || bp == body_part_hand_r ) &&
weapon.volume() < 500_ml ) {
ret += bestwarmth( worn, flag_POCKETS );
std::map<bodypart_id, int> ret;
for( const bodypart_id &bp : get_all_body_parts() ) {
ret.emplace( bp, 0 );
}
for( const std::pair<const bodypart_id, std::vector<const item *>> &on_bp : clothing_map ) {
const bodypart_id &bp = on_bp.first;
// If the player is not wielding anything big, check if hands can be put in pockets
if( ( bp == body_part_hand_l || bp == body_part_hand_r ) &&
weapon.volume() < 500_ml ) {
ret[bp] += bestwarmth( on_bp.second, flag_POCKETS );
}

// If the player's head is not encumbered, check if hood can be put up
if( bp == body_part_head && encumb( body_part_head ) < 10 ) {
ret += bestwarmth( worn, flag_HOOD );
}
// If the player's head is not encumbered, check if hood can be put up
if( bp == body_part_head && encumb( body_part_head ) < 10 ) {
ret[bp] += bestwarmth( on_bp.second, flag_HOOD );
}

// If the player's mouth is not encumbered, check if collar can be put up
if( bp == body_part_mouth && encumb( body_part_mouth ) < 10 ) {
ret += bestwarmth( worn, flag_COLLAR );
// If the player's mouth is not encumbered, check if collar can be put up
if( bp == body_part_mouth && encumb( body_part_mouth ) < 10 ) {
ret[bp] += bestwarmth( on_bp.second, flag_COLLAR );
}
}

return ret;
Expand Down
19 changes: 13 additions & 6 deletions src/character.h
Original file line number Diff line number Diff line change
Expand Up @@ -680,7 +680,8 @@ class Character : public Creature, public visitable
bool is_hibernating() const;
/** Maintains body temperature */
void update_bodytemp();
void update_frostbite( const bodypart_id &bp, int FBwindPower );
void update_frostbite( const bodypart_id &bp, int FBwindPower,
const std::map<bodypart_id, int> &warmth_per_bp );
/** Equalizes heat between body parts */
void temp_equalizer( const bodypart_id &bp1, const bodypart_id &bp2 );

Expand Down Expand Up @@ -719,7 +720,8 @@ class Character : public Creature, public visitable
bool in_climate_control();

/** Returns wind resistance provided by armor, etc **/
int get_wind_resistance( const bodypart_id &bp ) const;
std::map<bodypart_id, int> get_wind_resistance( const
std::map<bodypart_id, std::vector<const item *>> &clothing_map ) const;

/** Returns true if the player isn't able to see */
bool is_blind() const;
Expand Down Expand Up @@ -972,8 +974,9 @@ class Character : public Creature, public visitable
float bionic_armor_bonus( const bodypart_id &bp, damage_type dt ) const;
/** Returns the armor bonus against given type from martial arts buffs */
int mabuff_armor_bonus( damage_type type ) const;
/** Returns overall fire resistance for the body part */
int get_armor_fire( const bodypart_id &bp ) const;
/** Returns overall fire resistance */
std::map<bodypart_id, int> get_armor_fire( const std::map<bodypart_id, std::vector<const item *>>
&clothing_map ) const;
// --------------- Mutation Stuff ---------------
// In newcharacter.cpp
/** Returns the id of a random starting trait that costs >= 0 points */
Expand Down Expand Up @@ -2318,6 +2321,8 @@ class Character : public Creature, public visitable
int get_armor_acid( bodypart_id bp ) const;
/** Returns overall resistance to given type on the bod part */
int get_armor_type( damage_type dt, bodypart_id bp ) const override;
std::map<bodypart_id, int> get_all_armor_type( damage_type dt,
const std::map<bodypart_id, std::vector<const item *>> &clothing_map ) const;

int get_stim() const;
void set_stim( int new_stim );
Expand Down Expand Up @@ -2479,9 +2484,11 @@ class Character : public Creature, public visitable
void set_destination_activity( const player_activity &new_destination_activity );
void clear_destination_activity();
/** Returns warmth provided by armor, etc. */
int warmth( const bodypart_id &bp ) const;
std::map<bodypart_id, int> warmth( const std::map<bodypart_id, std::vector<const item *>>
&clothing_map ) const;
/** Returns warmth provided by an armor's bonus, like hoods, pockets, etc. */
int bonus_item_warmth( const bodypart_id &bp ) const;
std::map<bodypart_id, int> bonus_item_warmth( const std::map<bodypart_id, std::vector<const item *>>
&clothing_map ) const;
/** Can the player lie down and cover self with blankets etc. **/
bool can_use_floor_warmth() const;
/**
Expand Down
Loading