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

Hot air from fires and direct heat radiation applies to tile temperatures #24956

Merged
merged 7 commits into from
Aug 27, 2018
4 changes: 2 additions & 2 deletions src/field.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1217,10 +1217,10 @@ bool map::process_fields_in_submap( submap *const current_submap,
dirty_transparency_cache = true; // Smoke affects transparency
}

// Hot air is a heavy load on the CPU and it doesn't do much
// Hot air is a load on the CPU
// Don't produce too much of it if we have a lot fires nearby, they produce
// radiant heat which does what hot air would do anyway
if( rng( 0, adjacent_fires ) > 2 ) {
if( adjacent_fires < 5 && rng( 0, 4 - adjacent_fires ) ) {
create_hot_air( p, cur.getFieldDensity() );
}
}
Expand Down
75 changes: 74 additions & 1 deletion src/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1789,10 +1789,83 @@ void game::update_weather()
}
}

int get_heat_radiation( const tripoint &location, bool direct )
{
// Direct heat from fire sources
// Cache fires to avoid scanning the map around us bp times
// Stored as intensity-distance pairs
int temp_mod = 0;
std::vector<std::pair<int, int>> fires;
fires.reserve( 13 * 13 );
int best_fire = 0;
for( const tripoint &dest : g->m.points_in_radius( location, 6 ) ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whether function is moved from class game to be a free function, or remains as is, can shorten all instances of g->m.__func__ to just m.__func__
Other lines impacted:

  • 1804
  • 1807
  • 1810

Function is only called currently as g->get_heat_radiation so it already has access to the global-g's map.

int heat_intensity = 0;

int ffire = g->m.get_field_strength( dest, fd_fire );
if( ffire > 0 ) {
heat_intensity = ffire;
} else if( g->m.tr_at( dest ).loadid == tr_lava ) {
heat_intensity = 3;
}
if( heat_intensity == 0 || !g->m.sees( location, dest, -1 ) ) {
// No heat source here
continue;
}
// Ensure fire_dist >= 1 to avoid divide-by-zero errors.
const int fire_dist = std::max( 1, square_dist( dest, location ) );
fires.emplace_back( std::make_pair( heat_intensity, fire_dist ) );
if( fire_dist <= 1 ) {
// Extend limbs/lean over a single adjacent fire to warm up
best_fire = std::max( best_fire, heat_intensity );
}
}

for( const auto &intensity_dist : fires ) {
const int intensity = intensity_dist.first;
const int distance = intensity_dist.second;
temp_mod += 6 * intensity * intensity / distance;
}
if( direct ) {
return best_fire;
}
return temp_mod;
}

int get_convection_temperature( const tripoint &location )
{
// Heat from hot air (fields)
int temp_mod = 0;
const trap &trap_at_pos = g->m.tr_at( location );
// directly on fire/lava tiles
int tile_strength = g->m.get_field_strength( location, fd_fire );
if( tile_strength > 0 || trap_at_pos.loadid == tr_lava ) {
temp_mod += 300;
}
// hot air of a fire/lava
auto tile_strength_mod = []( const tripoint &loc, field_id fld, int case_1, int case_2, int case_3 ){
int strength = g->m.get_field_strength( loc, fld );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In addition to changing from g->m.get_field_strength to just m.get_field_strength, if the function is made a free function and its function signature modified to pass in the map may need to modify the lamda to capture the passed in map variable: [&m]( const tripoint &loc, ... ){

int cases[3] = { case_1, case_2, case_3 };
return ( strength > 0 && strength < 4 ) ? cases[ strength - 1 ] : 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having looked into things more carefully the condition isn't necessary. Can be modified to just return cases[ strength - 1 ];

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...and it sends the game into infinite loop with insane temperatures, stamina loss, and inevitable death. No, just no...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh I'm dumb. I was making an assumption and not validating that assumption. If the field does not exist at the location it's not going to be returning a value in range [1, 3]. Sorry for the bad suggestion.

};

temp_mod += tile_strength_mod( location, fd_hot_air1, 2, 6, 10 );
temp_mod += tile_strength_mod( location, fd_hot_air2, 6, 16, 20 );
temp_mod += tile_strength_mod( location, fd_hot_air3, 16, 40, 70 );
temp_mod += tile_strength_mod( location, fd_hot_air4, 70, 100, 160 );

return temp_mod;
}

int game::get_temperature( const tripoint &location )
{
int temp_mod = 0; // local modifier

if( !new_game ) {
temp_mod += get_heat_radiation( location, false );
temp_mod += get_convection_temperature( location );
}
//underground temperature = average New England temperature = 43F/6C rounded to int
return ( location.z < 0 ? AVERAGE_ANNUAL_TEMPERATURE : temperature ) + ( new_game ? 0 : m.temperature( location ) );
return ( location.z < 0 ? AVERAGE_ANNUAL_TEMPERATURE : temperature ) + ( new_game ? 0 : ( m.temperature( location ) + temp_mod ) );
}

int game::assign_mission_id()
Expand Down
8 changes: 8 additions & 0 deletions src/game.h
Original file line number Diff line number Diff line change
Expand Up @@ -1116,4 +1116,12 @@ class game
bool save_player_data();
};

