Agent Perception: Restrict when idling agents respond to sounds.

- Prevent utterances and other sounds from causing undesired jitters and
  fleeing, such as those caused by greeting villagers.
- Agents will no longer flee from quieter weapon sounds such as melee.
This commit is contained in:
holychowders 2022-03-07 18:58:44 -06:00
parent 5ee6278860
commit df91f665d7
8 changed files with 20 additions and 33 deletions

View File

@ -21,9 +21,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Removed ### Removed
### Fixed ### Fixed
- Fixed bug that would sometimes cause taking a screenshot to panic because a buffer was mapped a the wrong time. - Fixed bug that would sometimes cause taking a screenshot to panic because a buffer was mapped at the wrong time.
- Players can no longer push waypoints around - Players can no longer push waypoints around
- Sites will now also be placed near the edge of the map - Sites will now also be placed near the edge of the map
- Fix a bug causing NPCs to jitter on interaction and randomly run away.
## [0.12.0] - 2022-02-19 ## [0.12.0] - 2022-02-19

View File

@ -347,13 +347,13 @@ impl Sound {
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub enum SoundKind { pub enum SoundKind {
Unknown, Unknown,
Utterance(UtteranceKind, Body),
Movement, Movement,
Melee, Melee,
Projectile, Projectile,
Explosion, Explosion,
Beam, Beam,
Shockwave, Shockwave,
Utterance(UtteranceKind, Body),
} }
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]

View File

@ -100,13 +100,12 @@ impl<'a> System<'a> for Sys {
let mut rng = thread_rng(); let mut rng = thread_rng();
if rng.gen_bool(0.005) { if rng.gen_bool(0.005) {
server_events.push(ServerEvent::Sound { server_events.push(ServerEvent::Sound {
sound: Sound::new(SoundKind::Beam, pos.0, 7.0, time), sound: Sound::new(SoundKind::Beam, pos.0, 13.0, time),
}); });
} }
// If beam segment is out of time emit destroy event but still continue since it // If beam segment is out of time emit destroy event but still continue since it
// may have traveled and produced effects a bit before reaching its // may have traveled and produced effects a bit before reaching its end point
// end point
if end_time < time { if end_time < time {
server_events.push(ServerEvent::Delete(entity)); server_events.push(ServerEvent::Delete(entity));
} }

View File

@ -73,7 +73,7 @@ impl<'a> System<'a> for Sys {
continue; continue;
} }
server_emitter.emit(ServerEvent::Sound { server_emitter.emit(ServerEvent::Sound {
sound: Sound::new(SoundKind::Melee, pos.0, 3.0, read_data.time.0), sound: Sound::new(SoundKind::Melee, pos.0, 2.0, read_data.time.0),
}); });
melee_attack.applied = true; melee_attack.applied = true;

View File

