diff --git a/src/character.cpp b/src/character.cpp index fed72177b2099..083b0da4fd447 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -213,7 +213,14 @@ static const trait_id trait_ADRENALINE( "ADRENALINE" ); static const trait_id trait_ANTENNAE( "ANTENNAE" ); static const trait_id trait_ANTLERS( "ANTLERS" ); static const trait_id trait_BADBACK( "BADBACK" ); +static const trait_id trait_CHITIN_FUR( "CHITIN_FUR" ); +static const trait_id trait_CHITIN_FUR2( "CHITIN_FUR2" ); +static const trait_id trait_CHITIN_FUR3( "CHITIN_FUR3" ); static const trait_id trait_DEBUG_NODMG( "DEBUG_NODMG" ); +static const trait_id trait_FELINE_FUR( "FELINE_FUR" ); +static const trait_id trait_FUR( "FUR" ); +static const trait_id trait_LIGHTFUR( "LIGHTFUR" ); +static const trait_id trait_LUPINE_FUR( "LUPINE_FUR" ); static const trait_id trait_HUGE( "HUGE" ); static const trait_id trait_HUGE_OK( "HUGE_OK" ); static const trait_id trait_PACIFIST( "PACIFIST" ); @@ -221,6 +228,7 @@ static const trait_id trait_SAVANT( "SAVANT" ); static const trait_id trait_SMALL2( "SMALL2" ); static const trait_id trait_SMALL_OK( "SMALL_OK" ); static const trait_id trait_SQUEAMISH( "SQUEAMISH" ); +static const trait_id trait_URSINE_FUR( "URSINE_FUR" ); static const trait_id trait_WOOLALLERGY( "WOOLALLERGY" ); static const bionic_id bio_ads( "bio_ads" ); @@ -1773,6 +1781,77 @@ int Character::get_part_hp_max( const bodypart_id &id ) const return enchantment_cache.modify_value( enchant_vals::mod::MAX_HP, Creature::get_part_hp_max( id ) ); } +void Character::update_body_wetness( const w_point &weather ) +{ + // Average number of turns to go from completely soaked to fully dry + // assuming average temperature and humidity + constexpr time_duration average_drying = 2_hours; + + // A modifier on drying time + double delay = 1.0; + // Weather slows down drying + delay += ( ( weather.humidity - 66 ) - ( weather.temperature - 65 ) ) / 100; + delay = std::max( 0.1, delay ); + // Fur/slime retains moisture + if( has_trait( trait_LIGHTFUR ) || has_trait( trait_FUR ) || has_trait( trait_FELINE_FUR ) || + has_trait( trait_LUPINE_FUR ) || has_trait( trait_CHITIN_FUR ) || has_trait( trait_CHITIN_FUR2 ) || + has_trait( trait_CHITIN_FUR3 ) ) { + delay = delay * 6 / 5; + } + if( has_trait( trait_URSINE_FUR ) || has_trait( trait_SLIMY ) ) { + delay *= 1.5; + } + + if( !x_in_y( 1, to_turns( average_drying * delay / 100.0 ) ) ) { + // No drying this turn + return; + } + + // Now per-body-part stuff + // To make drying uniform, make just one roll and reuse it + const int drying_roll = rng( 1, 80 ); + + for( const bodypart_id &bp : get_all_body_parts() ) { + if( get_part_wetness( bp ) == 0 ) { + continue; + } + // This is to normalize drying times + int drying_chance = get_part_drench_capacity( bp ); + const int temp_conv = get_part_temp_conv( bp ); + // Body temperature affects duration of wetness + // Note: Using temp_conv rather than temp_cur, to better approximate environment + if( temp_conv >= BODYTEMP_SCORCHING ) { + drying_chance *= 2; + } else if( temp_conv >= BODYTEMP_VERY_HOT ) { + drying_chance = drying_chance * 3 / 2; + } else if( temp_conv >= BODYTEMP_HOT ) { + drying_chance = drying_chance * 4 / 3; + } else if( temp_conv > BODYTEMP_COLD ) { + // Comfortable, doesn't need any changes + } else { + // Evaporation doesn't change that much at lower temp + drying_chance = drying_chance * 3 / 4; + } + + if( drying_chance < 1 ) { + drying_chance = 1; + } + + // TODO: Make evaporation reduce body heat + if( drying_chance >= drying_roll ) { + mod_part_wetness( bp, -1 ); + if( get_part_wetness( bp ) < 0 ) { + set_part_wetness( bp, 0 ); + } + } + // Safety measure to keep wetness within bounds + if( get_part_wetness( bp ) > get_part_drench_capacity( bp ) ) { + set_part_wetness( bp, get_part_drench_capacity( bp ) ); + } + } + // TODO: Make clothing slow down drying +} + // This must be called when any of the following change: // - effects // - bionics diff --git a/src/character.h b/src/character.h index 1a820d109be56..25d2a3e22b7b6 100644 --- a/src/character.h +++ b/src/character.h @@ -50,6 +50,7 @@ #include "units.h" #include "visitable.h" #include "weighted_list.h" +#include "weather_gen.h" class JsonIn; class JsonObject; @@ -637,6 +638,10 @@ class Character : public Creature, public visitable * - underwater * - clothes */ + + /** Maintains body wetness and handles the rate at which the player dries */ + void update_body_wetness( const w_point &weather ); + void recalc_sight_limits(); /** * Returns the apparent light level at which the player can see. diff --git a/src/effect.h b/src/effect.h index 8cbe379369a26..4dfff6d72ea4c 100644 --- a/src/effect.h +++ b/src/effect.h @@ -32,6 +32,10 @@ enum effect_rating { e_mixed // The effect has good and bad parts to the one who has it. }; +/** @relates string_id */ +template<> +const effect_type &string_id::obj() const; + class effect_type { friend void load_effect_type( const JsonObject &jo ); diff --git a/src/game.cpp b/src/game.cpp index e51d83267cb85..f3c5f2cba04d3 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -9873,7 +9873,7 @@ point game::place_player( const tripoint &dest_loc ) // Drench the player if swimmable if( m.has_flag( "SWIMMABLE", u.pos() ) && !( u.is_mounted() || ( u.in_vehicle && vp1->vehicle().can_float() ) ) ) { - u.drench( 40, { { bodypart_str_id( "foot_l" ), bodypart_str_id( "foot_r" ), bodypart_str_id( "leg_l" ), bodypart_str_id( "leg_r" ) } }, + u.drench( 80, { { bodypart_str_id( "foot_l" ), bodypart_str_id( "foot_r" ), bodypart_str_id( "leg_l" ), bodypart_str_id( "leg_r" ) } }, false ); } diff --git a/src/iuse.cpp b/src/iuse.cpp index 00c4d080bcb1b..cfd18880ebaf2 100644 --- a/src/iuse.cpp +++ b/src/iuse.cpp @@ -5888,7 +5888,7 @@ int iuse::towel_common( Character *p, item *it, bool t ) } // dry off from being wet - } else if( std::abs( p->has_morale( MORALE_WET ) ) ) { + } else if( p->has_atleast_one_wet_part() ) { p->rem_morale( MORALE_WET ); p->set_all_parts_wetness( 0 ); p->add_msg_if_player( _( "You use the %s to dry off, saturating it with water!" ), diff --git a/src/player.cpp b/src/player.cpp index 18a1ce0442375..d41d148fdf25b 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -125,9 +125,6 @@ static const trait_id trait_DEBUG_NODMG( "DEBUG_NODMG" ); static const trait_id trait_CANNIBAL( "CANNIBAL" ); static const trait_id trait_CENOBITE( "CENOBITE" ); static const trait_id trait_CF_HAIR( "CF_HAIR" ); -static const trait_id trait_CHITIN_FUR( "CHITIN_FUR" ); -static const trait_id trait_CHITIN_FUR2( "CHITIN_FUR2" ); -static const trait_id trait_CHITIN_FUR3( "CHITIN_FUR3" ); static const trait_id trait_CHLOROMORPH( "CHLOROMORPH" ); static const trait_id trait_CLUMSY( "CLUMSY" ); static const trait_id trait_COLDBLOOD4( "COLDBLOOD4" ); @@ -138,8 +135,6 @@ static const trait_id trait_EASYSLEEPER( "EASYSLEEPER" ); static const trait_id trait_EASYSLEEPER2( "EASYSLEEPER2" ); static const trait_id trait_EATHEALTH( "EATHEALTH" ); static const trait_id trait_FAT( "FAT" ); -static const trait_id trait_FELINE_FUR( "FELINE_FUR" ); -static const trait_id trait_FUR( "FUR" ); static const trait_id trait_HATES_BOOKS( "HATES_BOOKS" ); static const trait_id trait_HUGE( "HUGE" ); static const trait_id trait_HUGE_OK( "HUGE_OK" ); @@ -148,10 +143,8 @@ static const trait_id trait_INSOMNIA( "INSOMNIA" ); static const trait_id trait_INT_SLIME( "INT_SLIME" ); static const trait_id trait_LARGE( "LARGE" ); static const trait_id trait_LARGE_OK( "LARGE_OK" ); -static const trait_id trait_LIGHTFUR( "LIGHTFUR" ); static const trait_id trait_LIGHTSTEP( "LIGHTSTEP" ); static const trait_id trait_LOVES_BOOKS( "LOVES_BOOKS" ); -static const trait_id trait_LUPINE_FUR( "LUPINE_FUR" ); static const trait_id trait_M_IMMUNE( "M_IMMUNE" ); static const trait_id trait_M_SKIN3( "M_SKIN3" ); static const trait_id trait_MORE_PAIN( "MORE_PAIN" ); @@ -174,14 +167,12 @@ static const trait_id trait_QUILLS( "QUILLS" ); static const trait_id trait_SAPIOVORE( "SAPIOVORE" ); static const trait_id trait_SAVANT( "SAVANT" ); static const trait_id trait_SHELL2( "SHELL2" ); -static const trait_id trait_SLIMY( "SLIMY" ); static const trait_id trait_SPINES( "SPINES" ); static const trait_id trait_SPIRITUAL( "SPIRITUAL" ); static const trait_id trait_STRONGSTOMACH( "STRONGSTOMACH" ); static const trait_id trait_SUNLIGHT_DEPENDENT( "SUNLIGHT_DEPENDENT" ); static const trait_id trait_THORNS( "THORNS" ); static const trait_id trait_THRESH_SPIDER( "THRESH_SPIDER" ); -static const trait_id trait_URSINE_FUR( "URSINE_FUR" ); static const trait_id trait_VOMITOUS( "VOMITOUS" ); static const trait_id trait_WATERSLEEP( "WATERSLEEP" ); static const trait_id trait_WEAKSTOMACH( "WEAKSTOMACH" ); @@ -774,7 +765,7 @@ void player::pause() } }, true ); } else if( here.has_flag( "SWIMMABLE", pos() ) ) { - drench( 40, { { bodypart_str_id( "foot_l" ), bodypart_str_id( "foot_r" ), bodypart_str_id( "leg_l" ), bodypart_str_id( "leg_r" ) } }, + drench( 80, { { bodypart_str_id( "foot_l" ), bodypart_str_id( "foot_r" ), bodypart_str_id( "leg_l" ), bodypart_str_id( "leg_r" ) } }, false ); } } @@ -1722,73 +1713,6 @@ double player::vomit_mod() return mod; } -void player::update_body_wetness( const w_point &weather ) -{ - // Average number of turns to go from completely soaked to fully dry - // assuming average temperature and humidity - constexpr time_duration average_drying = 2_hours; - - // A modifier on drying time - double delay = 1.0; - // Weather slows down drying - delay += ( ( weather.humidity - 66 ) - ( weather.temperature - 65 ) ) / 100; - delay = std::max( 0.1, delay ); - // Fur/slime retains moisture - if( has_trait( trait_LIGHTFUR ) || has_trait( trait_FUR ) || has_trait( trait_FELINE_FUR ) || - has_trait( trait_LUPINE_FUR ) || has_trait( trait_CHITIN_FUR ) || has_trait( trait_CHITIN_FUR2 ) || - has_trait( trait_CHITIN_FUR3 ) ) { - delay = delay * 6 / 5; - } - if( has_trait( trait_URSINE_FUR ) || has_trait( trait_SLIMY ) ) { - delay *= 1.5; - } - - if( !x_in_y( 1, to_turns( average_drying * delay / 100.0 ) ) ) { - // No drying this turn - return; - } - - // Now per-body-part stuff - // To make drying uniform, make just one roll and reuse it - const int drying_roll = rng( 1, 80 ); - - for( const bodypart_id &bp : get_all_body_parts() ) { - if( get_part_wetness( bp ) == 0 ) { - continue; - } - // This is to normalize drying times - int drying_chance = get_part_drench_capacity( bp ); - const int temp_conv = get_part_temp_conv( bp ); - // Body temperature affects duration of wetness - // Note: Using temp_conv rather than temp_cur, to better approximate environment - if( temp_conv >= BODYTEMP_SCORCHING ) { - drying_chance *= 2; - } else if( temp_conv >= BODYTEMP_VERY_HOT ) { - drying_chance = drying_chance * 3 / 2; - } else if( temp_conv >= BODYTEMP_HOT ) { - drying_chance = drying_chance * 4 / 3; - } else if( temp_conv > BODYTEMP_COLD ) { - // Comfortable, doesn't need any changes - } else { - // Evaporation doesn't change that much at lower temp - drying_chance = drying_chance * 3 / 4; - } - - if( drying_chance < 1 ) { - drying_chance = 1; - } - - // TODO: Make evaporation reduce body heat - if( drying_chance >= drying_roll ) { - mod_part_wetness( bp, 1 ); - if( get_part_wetness( bp ) < 0 ) { - set_part_wetness( bp, 0 ); - } - } - } - // TODO: Make clothing slow down drying -} - void player::on_worn_item_transform( const item &old_it, const item &new_it ) { morale->on_worn_item_transform( old_it, new_it ); diff --git a/src/player.h b/src/player.h index 1e9866ee586cc..bce7578d1b0ad 100644 --- a/src/player.h +++ b/src/player.h @@ -155,9 +155,6 @@ class player : public Character /** Calculates the various speed bonuses we will get from mutations, etc. */ void recalc_speed_bonus(); - /** Maintains body wetness and handles the rate at which the player dries */ - void update_body_wetness( const w_point &weather ); - /** Returns true if the player has a conflicting trait to the entered trait * Uses has_opposite_trait(), has_lower_trait(), and has_higher_trait() to determine conflicts. */ diff --git a/src/suffer.cpp b/src/suffer.cpp index 5638080994987..7e740a854f205 100644 --- a/src/suffer.cpp +++ b/src/suffer.cpp @@ -1747,7 +1747,7 @@ void Character::drench( int saturation, const body_part_set &flags, bool ignore_ continue; } // Different sources will only make the bodypart wet to a limit - int source_wet_max = saturation * bp_wetness_max * 2 / 100; + int source_wet_max = saturation * bp_wetness_max / 100; int wetness_increment = ignore_waterproof ? 100 : 2; // Respect maximums const int wetness_max = std::min( source_wet_max, bp_wetness_max ); @@ -1757,7 +1757,7 @@ void Character::drench( int saturation, const body_part_set &flags, bool ignore_ } } const int torso_wetness = get_part_wetness( bodypart_id( "torso" ) ); - if( torso_wetness >= torso_wetness / 2.0 && + if( torso_wetness >= get_part_drench_capacity( bodypart_id( "torso" ) ) / 2.0 && has_effect( effect_masked_scent ) && get_value( "waterproof_scent" ).empty() ) { add_msg_if_player( m_info, _( "The water wash away the scent." ) ); diff --git a/tests/iuse_test.cpp b/tests/iuse_test.cpp index 1fa2119ea7df5..e6bf44a7ed2d4 100644 --- a/tests/iuse_test.cpp +++ b/tests/iuse_test.cpp @@ -339,10 +339,6 @@ TEST_CASE( "towel", "[iuse][towel]" ) REQUIRE( dummy.get_part_wetness( bodypart_id( "arm_l" ) ) > 0 ); REQUIRE( dummy.get_part_wetness( bodypart_id( "arm_r" ) ) > 0 ); - // FIXME: Morale alone is the trigger for drying off! - // Without the morale modifier, towel_common thinks you're dry - dummy.add_morale( MORALE_WET, -10, -10, 1_hours, 1_hours ); - WHEN( "they use a dry towel" ) { REQUIRE_FALSE( towel.has_flag( flag_WET ) ); dummy.invoke_item( &towel ); @@ -374,6 +370,8 @@ TEST_CASE( "towel", "[iuse][towel]" ) } GIVEN( "avatar has poor morale due to being wet" ) { + dummy.drench( 100, { bodypart_str_id( "torso" ), bodypart_str_id( "head" ), bodypart_str_id( "arm_l" ), bodypart_str_id( "arm_r" ) }, + false ); dummy.add_morale( MORALE_WET, -10, -10, 1_hours, 1_hours ); REQUIRE( dummy.has_morale( MORALE_WET ) == -10 );