Have line-of-sight checks explicitly account for eye-height for entities.

This commit is contained in:
holychowders 2022-04-21 20:32:56 -05:00
parent dda85e4bc3
commit 995504be26
3 changed files with 118 additions and 85 deletions

View File

@ -1990,18 +1990,12 @@ impl<'a> AgentData<'a> {
tgt_data, tgt_data,
read_data, read_data,
), ),
Tactic::Turret => { Tactic::Turret => self.handle_turret_attack(agent, controller, &attack_data, read_data),
self.handle_turret_attack(agent, controller, &attack_data, tgt_data, read_data) Tactic::FixedTurret => {
self.handle_fixed_turret_attack(agent, controller, &attack_data, read_data)
}, },
Tactic::FixedTurret => self.handle_fixed_turret_attack(
agent,
controller,
&attack_data,
tgt_data,
read_data,
),
Tactic::RotatingTurret => { Tactic::RotatingTurret => {
self.handle_rotating_turret_attack(agent, controller, tgt_data, read_data) self.handle_rotating_turret_attack(agent, controller, read_data)
}, },
Tactic::Mindflayer => self.handle_mindflayer_attack( Tactic::Mindflayer => self.handle_mindflayer_attack(
agent, agent,
@ -2048,13 +2042,7 @@ impl<'a> AgentData<'a> {
tgt_data, tgt_data,
read_data, read_data,
), ),
Tactic::RadialTurret => self.handle_radial_turret_attack( Tactic::RadialTurret => self.handle_radial_turret_attack(controller),
agent,
controller,
&attack_data,
tgt_data,
read_data,
),
Tactic::Yeti => { Tactic::Yeti => {
self.handle_yeti_attack(agent, controller, &attack_data, tgt_data, read_data) self.handle_yeti_attack(agent, controller, &attack_data, tgt_data, read_data)
}, },

View File

@ -1,5 +1,5 @@
use crate::sys::agent::{ use crate::sys::agent::{
consts::MAX_PATH_DIST, data::Path, util::positions_have_line_of_sight, AgentData, AttackData, consts::MAX_PATH_DIST, data::Path, util::entities_have_line_of_sight, AgentData, AttackData,
ReadData, TargetData, ReadData, TargetData,
}; };
use common::{ use common::{
@ -130,11 +130,15 @@ impl<'a> AgentData<'a> {
tgt_data: &TargetData, tgt_data: &TargetData,
read_data: &ReadData, read_data: &ReadData,
) { ) {
let line_of_sight_with_target = agent.target.map_or(false, |target| {
entities_have_line_of_sight(*self.entity, target.target, read_data)
});
let elevation = self.pos.0.z - tgt_data.pos.0.z; let elevation = self.pos.0.z - tgt_data.pos.0.z;
const PREF_DIST: f32 = 30_f32; const PREF_DIST: f32 = 30_f32;
if attack_data.angle_xy < 30.0 if attack_data.angle_xy < 30.0
&& (elevation > 10.0 || attack_data.dist_sqrd > PREF_DIST.powi(2)) && (elevation > 10.0 || attack_data.dist_sqrd > PREF_DIST.powi(2))
&& positions_have_line_of_sight(self.pos, tgt_data.pos, read_data) && line_of_sight_with_target
{ {
controller.push_basic_input(InputKind::Primary); controller.push_basic_input(InputKind::Primary);
} else if attack_data.dist_sqrd < (PREF_DIST / 2.).powi(2) { } else if attack_data.dist_sqrd < (PREF_DIST / 2.).powi(2) {
@ -181,7 +185,7 @@ impl<'a> AgentData<'a> {
..self.traversal_config ..self.traversal_config
}, },
) { ) {
if positions_have_line_of_sight(self.pos, tgt_data.pos, read_data) { if line_of_sight_with_target {
controller.push_basic_input(InputKind::Primary); controller.push_basic_input(InputKind::Primary);
} }
controller.inputs.move_dir = controller.inputs.move_dir =
@ -209,12 +213,11 @@ impl<'a> AgentData<'a> {
rng: &mut impl Rng, rng: &mut impl Rng,
) { ) {
let has_leap = || self.skill_set.has_skill(Skill::Axe(AxeSkill::UnlockLeap)); let has_leap = || self.skill_set.has_skill(Skill::Axe(AxeSkill::UnlockLeap));
let has_energy = |need| self.energy.current() > need; let has_energy = |need| self.energy.current() > need;
let use_leap = |controller: &mut Controller| { let use_leap = |controller: &mut Controller| {
controller.push_basic_input(InputKind::Ability(0)); controller.push_basic_input(InputKind::Ability(0));
}; };
if attack_data.in_min_range() && attack_data.angle < 45.0 { if attack_data.in_min_range() && attack_data.angle < 45.0 {
controller.inputs.move_dir = Vec2::zero(); controller.inputs.move_dir = Vec2::zero();
if agent.action_state.timer > 5.0 { if agent.action_state.timer > 5.0 {
@ -243,7 +246,9 @@ impl<'a> AgentData<'a> {
if attack_data.dist_sqrd < 32.0f32.powi(2) if attack_data.dist_sqrd < 32.0f32.powi(2)
&& has_leap() && has_leap()
&& has_energy(50.0) && has_energy(50.0)
&& positions_have_line_of_sight(self.pos, tgt_data.pos, read_data) && agent.target.map_or(false, |target| {
entities_have_line_of_sight(*self.entity, target.target, read_data)
})
{ {
use_leap(controller); use_leap(controller);
} }
@ -304,7 +309,9 @@ impl<'a> AgentData<'a> {
if attack_data.dist_sqrd < 32.0f32.powi(2) if attack_data.dist_sqrd < 32.0f32.powi(2)
&& has_leap() && has_leap()
&& has_energy(50.0) && has_energy(50.0)
&& positions_have_line_of_sight(self.pos, tgt_data.pos, read_data) && agent.target.map_or(false, |target| {
entities_have_line_of_sight(*self.entity, target.target, read_data)
})
{ {
use_leap(controller); use_leap(controller);
} }
@ -350,8 +357,9 @@ impl<'a> AgentData<'a> {
read_data, read_data,
Path::Separate, Path::Separate,
None, None,
) && positions_have_line_of_sight(self.pos, tgt_data.pos, read_data) ) && agent.target.map_or(false, |target| {
{ entities_have_line_of_sight(*self.entity, target.target, read_data)
}) {
if agent.action_state.timer > 4.0 && attack_data.angle < 45.0 { if agent.action_state.timer > 4.0 && attack_data.angle < 45.0 {
controller.push_basic_input(InputKind::Secondary); controller.push_basic_input(InputKind::Secondary);
agent.action_state.timer = 0.0; agent.action_state.timer = 0.0;
@ -389,6 +397,11 @@ impl<'a> AgentData<'a> {
const MIN_CHARGE_FRAC: f32 = 0.5; const MIN_CHARGE_FRAC: f32 = 0.5;
const OPTIMAL_TARGET_VELOCITY: f32 = 5.0; const OPTIMAL_TARGET_VELOCITY: f32 = 5.0;
const DESIRED_ENERGY_LEVEL: f32 = 50.0; const DESIRED_ENERGY_LEVEL: f32 = 50.0;
let line_of_sight_with_target = agent.target.map_or(false, |target| {
entities_have_line_of_sight(*self.entity, target.target, read_data)
});
// Logic to use abilities // Logic to use abilities
if let CharacterState::ChargedRanged(c) = self.char_state { if let CharacterState::ChargedRanged(c) = self.char_state {
if !matches!(c.stage_section, StageSection::Recover) { if !matches!(c.stage_section, StageSection::Recover) {
@ -413,7 +426,7 @@ impl<'a> AgentData<'a> {
// If in repeater ranged, have enough energy, and aren't in recovery, try to // If in repeater ranged, have enough energy, and aren't in recovery, try to
// keep firing // keep firing
if attack_data.dist_sqrd > attack_data.min_attack_dist.powi(2) if attack_data.dist_sqrd > attack_data.min_attack_dist.powi(2)
&& positions_have_line_of_sight(self.pos, tgt_data.pos, read_data) && line_of_sight_with_target
{ {
// Only keep firing if not in melee range or if can see target // Only keep firing if not in melee range or if can see target
controller.push_basic_input(InputKind::Secondary); controller.push_basic_input(InputKind::Secondary);
@ -447,9 +460,7 @@ impl<'a> AgentData<'a> {
controller.push_basic_input(InputKind::Primary); controller.push_basic_input(InputKind::Primary);
} }
} }
} else if attack_data.dist_sqrd < MAX_PATH_DIST.powi(2) } else if attack_data.dist_sqrd < MAX_PATH_DIST.powi(2) && line_of_sight_with_target {
&& positions_have_line_of_sight(self.pos, tgt_data.pos, read_data)
{
// If not really far, and can see target, attempt to shoot bow // If not really far, and can see target, attempt to shoot bow
if self.energy.current() < DESIRED_ENERGY_LEVEL { if self.energy.current() < DESIRED_ENERGY_LEVEL {
// If low on energy, use primary to attempt to regen energy // If low on energy, use primary to attempt to regen energy
@ -488,9 +499,7 @@ impl<'a> AgentData<'a> {
..self.traversal_config ..self.traversal_config
}, },
) { ) {
if positions_have_line_of_sight(self.pos, tgt_data.pos, read_data) if line_of_sight_with_target && attack_data.angle < 45.0 {
&& attack_data.angle < 45.0
{
controller.inputs.move_dir = bearing controller.inputs.move_dir = bearing
.xy() .xy()
.rotated_z(rng.gen_range(0.5..1.57)) .rotated_z(rng.gen_range(0.5..1.57))
@ -622,8 +631,9 @@ impl<'a> AgentData<'a> {
..self.traversal_config ..self.traversal_config
}, },
) { ) {
if positions_have_line_of_sight(self.pos, tgt_data.pos, read_data) if agent.target.map_or(false, |target| {
&& attack_data.angle < 45.0 entities_have_line_of_sight(*self.entity, target.target, read_data)
}) && attack_data.angle < 45.0
{ {
controller.inputs.move_dir = bearing controller.inputs.move_dir = bearing
.xy() .xy()
@ -671,9 +681,13 @@ impl<'a> AgentData<'a> {
) { ) {
const DESIRED_ENERGY_LEVEL: f32 = 50.0; const DESIRED_ENERGY_LEVEL: f32 = 50.0;
const DESIRED_COMBO_LEVEL: u32 = 8; const DESIRED_COMBO_LEVEL: u32 = 8;
let line_of_sight_with_target = agent.target.map_or(false, |target| {
entities_have_line_of_sight(*self.entity, target.target, read_data)
});
// Logic to use abilities // Logic to use abilities
if attack_data.dist_sqrd > attack_data.min_attack_dist.powi(2) if attack_data.dist_sqrd > attack_data.min_attack_dist.powi(2) && line_of_sight_with_target
&& positions_have_line_of_sight(self.pos, tgt_data.pos, read_data)
{ {
// If far enough away, and can see target, check which skill is appropriate to // If far enough away, and can see target, check which skill is appropriate to
// use // use
@ -751,9 +765,7 @@ impl<'a> AgentData<'a> {
..self.traversal_config ..self.traversal_config
}, },
) { ) {
if positions_have_line_of_sight(self.pos, tgt_data.pos, read_data) if line_of_sight_with_target && attack_data.angle < 45.0 {
&& attack_data.angle < 45.0
{
controller.inputs.move_dir = bearing controller.inputs.move_dir = bearing
.xy() .xy()
.rotated_z(rng.gen_range(0.5..1.57)) .rotated_z(rng.gen_range(0.5..1.57))
@ -812,8 +824,9 @@ impl<'a> AgentData<'a> {
read_data, read_data,
Path::Separate, Path::Separate,
None, None,
) && positions_have_line_of_sight(self.pos, tgt_data.pos, read_data) ) && agent.target.map_or(false, |target| {
&& attack_data.angle < 90.0 entities_have_line_of_sight(*self.entity, target.target, read_data)
}) && attack_data.angle < 90.0
{ {
if agent.action_state.timer > 5.0 { if agent.action_state.timer > 5.0 {
controller.push_basic_input(InputKind::Secondary); controller.push_basic_input(InputKind::Secondary);
@ -951,7 +964,9 @@ impl<'a> AgentData<'a> {
}, },
) { ) {
if attack_data.angle < 15.0 if attack_data.angle < 15.0
&& positions_have_line_of_sight(self.pos, tgt_data.pos, read_data) && agent.target.map_or(false, |target| {
entities_have_line_of_sight(*self.entity, target.target, read_data)
})
{ {
if agent.action_state.timer > 5.0 { if agent.action_state.timer > 5.0 {
agent.action_state.timer = 0.0; agent.action_state.timer = 0.0;
@ -1144,7 +1159,9 @@ impl<'a> AgentData<'a> {
Path::Separate, Path::Separate,
None, None,
) && attack_data.angle < 15.0 ) && attack_data.angle < 15.0
&& positions_have_line_of_sight(self.pos, tgt_data.pos, read_data) && agent.target.map_or(false, |target| {
entities_have_line_of_sight(*self.entity, target.target, read_data)
})
{ {
controller.push_basic_input(InputKind::Primary); controller.push_basic_input(InputKind::Primary);
} }
@ -1296,11 +1313,11 @@ impl<'a> AgentData<'a> {
agent: &mut Agent, agent: &mut Agent,
controller: &mut Controller, controller: &mut Controller,
attack_data: &AttackData, attack_data: &AttackData,
tgt_data: &TargetData,
read_data: &ReadData, read_data: &ReadData,
) { ) {
if positions_have_line_of_sight(self.pos, tgt_data.pos, read_data) if agent.target.map_or(false, |target| {
&& attack_data.angle < 15.0 entities_have_line_of_sight(*self.entity, target.target, read_data)
}) && attack_data.angle < 15.0
{ {
controller.push_basic_input(InputKind::Primary); controller.push_basic_input(InputKind::Primary);
} else { } else {
@ -1313,12 +1330,12 @@ impl<'a> AgentData<'a> {
agent: &mut Agent, agent: &mut Agent,
controller: &mut Controller, controller: &mut Controller,
attack_data: &AttackData, attack_data: &AttackData,
tgt_data: &TargetData,
read_data: &ReadData, read_data: &ReadData,
) { ) {
controller.inputs.look_dir = self.ori.look_dir(); controller.inputs.look_dir = self.ori.look_dir();
if positions_have_line_of_sight(self.pos, tgt_data.pos, read_data) if agent.target.map_or(false, |target| {
&& attack_data.angle < 15.0 entities_have_line_of_sight(*self.entity, target.target, read_data)
}) && attack_data.angle < 15.0
{ {
controller.push_basic_input(InputKind::Primary); controller.push_basic_input(InputKind::Primary);
} else { } else {
@ -1330,7 +1347,6 @@ impl<'a> AgentData<'a> {
&self, &self,
agent: &mut Agent, agent: &mut Agent,
controller: &mut Controller, controller: &mut Controller,
tgt_data: &TargetData,
read_data: &ReadData, read_data: &ReadData,
) { ) {
controller.inputs.look_dir = Dir::new( controller.inputs.look_dir = Dir::new(
@ -1340,21 +1356,16 @@ impl<'a> AgentData<'a> {
.try_normalized() .try_normalized()
.unwrap_or_default(), .unwrap_or_default(),
); );
if positions_have_line_of_sight(self.pos, tgt_data.pos, read_data) { if agent.target.map_or(false, |target| {
entities_have_line_of_sight(*self.entity, target.target, read_data)
}) {
controller.push_basic_input(InputKind::Primary); controller.push_basic_input(InputKind::Primary);
} else { } else {
agent.target = None; agent.target = None;
} }
} }
pub fn handle_radial_turret_attack( pub fn handle_radial_turret_attack(&self, controller: &mut Controller) {
&self,
_agent: &mut Agent,
controller: &mut Controller,
_attack_data: &AttackData,
_tgt_data: &TargetData,
_read_data: &ReadData,
) {
controller.push_basic_input(InputKind::Primary); controller.push_basic_input(InputKind::Primary);
} }
@ -1386,7 +1397,9 @@ impl<'a> AgentData<'a> {
agent.action_state.counter -= MINION_SUMMON_THRESHOLD; agent.action_state.counter -= MINION_SUMMON_THRESHOLD;
} }
} else if attack_data.dist_sqrd < MINDFLAYER_ATTACK_DIST.powi(2) { } else if attack_data.dist_sqrd < MINDFLAYER_ATTACK_DIST.powi(2) {
if positions_have_line_of_sight(self.pos, tgt_data.pos, read_data) { if agent.target.map_or(false, |target| {
entities_have_line_of_sight(*self.entity, target.target, read_data)
}) {
// If close to target, use either primary or secondary ability // If close to target, use either primary or secondary ability
if matches!(self.char_state, CharacterState::BasicBeam(c) if c.timer < Duration::from_secs(10) && !matches!(c.stage_section, StageSection::Recover)) if matches!(self.char_state, CharacterState::BasicBeam(c) if c.timer < Duration::from_secs(10) && !matches!(c.stage_section, StageSection::Recover))
{ {
@ -1476,7 +1489,9 @@ impl<'a> AgentData<'a> {
let small_chance = rng.gen_bool(0.05); let small_chance = rng.gen_bool(0.05);
if small_chance if small_chance
&& positions_have_line_of_sight(self.pos, tgt_data.pos, read_data) && agent.target.map_or(false, |target| {
entities_have_line_of_sight(*self.entity, target.target, read_data)
})
&& attack_data.angle < 15.0 && attack_data.angle < 15.0
{ {
// Fireball // Fireball
@ -1597,7 +1612,9 @@ impl<'a> AgentData<'a> {
controller.push_cancel_input(InputKind::Fly); controller.push_cancel_input(InputKind::Fly);
if attack_data.dist_sqrd > 30.0_f32.powi(2) { if attack_data.dist_sqrd > 30.0_f32.powi(2) {
if rng.gen_bool(0.05) if rng.gen_bool(0.05)
&& positions_have_line_of_sight(self.pos, tgt_data.pos, read_data) && agent.target.map_or(false, |target| {
entities_have_line_of_sight(*self.entity, target.target, read_data)
})
&& attack_data.angle < 15.0 && attack_data.angle < 15.0
{ {
controller.push_basic_input(InputKind::Primary); controller.push_basic_input(InputKind::Primary);
@ -1809,7 +1826,9 @@ impl<'a> AgentData<'a> {
}, },
) { ) {
if attack_data.angle < 15.0 if attack_data.angle < 15.0
&& positions_have_line_of_sight(self.pos, tgt_data.pos, read_data) && agent.target.map_or(false, |target| {
entities_have_line_of_sight(*self.entity, target.target, read_data)
})
{ {
if agent.action_state.timer > 5.0 { if agent.action_state.timer > 5.0 {
agent.action_state.timer = 0.0; agent.action_state.timer = 0.0;
@ -2019,6 +2038,10 @@ impl<'a> AgentData<'a> {
.map(|t| t.target) .map(|t| t.target)
.and_then(|e| read_data.velocities.get(e)) .and_then(|e| read_data.velocities.get(e))
.map_or(0.0, |v| v.0.cross(self.ori.look_vec()).magnitude_squared()); .map_or(0.0, |v| v.0.cross(self.ori.look_vec()).magnitude_squared());
let line_of_sight_with_target = agent.target.map_or(false, |target| {
entities_have_line_of_sight(*self.entity, target.target, read_data)
});
if attack_data.dist_sqrd < golem_melee_range.powi(2) { if attack_data.dist_sqrd < golem_melee_range.powi(2) {
if agent.action_state.counter < 7.5 { if agent.action_state.counter < 7.5 {
// If target is close, whack them // If target is close, whack them
@ -2035,7 +2058,7 @@ impl<'a> AgentData<'a> {
} else if attack_data.dist_sqrd < GOLEM_LASER_RANGE.powi(2) { } else if attack_data.dist_sqrd < GOLEM_LASER_RANGE.powi(2) {
if matches!(self.char_state, CharacterState::BasicBeam(c) if c.timer < Duration::from_secs(5)) if matches!(self.char_state, CharacterState::BasicBeam(c) if c.timer < Duration::from_secs(5))
|| target_speed_cross_sqd < GOLEM_TARGET_SPEED.powi(2) || target_speed_cross_sqd < GOLEM_TARGET_SPEED.powi(2)
&& positions_have_line_of_sight(self.pos, tgt_data.pos, read_data) && line_of_sight_with_target
&& attack_data.angle < 45.0 && attack_data.angle < 45.0
{ {
// If target in range threshold and haven't been lasering for more than 5 // If target in range threshold and haven't been lasering for more than 5
@ -2047,9 +2070,7 @@ impl<'a> AgentData<'a> {
controller.push_basic_input(InputKind::Ability(0)); controller.push_basic_input(InputKind::Ability(0));
} }
} else if attack_data.dist_sqrd < GOLEM_LONG_RANGE.powi(2) { } else if attack_data.dist_sqrd < GOLEM_LONG_RANGE.powi(2) {
if target_speed_cross_sqd < GOLEM_TARGET_SPEED.powi(2) if target_speed_cross_sqd < GOLEM_TARGET_SPEED.powi(2) && line_of_sight_with_target {
&& positions_have_line_of_sight(self.pos, tgt_data.pos, read_data)
{
// If target is far-ish and moving slow-ish, rocket them // If target is far-ish and moving slow-ish, rocket them
controller.push_basic_input(InputKind::Ability(1)); controller.push_basic_input(InputKind::Ability(1));
} else if health_fraction < 0.7 { } else if health_fraction < 0.7 {
@ -2081,6 +2102,10 @@ impl<'a> AgentData<'a> {
const BUBBLE_RANGE: f32 = 20.0; const BUBBLE_RANGE: f32 = 20.0;
const MINION_SUMMON_THRESHOLD: f32 = 0.20; const MINION_SUMMON_THRESHOLD: f32 = 0.20;
let health_fraction = self.health.map_or(0.5, |h| h.fraction()); let health_fraction = self.health.map_or(0.5, |h| h.fraction());
let line_of_sight_with_target = agent.target.map_or(false, |target| {
entities_have_line_of_sight(*self.entity, target.target, read_data)
});
// Sets counter at start of combat, using `condition` to keep track of whether // Sets counter at start of combat, using `condition` to keep track of whether
// it was already intitialized // it was already intitialized
if !agent.action_state.condition { if !agent.action_state.condition {
@ -2110,16 +2135,12 @@ impl<'a> AgentData<'a> {
} else if attack_data.in_min_range() && attack_data.angle < 60.0 { } else if attack_data.in_min_range() && attack_data.angle < 60.0 {
// Pincer them if they're in range and angle // Pincer them if they're in range and angle
controller.push_basic_input(InputKind::Primary); controller.push_basic_input(InputKind::Primary);
} else if attack_data.angle < 30.0 } else if attack_data.angle < 30.0 && line_of_sight_with_target {
&& positions_have_line_of_sight(self.pos, tgt_data.pos, read_data)
{
// Start bubbling them if not close enough to do something else and in angle and // Start bubbling them if not close enough to do something else and in angle and
// can see target // can see target
controller.push_basic_input(InputKind::Ability(0)); controller.push_basic_input(InputKind::Ability(0));
} }
} else if attack_data.angle < 90.0 } else if attack_data.angle < 90.0 && line_of_sight_with_target {
&& positions_have_line_of_sight(self.pos, tgt_data.pos, read_data)
{
// Start scuttling if not close enough to do something else and in angle and can // Start scuttling if not close enough to do something else and in angle and can
// see target // see target
controller.push_basic_input(InputKind::Secondary); controller.push_basic_input(InputKind::Secondary);
@ -2202,6 +2223,9 @@ impl<'a> AgentData<'a> {
const FIRE_BREATH_RANGE: f32 = 20.0; const FIRE_BREATH_RANGE: f32 = 20.0;
const MAX_PUMPKIN_RANGE: f32 = 50.0; const MAX_PUMPKIN_RANGE: f32 = 50.0;
let health_fraction = self.health.map_or(0.5, |h| h.fraction()); let health_fraction = self.health.map_or(0.5, |h| h.fraction());
let line_of_sight_with_target = agent.target.map_or(false, |target| {
entities_have_line_of_sight(*self.entity, target.target, read_data)
});
if health_fraction < VINE_CREATION_THRESHOLD && !agent.action_state.condition { if health_fraction < VINE_CREATION_THRESHOLD && !agent.action_state.condition {
// Summon vines when reach threshold of health // Summon vines when reach threshold of health
@ -2213,7 +2237,7 @@ impl<'a> AgentData<'a> {
} }
} else if attack_data.dist_sqrd < FIRE_BREATH_RANGE.powi(2) { } else if attack_data.dist_sqrd < FIRE_BREATH_RANGE.powi(2) {
if matches!(self.char_state, CharacterState::BasicBeam(c) if c.timer < Duration::from_secs(5)) if matches!(self.char_state, CharacterState::BasicBeam(c) if c.timer < Duration::from_secs(5))
&& positions_have_line_of_sight(self.pos, tgt_data.pos, read_data) && line_of_sight_with_target
{ {
// Keep breathing fire if close enough, can see target, and have not been // Keep breathing fire if close enough, can see target, and have not been
// breathing for more than 5 seconds // breathing for more than 5 seconds
@ -2221,15 +2245,11 @@ impl<'a> AgentData<'a> {
} else if attack_data.in_min_range() && attack_data.angle < 60.0 { } else if attack_data.in_min_range() && attack_data.angle < 60.0 {
// Scythe them if they're in range and angle // Scythe them if they're in range and angle
controller.push_basic_input(InputKind::Primary); controller.push_basic_input(InputKind::Primary);
} else if attack_data.angle < 30.0 } else if attack_data.angle < 30.0 && line_of_sight_with_target {
&& positions_have_line_of_sight(self.pos, tgt_data.pos, read_data)
{
// Start breathing fire at them if close enough, in angle, and can see target // Start breathing fire at them if close enough, in angle, and can see target
controller.push_basic_input(InputKind::Secondary); controller.push_basic_input(InputKind::Secondary);
} }
} else if attack_data.dist_sqrd < MAX_PUMPKIN_RANGE.powi(2) } else if attack_data.dist_sqrd < MAX_PUMPKIN_RANGE.powi(2) && line_of_sight_with_target {
&& positions_have_line_of_sight(self.pos, tgt_data.pos, read_data)
{
// Throw a pumpkin at them if close enough and can see them // Throw a pumpkin at them if close enough and can see them
controller.push_basic_input(InputKind::Ability(1)); controller.push_basic_input(InputKind::Ability(1));
} }
@ -2323,7 +2343,9 @@ impl<'a> AgentData<'a> {
if attack_data.in_min_range() { if attack_data.in_min_range() {
controller.push_basic_input(InputKind::Primary); controller.push_basic_input(InputKind::Primary);
} else if attack_data.dist_sqrd < MAX_PATH_DIST.powi(2) } else if attack_data.dist_sqrd < MAX_PATH_DIST.powi(2)
&& positions_have_line_of_sight(self.pos, tgt_data.pos, read_data) && agent.target.map_or(false, |target| {
entities_have_line_of_sight(*self.entity, target.target, read_data)
})
{ {
// If in pathing range and can see target, move towards them // If in pathing range and can see target, move towards them
self.path_toward_target( self.path_toward_target(
@ -2455,7 +2477,9 @@ impl<'a> AgentData<'a> {
if attack_data.in_min_range() { if attack_data.in_min_range() {
// If in range, shockwave // If in range, shockwave
controller.push_basic_input(InputKind::Ability(0)); controller.push_basic_input(InputKind::Ability(0));
} else if positions_have_line_of_sight(self.pos, tgt_data.pos, read_data) { } else if agent.target.map_or(false, |target| {
entities_have_line_of_sight(*self.entity, target.target, read_data)
}) {
// Else if in sight, barrage // Else if in sight, barrage
controller.push_basic_input(InputKind::Secondary); controller.push_basic_input(InputKind::Secondary);
} }

View File

@ -151,12 +151,33 @@ pub fn are_our_owners_hostile(
}) })
} }
pub fn entities_have_line_of_sight(
entity: EcsEntity,
other: EcsEntity,
read_data: &ReadData,
) -> bool {
let eye_pos = |ent: EcsEntity| {
let eye_offset = read_data.bodies.get(ent).map_or(0.0, |b| b.eye_height());
read_data
.positions
.get(entity)
.map(|pos| Pos(Vec3::new(pos.0.x, pos.0.y, pos.0.z + eye_offset)))
};
if let (Some(eye_pos), Some(other_eye_pos)) = (eye_pos(entity), eye_pos(other)) {
positions_have_line_of_sight(&eye_pos, &other_eye_pos, read_data)
} else {
false
}
}
pub fn positions_have_line_of_sight(pos_a: &Pos, pos_b: &Pos, read_data: &ReadData) -> bool { pub fn positions_have_line_of_sight(pos_a: &Pos, pos_b: &Pos, read_data: &ReadData) -> bool {
let dist_sqrd = pos_b.0.distance_squared(pos_a.0); let dist_sqrd = pos_b.0.distance_squared(pos_a.0);
read_data read_data
.terrain .terrain
.ray(pos_a.0 + Vec3::unit_z(), pos_b.0 + Vec3::unit_z()) .ray(pos_a.0, pos_b.0)
.until(Block::is_opaque) .until(Block::is_opaque)
.cast() .cast()
.0 .0
@ -215,7 +236,7 @@ pub fn does_entity_see_other(
.try_normalized() .try_normalized()
.map_or(false, |v| v.dot(*controller.inputs.look_dir) > 0.15); .map_or(false, |v| v.dot(*controller.inputs.look_dir) > 0.15);
within_sight_dist && positions_have_line_of_sight(pos, other_pos, read_data) && within_fov within_sight_dist && entities_have_line_of_sight(entity, other, read_data) && within_fov
} else { } else {
false false
} }