@ -81,7 +81,7 @@ impl<'a> System<'a> for Sys {
let mut rng = thread_rng(); let mut rng = thread_rng();
if physics.on_surface().is_none() && rng.gen_bool(0.05) { if physics.on_surface().is_none() && rng.gen_bool(0.05) {
server_emitter.emit(ServerEvent::Sound { server_emitter.emit(ServerEvent::Sound {
sound: Sound::new(SoundKind::Projectile, pos.0, 2.0, read_data.time.0), sound: Sound::new(SoundKind::Projectile, pos.0, 4.0, read_data.time.0),
}); });
} }

View File

@ -94,7 +94,7 @@ impl<'a> System<'a> for Sys {
let mut rng = thread_rng(); let mut rng = thread_rng();
if rng.gen_bool(0.05) { if rng.gen_bool(0.05) {
server_emitter.emit(ServerEvent::Sound { server_emitter.emit(ServerEvent::Sound {
sound: Sound::new(SoundKind::Shockwave, pos.0, 16.0, time), sound: Sound::new(SoundKind::Shockwave, pos.0, 40.0, time),
}); });
} }

View File

@ -597,7 +597,7 @@ pub fn handle_explosion(server: &Server, pos: Vec3<f32>, explosion: Explosion, o
.retrieve_entity_internal(uid.into()) .retrieve_entity_internal(uid.into())
}); });
let explosion_volume = 2.5 * explosion.radius; let explosion_volume = 6.25 * explosion.radius;
let mut emitter = server_eventbus.emitter(); let mut emitter = server_eventbus.emitter();
emitter.emit(ServerEvent::Sound { emitter.emit(ServerEvent::Sound {
sound: Sound::new(SoundKind::Explosion, pos, explosion_volume, time.0), sound: Sound::new(SoundKind::Explosion, pos, explosion_volume, time.0),

View File

@ -524,7 +524,7 @@ impl<'a> AgentData<'a> {
match sound { match sound {
Some(AgentEvent::ServerSound(sound)) => { Some(AgentEvent::ServerSound(sound)) => {
agent.sounds_heard.push(sound); agent.sounds_heard.push(sound);
agent.awareness += sound.vol; agent.awareness += sound.vol / 2.0;
}, },
Some(AgentEvent::Hurt) => { Some(AgentEvent::Hurt) => {
// Hurt utterances at random upon receiving damage // Hurt utterances at random upon receiving damage
@ -2165,36 +2165,23 @@ impl<'a> AgentData<'a> {
if let Some(agent_stats) = read_data.stats.get(*self.entity) { if let Some(agent_stats) = read_data.stats.get(*self.entity) {
let sound_pos = Pos(sound.pos); let sound_pos = Pos(sound.pos);
let dist_sqrd = self.pos.0.distance_squared(sound_pos.0); let dist_sqrd = self.pos.0.distance_squared(sound_pos.0);
let close_enough_to_react = dist_sqrd < 35.0_f32.powi(2);
let too_far_to_investigate = dist_sqrd > 10.0_f32.powi(2);
// FIXME: We need to be able to change the name of a guard without breaking this // FIXME: We need to be able to change the name of a guard without breaking this
// logic The `Mark` enum from common::agent could be used to // logic. The `Mark` enum from common::agent could be used to
// match with `agent::Mark::Guard` // match with `agent::Mark::Guard`
let is_village_guard = agent_stats.name == *"Guard".to_string(); let is_village_guard = agent_stats.name == *"Guard".to_string();
let is_enemy = matches!(self.alignment, Some(Alignment::Enemy)); let is_enemy = matches!(self.alignment, Some(Alignment::Enemy));
if is_enemy { let sound_was_loud = sound.vol >= 10.0;
let far_enough = dist_sqrd > 10.0_f32.powi(2); let sound_was_threatening = sound_was_loud
|| matches!(sound.kind, SoundKind::Utterance(UtteranceKind::Scream, _));
if far_enough { if (is_enemy || is_village_guard) && too_far_to_investigate {
self.follow(agent, controller, &read_data.terrain, &sound_pos);
} else {
// TODO: Change this to a search action instead of idle
self.idle(agent, controller, read_data, rng);
}
} else if is_village_guard {
self.follow(agent, controller, &read_data.terrain, &sound_pos); self.follow(agent, controller, &read_data.terrain, &sound_pos);
} else if !is_village_guard { } else if sound_was_threatening && close_enough_to_react {
let flee_health = agent.psyche.flee_health; self.flee(agent, controller, &read_data.terrain, &sound_pos);
let close_enough = dist_sqrd < 35.0_f32.powi(2);
let sound_was_loud = sound.vol >= 10.0;
if close_enough
&& (flee_health <= 0.7 || (flee_health <= 0.5 && sound_was_loud))
{
self.flee(agent, controller, &read_data.terrain, &sound_pos);
} else {
self.idle(agent, controller, read_data, rng);
}
} else { } else {
// TODO: Change this to a search action instead of idle // TODO: Change this to a search action instead of idle
self.idle(agent, controller, read_data, rng); self.idle(agent, controller, read_data, rng);
@ -2354,7 +2341,7 @@ impl<'a> AgentData<'a> {
sound: Sound::new( sound: Sound::new(
SoundKind::Utterance(UtteranceKind::Scream, *body), SoundKind::Utterance(UtteranceKind::Scream, *body),
self.pos.0, self.pos.0,
100.0, 13.0,
time, time,
), ),
}); });