// Returns temperature modifier from direct heat radiation of nearby sources
// @param location Location affected by heat sources
// @param direct forces return of heat intensity (and not temperature modifier) of
// adjacent hottest heat source
int get_heat_radiation( const tripoint &location, bool direct );
// Returns temperature modifier from hot air fields of given location
int get_convection_temperature( const tripoint &location );

#endif
144 changes: 20 additions & 124 deletions src/player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1005,7 +1005,7 @@ void player::update_bodytemp()
const bool has_bark = has_trait( trait_BARK );
const bool has_sleep = has_effect( effect_sleep );
const bool has_sleep_state = has_sleep || in_sleep_state();
const bool has_heatsink = has_bionic( bio_heatsink ) || is_wearing( "rm13_armor_on" );
const bool has_heatsink = has_bionic( bio_heatsink ) || is_wearing( "rm13_armor_on" ) || has_trait( trait_id( "M_SKIN2" ) );
const bool has_common_cold = has_effect( effect_common_cold );
const bool has_climate_control = in_climate_control();
const bool use_floor_warmth = can_use_floor_warmth();
Expand All @@ -1032,35 +1032,10 @@ void player::update_bodytemp()
// Sunlight
const int sunlight_warmth = g->is_in_sunlight( pos() ) ? 0 :
( g->weather == WEATHER_SUNNY ? 1000 : 500 );
// Fire at our tile
const int fire_warmth = bodytemp_modifier_fire();

// Cache fires to avoid scanning the map around us bp times
// Stored as intensity-distance pairs
std::vector<std::pair<int, int>> fires;
fires.reserve( 13 * 13 );
int best_fire = 0;
for( const tripoint &dest : g->m.points_in_radius( pos(), 6 ) ) {
int heat_intensity = 0;

int ffire = g->m.get_field_strength( dest, fd_fire );
if( ffire > 0 ) {
heat_intensity = ffire;
} else if( g->m.tr_at( dest ).loadid == tr_lava ) {
heat_intensity = 3;
}
if( heat_intensity == 0 || !g->m.sees( pos(), dest, -1 ) ) {
// No heat source here
continue;
}
// Ensure fire_dist >= 1 to avoid divide-by-zero errors.
const int fire_dist = std::max( 1, square_dist( dest, pos() ) );
fires.emplace_back( std::make_pair( heat_intensity, fire_dist ) );
if( fire_dist <= 1 ) {
// Extend limbs/lean over a single adjacent fire to warm up
best_fire = std::max( best_fire, heat_intensity );
}
}
// CONVECTION HEAT; Hot air from a heat source
// times 50 to convert to weather.h BODYTEMP scale
const int fire_warmth = get_convection_temperature( pos() ) * 50;
const int best_fire = get_heat_radiation( pos(), true );

const int lying_warmth = use_floor_warmth ? floor_warmth( pos() ) : 0;
const int water_temperature =
Expand Down Expand Up @@ -1119,34 +1094,29 @@ void player::update_bodytemp()
temp_conv[bp] += fatigue_warmth;
// Mutations
temp_conv[bp] += mutation_heat_low;
// CONVECTION HEAT SOURCES (generates body heat, helps fight frostbite)
// Bark : lowers blister count to -10; harder to get blisters
int blister_count = ( has_bark ? -10 : 0 ); // If the counter is high, your skin starts to burn
for( const auto &intensity_dist : fires ) {
const int intensity = intensity_dist.first;
const int distance = intensity_dist.second;
if( frostbite_timer[bp] > 0 ) {
frostbite_timer[bp] -= std::max( 0, intensity - distance / 2 );
}
const int heat_here = intensity * intensity / distance;
temp_conv[bp] += 300 * heat_here;
blister_count += heat_here;
}
// Bionic "Thermal Dissipation" says it prevents fire damage up to 2000F.
// But it's kinda hard to get the balance right, let's go with 20 blisters
if( has_heatsink ) {
blister_count -= 20;
}
// DIRECT HEAT SOURCES (generates body heat, helps fight frostbite)
// Bark : lowers blister count to -5; harder to get blisters
int blister_count = ( has_bark ? -5 : 0 ); // If the counter is high, your skin starts to burn

