diff --git a/src/monmove.cpp b/src/monmove.cpp index fb4e705e1c006..12cfb6709672d 100644 --- a/src/monmove.cpp +++ b/src/monmove.cpp @@ -999,7 +999,7 @@ void monster::move() continue; } // Don't bash if we're just tracking a noise. - if( wander() && destination == wander_pos ) { + if( !provocative_sound && wander() && destination == wander_pos ) { continue; } const int estimate = here.bash_rating( bash_estimate(), candidate ); diff --git a/src/monster.cpp b/src/monster.cpp index 89bf9a4009e7e..7b4e8d54f369a 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -2863,7 +2863,7 @@ float monster::get_mountable_weight_ratio() const return type->mountable_weight_ratio; } -void monster::hear_sound( const tripoint &source, const int vol, const int dist ) +void monster::hear_sound( const tripoint &source, const int vol, const int dist, bool provocative ) { if( !can_hear() ) { return; @@ -2876,6 +2876,12 @@ void monster::hear_sound( const tripoint &source, const int vol, const int dist return; } + int tmp_provocative = provocative || volume >= normal_roll( 30, 5 ); + // already following a more interesting sound + if( provocative_sound && !tmp_provocative && wandf > 0 ) { + return; + } + int max_error = 0; if( volume < 2 ) { max_error = 10; @@ -2892,7 +2898,12 @@ void monster::hear_sound( const tripoint &source, const int vol, const int dist // target_z will require some special check due to soil muffling sounds int wander_turns = volume * ( goodhearing ? 6 : 1 ); + // again, already following a more interesting sound + if( wander_turns < wandf ) { + return; + } process_trigger( mon_trigger::SOUND, volume ); + provocative_sound = tmp_provocative; if( morale >= 0 && anger >= 10 ) { // TODO: Add a proper check for fleeing attitude // but cache it nicely, because this part is called a lot diff --git a/src/monster.h b/src/monster.h index b6df1e87a7f64..d8bf775bb61a5 100644 --- a/src/monster.h +++ b/src/monster.h @@ -430,7 +430,7 @@ class monster : public Creature * @param vol Volume at the center of the sound source * @param distance Distance to sound source (currently just rl_dist) */ - void hear_sound( const tripoint &source, int vol, int distance ); + void hear_sound( const tripoint &source, int vol, int distance, bool provocative ); bool is_hallucination() const override; // true if the monster isn't actually real @@ -447,6 +447,7 @@ class monster : public Creature const std::string &npc_msg ) const override; // TEMP VALUES tripoint wander_pos; // Wander destination - Just try to move in that direction + bool provocative_sound = false; // Are we wandering toward something we think is alive? int wandf = 0; // Urge to wander - Increased by sound, decrements each move std::vector inv; // Inventory Character *mounted_player = nullptr; // player that is mounting this creature diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index dfa03d52e5592..751b7c717a3db 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -1955,6 +1955,7 @@ void monster::load( const JsonObject &data ) position.z = get_map().get_abs_sub().z; } + data.read( "provocative_sound", provocative_sound ); data.read( "wandf", wandf ); data.read( "wandx", wander_pos.x ); data.read( "wandy", wander_pos.y ); @@ -2110,6 +2111,7 @@ void monster::store( JsonOut &json ) const json.member( "wandx", wander_pos.x ); json.member( "wandy", wander_pos.y ); json.member( "wandz", wander_pos.z ); + json.member( "provocative_sound", provocative_sound ); json.member( "wandf", wandf ); json.member( "hp", hp ); json.member( "special_attacks", special_attacks ); diff --git a/src/sounds.cpp b/src/sounds.cpp index d9036b397fb67..3ace8e3baa78f 100644 --- a/src/sounds.cpp +++ b/src/sounds.cpp @@ -82,6 +82,11 @@ static const itype_id fuel_type_battery( "battery" ); static const itype_id itype_weapon_fire_suppressed( "weapon_fire_suppressed" ); +struct monster_sound_event { + int volume; + bool provocative; +}; + struct sound_event { int volume; sounds::sound_t category; @@ -99,6 +104,7 @@ struct centroid { float z; float volume; float weight; + bool provocative; }; namespace io @@ -130,7 +136,7 @@ std::string enum_to_string( sounds::sound_t data ) // Static globals tracking sounds events of various kinds. // The sound events since the last monster turn. -static std::vector> recent_sounds; +static std::vector> recent_sounds; // The sound events since the last interactive player turn. (doesn't count sleep etc) static std::vector> sounds_since_last_turn; // The sound events currently displayed to the player. @@ -160,6 +166,30 @@ static int sound_distance( const tripoint &source, const tripoint &sink ) return rl_dist( source.xy(), sink.xy() ) + vertical_attenuation; } +static bool is_provocative( sounds::sound_t category ) +{ + switch( category ) { + case sounds::sound_t::background: + case sounds::sound_t::weather: + case sounds::sound_t::music: + case sounds::sound_t::activity: + case sounds::sound_t::destructive_activity: + case sounds::sound_t::alarm: + case sounds::sound_t::combat: + return false; + case sounds::sound_t::movement: + case sounds::sound_t::speech: + case sounds::sound_t::electronic_speech: + case sounds::sound_t::alert: + case sounds::sound_t::order: + return true; + case sounds::sound_t::_LAST: + break; + } + debugmsg( "Invalid sound_t category" ); + abort(); +} + void sounds::ambient_sound( const tripoint &p, int vol, sound_t category, const std::string &description ) { @@ -178,7 +208,7 @@ void sounds::sound( const tripoint &p, int vol, sound_t category, const std::str if( description.empty() ) { debugmsg( "Sound at %d:%d has no description!", p.x, p.y ); } - recent_sounds.emplace_back( std::make_pair( p, vol ) ); + recent_sounds.emplace_back( std::make_pair( p, monster_sound_event{ vol, is_provocative( category ) } ) ); sounds_since_last_turn.emplace_back( std::make_pair( p, sound_event {vol, category, description, ambient, false, id, variant} ) ); @@ -208,7 +238,8 @@ static void vector_quick_remove( std::vector &source, int index ) source.pop_back(); } -static std::vector cluster_sounds( std::vector> input_sounds ) +static std::vector cluster_sounds( std::vector> + input_sounds ) { // If there are too many monsters and too many noise sources (which can be monsters, go figure), // applying sound events to monsters can dominate processing time for the whole game, @@ -233,7 +264,8 @@ static std::vector cluster_sounds( std::vector( input_sounds[index].first.x ), static_cast( input_sounds[index].first.y ), static_cast( input_sounds[index].first.z ), - static_cast( input_sounds[index].second ), static_cast( input_sounds[index].second ) + static_cast( input_sounds[index].second.volume ), static_cast( input_sounds[index].second.volume ), + input_sounds[index].second.provocative } ); vector_quick_remove( input_sounds, index ); } @@ -251,19 +283,25 @@ static std::vector cluster_sounds( std::vector( sound_event_pair.second ) + found_centroid->weight; + const float volume_sum = static_cast( sound_event_pair.second.volume ) + + found_centroid->weight; // Set the centroid location to the average of the two locations, weighted by volume. - found_centroid->x = static_cast( ( sound_event_pair.first.x * sound_event_pair.second ) + + found_centroid->x = static_cast( ( sound_event_pair.first.x * + sound_event_pair.second.volume ) + ( found_centroid->x * found_centroid->weight ) ) / volume_sum; - found_centroid->y = static_cast( ( sound_event_pair.first.y * sound_event_pair.second ) + + found_centroid->y = static_cast( ( sound_event_pair.first.y * + sound_event_pair.second.volume ) + ( found_centroid->y * found_centroid->weight ) ) / volume_sum; - found_centroid->z = static_cast( ( sound_event_pair.first.z * sound_event_pair.second ) + + found_centroid->z = static_cast( ( sound_event_pair.first.z * + sound_event_pair.second.volume ) + ( found_centroid->z * found_centroid->weight ) ) / volume_sum; // Set the centroid volume to the larger of the volumes. found_centroid->volume = std::max( found_centroid->volume, - static_cast( sound_event_pair.second ) ); + static_cast( sound_event_pair.second.volume ) ); // Set the centroid weight to the sum of the weights. found_centroid->weight = volume_sum; + // Set and keep provocative if any sound in the centroid is provocative + found_centroid->provocative |= sound_event_pair.second.provocative; } return sound_clusters; } @@ -320,7 +358,7 @@ void sounds::process_sounds() const int dist = sound_distance( source, critter.pos() ); if( vol * 2 > dist ) { // Exclude monsters that certainly won't hear the sound - critter.hear_sound( source, vol, dist ); + critter.hear_sound( source, vol, dist, this_centroid.provocative ); } } }