entevsbaer/src/enemy.rs

105 lines
3.5 KiB
Rust

use std::f32::consts::PI;
use std::ops::RangeInclusive;
use rand::Rng;
use bevy::prelude::*;
use heron::prelude::*;
use crate::{
map::GroundContact,
player::Player,
};
#[derive(Component)]
pub struct Enemy {
pub rotation: f32,
pub bounds: (RangeInclusive<f32>, RangeInclusive<f32>),
pub patrol_target: Option<(f32, f32)>,
}
pub fn walk(
time: Res<Time>,
mut queries: QuerySet<(
QueryState<&Transform, With<Player>>,
QueryState<(&mut Transform, &mut Enemy, &mut Velocity, &GroundContact)>
)>,
) {
let mut rng = rand::thread_rng();
let player_positions = queries.q0()
.iter()
.map(|player_transform|
player_transform.translation
)
.collect::<Vec<_>>();
let mut enemies = queries.q1();
for (mut transform, mut enemy, mut velocity, contact) in enemies.iter_mut() {
if contact.0 == 0 {
// do not move unless on ground
continue;
}
// TODO: if transform is out of enemy.bounds, attack any player
// TODO: do not patrol if player is near :)
// find nearest player in bounds
let mut target = None;
let mut nearest_distance = None;
for player_position in &player_positions {
if enemy.bounds.0.contains(&player_position.x) && enemy.bounds.1.contains(&player_position.z) {
let way = *player_position - transform.translation;
let distance = way.length_squared();
if nearest_distance.map_or(true, |nearest_distance| distance < nearest_distance) {
target = Some(*player_position);
nearest_distance = Some(distance);
}
}
}
if target.is_none() {
// patrol
let x_bounds = enemy.bounds.0.clone();
let z_bounds = enemy.bounds.1.clone();
let (x, z) = enemy.patrol_target
// new patrol target
.get_or_insert_with(|| (
rng.gen_range(x_bounds),
rng.gen_range(z_bounds)
)).clone();
if Vec2::new(transform.translation.x - x,
transform.translation.z - z)
.length_squared() < 1.0
{
// target reached, set new one next frame
enemy.patrol_target = None;
} else {
// continue patrolling
target = Some(Vec3::new(x, 0.0, z));
}
} else {
// if there had been a target,
// reset previous patrolling
enemy.patrol_target = None;
}
if let Some(direction) = target.and_then(|target| {
let way = target - transform.translation;
Vec3::new(way.x, 0.0, way.z)
.try_normalize()
})
{
let target_rotation = Vec2::new(direction.x, direction.z)
.angle_between(Vec2::Y);
let rotation_delta = 2.0 * PI * time.delta_seconds() * if target_rotation > enemy.rotation { 1.0 } else { -1.0 };
if rotation_delta.abs() < (target_rotation - enemy.rotation).abs() {
// rotate before move
enemy.rotation += rotation_delta;
} else {
enemy.rotation = target_rotation;
// move
velocity.linear = 3.0 * direction + 2.0 * Vec3::Y;
}
transform.rotation = Quat::from_rotation_y(enemy.rotation);
}
}
}