const int h_radiation = get_heat_radiation( pos(), false );
temp_conv[bp] += h_radiation * 50; // conversion to BODYTEMP see weather.h
if( frostbite_timer[bp] > 0 ) {
frostbite_timer[bp] -= std::max( 5, h_radiation );
}
// 111F (44C) is a temperature in which proteins break down: https://en.wikipedia.org/wiki/Burn
blister_count += h_radiation - 111 > 0 ? std::max( (int)sqrt( h_radiation - 111 ), 0 ) : 0;

const bool pyromania = has_trait( trait_PYROMANIA );
// BLISTERS : Skin gets blisters from intense heat exposure.
if( blister_count - get_env_resist( bp ) > 10 ) {
// Fire protection protects from blisters.
// Heatsinks give near-immunity.
if( blister_count - get_armor_fire( bp ) - ( has_heatsink ? 20 : 0 ) > 0 ) {
add_effect( effect_blisters, 1_turns, bp );
if( pyromania ) {
add_morale( MORALE_PYROMANIA_NEARFIRE, 10, 10, 1_hours, 30_minutes ); // Proximity that's close enough to harm us gives us a bit of a thrill
rem_morale( MORALE_PYROMANIA_NOFIRE );
}
} else if( pyromania && fire_warmth >= 1 ) { // Only give us fire bonus if there's actually fire
} else if( pyromania && best_fire >= 1 ) { // Only give us fire bonus if there's actually fire
add_morale( MORALE_PYROMANIA_NEARFIRE, 5, 5, 30_minutes, 15_minutes ); // Gain a much smaller mood boost even if it doesn't hurt us
rem_morale( MORALE_PYROMANIA_NOFIRE );
}
Expand Down Expand Up @@ -1569,80 +1539,6 @@ int player::floor_warmth( const tripoint &pos ) const
return ( item_warmth + bedding_warmth + floor_mut_warmth );
}

int player::bodytemp_modifier_fire() const
{
int temp_conv = 0;
// Being on fire increases very intensely the convergent temperature.
if( has_effect( effect_onfire ) ) {
temp_conv += 15000;
}

const trap &trap_at_pos = g->m.tr_at( pos() );
// Same with standing on fire.
int tile_strength = g->m.get_field_strength( pos(), fd_fire );
if( tile_strength > 2 || trap_at_pos.loadid == tr_lava ) {
temp_conv += 15000;
}
// Standing in the hot air of a fire is nice.
tile_strength = g->m.get_field_strength( pos(), fd_hot_air1 );
switch( tile_strength ) {
case 3:
temp_conv += 500;
break;
case 2:
temp_conv += 300;
break;
case 1:
temp_conv += 100;
break;
default:
break;
}
tile_strength = g->m.get_field_strength( pos(), fd_hot_air2 );
switch( tile_strength ) {
case 3:
temp_conv += 1000;
break;
case 2:
temp_conv += 800;
break;
case 1:
temp_conv += 300;
break;
default:
break;
}
tile_strength = g->m.get_field_strength( pos(), fd_hot_air3 );
switch( tile_strength ) {
case 3:
temp_conv += 3500;
break;
case 2:
temp_conv += 2000;
break;
case 1:
temp_conv += 800;
break;
default:
break;
}
tile_strength = g->m.get_field_strength( pos(), fd_hot_air4 );
switch( tile_strength ) {
case 3:
temp_conv += 8000;
break;
case 2:
temp_conv += 5000;
break;
case 1:
temp_conv += 3500;
break;
default:
break;
}
return temp_conv;
}

int player::bodytemp_modifier_traits( bool overheated ) const
{
int mod = 0;
Expand Down
2 changes: 0 additions & 2 deletions src/player.h
Original file line number Diff line number Diff line change
Expand Up @@ -1631,8 +1631,6 @@ class player : public Character
static int floor_item_warmth( const tripoint &pos );
/** Final warmth from the floor **/
int floor_warmth( const tripoint &pos ) const;
/** Correction factor of the body temperature due to fire **/
int bodytemp_modifier_fire() const;
/** Correction factor of the body temperature due to traits and mutations **/
int bodytemp_modifier_traits( bool overheated ) const;
/** Correction factor of the body temperature due to traits and mutations for player lying on the floor **/
Expand Down