(Buggy) Sounds are investigated; choose_target is used for guards to target villager attackers

Bug: The additional code in choose_target does not seem to function.
This commit is contained in:
holychowders 2021-05-23 15:28:58 -07:00 committed by Avi Weinstock
parent 476be41b9d
commit c2239daaa0
8 changed files with 125 additions and 104 deletions

View File

@ -272,23 +272,15 @@ pub struct Sound {
pub pos: Vec3<f32>,
pub vol: f32,
pub time: f64,
pub owner: Option<EcsEntity>,
}
impl Sound {
pub fn new(
kind: SoundKind,
pos: Vec3<f32>,
vol: f32,
time: f64,
owner: Option<EcsEntity>,
) -> Self {
pub fn new(kind: SoundKind, pos: Vec3<f32>, vol: f32, time: f64) -> Self {
Sound {
kind,
pos,
vol,
time,
owner,
}
}

View File

@ -98,7 +98,7 @@ impl<'a> System<'a> for Sys {
let mut rng = thread_rng();
if rng.gen_bool(0.005) {
server_events.push(ServerEvent::Sound {
sound: Sound::new(SoundKind::Beam, pos.0, 7.0, time, beam_owner),
sound: Sound::new(SoundKind::Beam, pos.0, 7.0, time),
});
}

View File

@ -99,7 +99,6 @@ impl<'a> System<'a> for Sys {
pos.0 + Vec3::unit_z() * body.eye_height(),
8.0, // TODO: Come up with a better way of determining this
1.0,
Some(entity),
);
server_emitter.emit(ServerEvent::Sound { sound });
}

View File

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

View File

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

View File

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

View File

@ -699,13 +699,7 @@ pub fn handle_explosion(server: &Server, pos: Vec3<f32>, explosion: Explosion, o
let explosion_volume = 2.5 * explosion.radius;
server_eventbus.emit_now(ServerEvent::Sound {
sound: Sound::new(
SoundKind::Explosion,
pos,
explosion_volume,
time.0,
owner_entity,
),
sound: Sound::new(SoundKind::Explosion, pos, explosion_volume, time.0),
});
// Add an outcome

View File

@ -400,8 +400,8 @@ impl<'a> System<'a> for Sys {
&read_data.terrain,
tgt_pos,
);
} else if target_was_attacked(target, &read_data) {
data.attack_targets_attacker(
} else if entity_was_attacked(target, &read_data) {
data.attack_agents_attacker(
agent, &read_data, controller,
);
// Follow owner if too far away and not
@ -445,15 +445,13 @@ impl<'a> System<'a> for Sys {
// have a health component
match health {
Some(health) if health.last_change.0 < DAMAGE_MEMORY_DURATION => {
// TODO: Can most of this match be replaced by the function
// `attack_agents_attacker`?
if let comp::HealthSource::Damage { by: Some(by), .. } =
health.last_change.1.cause
{
if let Some(attacker) =
read_data.uid_allocator.retrieve_entity_internal(by.id())
{
if let Some(attacker) = get_entity_by_id(by.id(), &read_data) {
if let Some(tgt_pos) = read_data.positions.get(attacker) {
// If the target is dead or in a safezone, remove the
// target and idle.
if should_stop_attacking(attacker, &read_data) {
agent.target = None;
data.idle_tree(
@ -485,17 +483,15 @@ impl<'a> System<'a> for Sys {
.rtsim_entity
.and_then(|_| read_data.stats.get(attacker))
{
agent.rtsim_controller.events.push(
RtSimEvent::AddMemory(Memory {
item: MemoryItem::CharacterFight {
name: tgt_stats.name.clone(),
},
time_to_forget: read_data.time.0
+ 300.0,
}),
if data.rtsim_entity.is_some() {
remember_fight(
agent,
tgt_stats.name.clone(),
read_data.time.0,
);
}
}
}
} else {
agent.target = None;
data.idle_tree(
@ -561,7 +557,7 @@ impl<'a> AgentData<'a> {
// Set owner if no target
if agent.target.is_none() && thread_rng().gen_bool(0.1) {
if let Some(Alignment::Owned(owner)) = self.alignment {
if let Some(owner) = read_data.uid_allocator.retrieve_entity_internal(owner.id()) {
if let Some(owner) = get_entity_by_id(owner.id(), read_data) {
agent.target = build_target(owner, false, read_data.time.0);
}
}
@ -947,8 +943,7 @@ impl<'a> AgentData<'a> {
match msg {
Some(AgentEvent::Talk(by, subject)) => {
if can_speak(agent) {
if let Some(target) = read_data.uid_allocator.retrieve_entity_internal(by.id())
{
if let Some(target) = get_entity_by_id(by.id(), read_data) {
agent.target = build_target(target, false, read_data.time.0);
if self.look_toward(controller, read_data, &target) {
@ -1135,9 +1130,7 @@ impl<'a> AgentData<'a> {
// stand still and looking towards the trading player
controller.actions.push(ControlAction::Stand);
controller.actions.push(ControlAction::Talk);
if let Some(target) =
read_data.uid_allocator.retrieve_entity_internal(with.id())
{
if let Some(target) = get_entity_by_id(with.id(), read_data) {
agent.target = build_target(target, false, read_data.time.0);
}
controller
@ -1163,9 +1156,7 @@ impl<'a> AgentData<'a> {
},
Some(AgentEvent::TradeAccepted(with)) => {
if !agent.behavior.is(BehaviorState::TRADING) {
if let Some(target) =
read_data.uid_allocator.retrieve_entity_internal(with.id())
{
if let Some(target) = get_entity_by_id(with.id(), read_data) {
agent.target = build_target(target, false, read_data.time.0);
}
agent.behavior.set(BehaviorState::TRADING);
@ -1384,6 +1375,17 @@ impl<'a> AgentData<'a> {
agent.action_state.timer = 0.0;
// Search area
// TODO: REMOVE THIS BEFORE MERGE
if let Some(agent_stats) = read_data.stats.get(*self.entity) {
let is_village_guard = agent_stats.name == *"Guard".to_string();
if is_village_guard {
self.chat_general(
"I am a guard, searching for target".to_string(),
event_emitter,
);
}
}
let target = self.cached_spatial_grid.0
.in_circle_aabr(self.pos.0.xy(), SEARCH_DIST)
.filter_map(|entity| {
@ -1404,11 +1406,7 @@ impl<'a> AgentData<'a> {
search_dist *= SNEAK_COEFFICIENT;
listen_dist *= SNEAK_COEFFICIENT;
}
((e_pos.0.distance_squared(self.pos.0) < search_dist.powi(2) &&
// Within our view
(e_pos.0 - self.pos.0).try_normalized().map(|v| v.dot(*controller.inputs.look_dir) > 0.15).unwrap_or(true))
// Within listen distance
|| e_pos.0.distance_squared(self.pos.0) < listen_dist.powi(2)) // TODO implement proper sound system for agents
((self.within_range_of(search_dist, e_pos.0) && self.within_view_angle(e_pos.0, controller)) || self.within_range_of(listen_dist, e_pos.0)) // TODO implement proper sound system for agents
&& e != self.entity
&& !e_health.is_dead
&& !invulnerability_is_in_buffs(read_data.buffs.get(*e))
@ -1416,12 +1414,7 @@ impl<'a> AgentData<'a> {
if let Some(rtsim_entity) = &self.rtsim_entity {
if can_speak(agent) {
if rtsim_entity.brain.remembers_fight_with_character(&e_stats.name) {
agent.rtsim_controller.events.push(
RtSimEvent::AddMemory(Memory {
item: MemoryItem::CharacterFight { name: e_stats.name.clone() },
time_to_forget: read_data.time.0 + 300.0,
})
);
remember_fight(agent, e_stats.name.clone(), read_data.time.0);
let msg = format!("{}! How dare you cross me again!", e_stats.name.clone());
self.chat_general(msg, event_emitter);
true
@ -1440,12 +1433,7 @@ impl<'a> AgentData<'a> {
if matches!(alignment, Alignment::Npc) && e_inventory.equipped_items().filter(|item| item.tags().contains(&ItemTag::Cultist)).count() > 2 {
if can_speak(agent) {
if self.rtsim_entity.is_some() {
agent.rtsim_controller.events.push(
RtSimEvent::AddMemory(Memory {
item: MemoryItem::CharacterFight { name: e_stats.name.clone() },
time_to_forget: read_data.time.0 + 300.0,
})
);
remember_fight(agent, e_stats.name.clone(), read_data.time.0);
}
let msg = "npc.speech.villager_cultist_alarm".to_string();
self.chat_general(msg, event_emitter);
@ -1459,14 +1447,57 @@ impl<'a> AgentData<'a> {
))
})
.filter_map(|(e, e_pos, e_health, _e_stats, _e_inventory, e_alignment, _char_state)| {
// TODO: REMOVE THIS BEFORE MERGE
if let Some(agent_stats) = read_data.stats.get(*self.entity) {
let is_village_guard = agent_stats.name == *"Guard".to_string();
if is_village_guard {
println!("{}", "I am a guard, made it to the filter_map".to_string());
}
}
if entity_was_attacked(e, &read_data) {
if let Some(alignment) = e_alignment {
let is_npc = matches!(alignment, Alignment::Npc);
if is_npc {
if let comp::HealthSource::Damage { by: Some(by), .. } = e_health.last_change.1.cause {
get_entity_by_id(by.id(), read_data)
.and_then(|attacker| {
println!("attacker data: {:?}", Some(attacker).zip(read_data.positions.get(attacker)));
Some(attacker).zip(read_data.positions.get(attacker))
})
} else {
Some(e).zip(Some(e_pos))
}
} else {
Some(e).zip(Some(e_pos))
}
} else {
Some(e).zip(Some(e_pos))
}
} else {
Some(e).zip(Some(e_pos))
}
})
// Can we even see them?
.filter(|(_, e_pos, _, _, _, _, _)| read_data.terrain
.filter(|(_e, e_pos)| {
// TODO: REMOVE THIS BEFORE MERGE
if let Some(agent_stats) = read_data.stats.get(*self.entity) {
let is_village_guard = agent_stats.name == *"Guard".to_string();
if is_village_guard {
println!("looking at: {:?} at {:?}", _e, e_pos);
println!("can we see them: {:?}", read_data.terrain.ray(self.pos.0 + Vec3::unit_z(), e_pos.0 + Vec3::unit_z()).until(Block::is_opaque).cast().0 >= e_pos.0.distance(self.pos.0));
}
}
read_data.terrain
.ray(self.pos.0 + Vec3::unit_z(), e_pos.0 + Vec3::unit_z())
.until(Block::is_opaque)
.cast()
.0 >= e_pos.0.distance(self.pos.0))
.min_by_key(|(_, e_pos, _, _, _, _, _)| (e_pos.0.distance_squared(self.pos.0) * 100.0) as i32) // TODO choose target by more than just distance
.map(|(e, _, _, _, _, _, _)| e);
.0 >= e_pos.0.distance(self.pos.0)
})
.min_by_key(|(_e, e_pos)| (e_pos.0.distance_squared(self.pos.0) * 100.0) as i32) // TODO choose target by more than just distance
.map(|(e, _e_pos)| e);
if agent.target.is_none() && target.is_some() {
controller.push_event(ControlEvent::Utterance(UtteranceKind::Angry));
@ -1477,6 +1508,14 @@ impl<'a> AgentData<'a> {
hostile: true,
selected_at: read_data.time.0,
});
// TODO: REMOVE THIS BEFORE MERGE
if let Some(agent_stats) = read_data.stats.get(*self.entity) {
let is_village_guard = agent_stats.name == *"Guard".to_string();
if is_village_guard {
println!("guard's target is: {:?}", agent.target);
}
}
}
fn attack(
@ -3845,12 +3884,7 @@ impl<'a> AgentData<'a> {
let sound_is_villager_alarm = matches!(sound.kind, SoundKind::VillagerAlarm);
if sound_is_villager_alarm {
if let Some(alarmist) = sound.owner {
agent.target = build_target(alarmist, true, read_data.time.0);
self.attack_targets_attacker(agent, &read_data, controller);
} else {
self.follow(agent, controller, &read_data.terrain, &sound_pos);
}
} else {
self.idle(agent, controller, &read_data);
}
@ -3862,7 +3896,7 @@ impl<'a> AgentData<'a> {
}
}
fn attack_targets_attacker(
fn attack_agents_attacker(
&self,
agent: &mut Agent,
read_data: &ReadData,
@ -3873,9 +3907,7 @@ impl<'a> AgentData<'a> {
if let comp::HealthSource::Damage { by: Some(by), .. } =
tgt_health.last_change.1.cause
{
if let Some(attacker) =
read_data.uid_allocator.retrieve_entity_internal(by.id())
{
if let Some(attacker) = get_entity_by_id(by.id(), read_data) {
if agent.target.is_none() {
controller.push_event(ControlEvent::Utterance(UtteranceKind::Angry));
}
@ -3976,15 +4008,20 @@ impl<'a> AgentData<'a> {
fn emit_villager_alarm(&self, time: f64, event_emitter: &mut Emitter<'_, ServerEvent>) {
event_emitter.emit(ServerEvent::Sound {
sound: Sound::new(
SoundKind::VillagerAlarm,
self.pos.0,
100.0,
time,
Some(*self.entity),
),
sound: Sound::new(SoundKind::VillagerAlarm, self.pos.0, 100.0, time),
});
}
fn within_view_angle(&self, tgt_pos: Vec3<f32>, controller: &Controller) -> bool {
(tgt_pos - self.pos.0)
.try_normalized()
.map(|v| v.dot(*controller.inputs.look_dir) > 0.15)
.unwrap_or(true)
}
fn within_range_of(&self, range: f32, their_pos: Vec3<f32>) -> bool {
their_pos.distance_squared(self.pos.0) < range.powi(2)
}
}
fn can_see_tgt(terrain: &TerrainGrid, pos: &Pos, tgt_pos: &Pos, dist_sqrd: f32) -> bool {
@ -4017,10 +4054,7 @@ fn try_owner_alignment<'a>(
read_data: &'a ReadData,
) -> Option<&'a Alignment> {
if let Some(Alignment::Owned(owner_uid)) = alignment {
if let Some(owner) = read_data
.uid_allocator
.retrieve_entity_internal(owner_uid.id())
{
if let Some(owner) = get_entity_by_id(owner_uid.id(), read_data) {
return read_data.alignments.get(owner);
}
}
@ -4079,9 +4113,9 @@ fn decrement_awareness(agent: &mut Agent) {
agent.awareness -= decrement;
}
fn target_was_attacked(target: EcsEntity, read_data: &ReadData) -> bool {
if let Some(tgt_health) = read_data.healths.get(target) {
tgt_health.last_change.0 < 5.0 && tgt_health.last_change.1.amount < 0
fn entity_was_attacked(entity: EcsEntity, read_data: &ReadData) -> bool {
if let Some(entity_health) = read_data.healths.get(entity) {
entity_health.last_change.0 < 5.0 && entity_health.last_change.1.amount < 0
} else {
false
}
@ -4108,3 +4142,17 @@ fn build_target_data<'a>(
}
fn can_speak(agent: &Agent) -> bool { agent.behavior.can(BehaviorCapability::SPEAK) }
fn remember_fight(agent: &mut Agent, name: String, time: f64) {
agent
.rtsim_controller
.events
.push(RtSimEvent::AddMemory(Memory {
item: MemoryItem::CharacterFight { name },
time_to_forget: time + 300.0,
}));
}
fn get_entity_by_id(id: u64, read_data: &ReadData) -> Option<EcsEntity> {
read_data.uid_allocator.retrieve_entity_internal(id)
}