simple rotate to player ai while engaging

This commit is contained in:
2025-04-27 18:46:11 +02:00
parent ea84893c70
commit c37dd64221
5 changed files with 134 additions and 4 deletions

View File

@@ -6,19 +6,141 @@ use crate::{
aim::AimTarget,
heads::ActiveHeads,
heads_database::HeadsDatabase,
player::Player,
};
#[derive(Component, Reflect)]
#[reflect(Component)]
pub struct Ai;
#[derive(Component, Reflect, Clone)]
#[reflect(Component)]
struct WaitForAnyPlayer;
#[derive(Component, Reflect, Clone)]
#[reflect(Component)]
struct Engage(Entity);
#[derive(Component, Reflect, Clone)]
#[reflect(Component)]
struct Reload;
pub fn plugin(app: &mut App) {
app.add_systems(Update, update.run_if(in_state(GameState::Playing)));
app.add_systems(
Update,
(
engage_and_throw,
wait_for_player,
out_of_range,
detect_reload,
detect_reload_done,
rotate,
)
.run_if(in_state(GameState::Playing)),
);
app.add_systems(Update, on_ai_added);
}
fn update(
fn on_ai_added(mut commands: Commands, query: Query<Entity, Added<Ai>>) {
for entity in query.iter() {
commands.entity(entity).insert(WaitForAnyPlayer);
}
}
fn wait_for_player(
mut commands: Commands,
mut query: Query<(&mut ActiveHeads, &AimTarget, &Transform), With<Ai>>,
agents: Query<Entity, With<WaitForAnyPlayer>>,
transform: Query<&Transform>,
players: Query<Entity, With<Player>>,
) {
for agent in agents.iter() {
if let Some(player) = in_range(50., agent, &players, &transform) {
info!("[{agent}] Engage: {player}");
commands
.entity(agent)
.remove::<WaitForAnyPlayer>()
.insert(Engage(player));
}
}
}
fn out_of_range(
mut commands: Commands,
agents: Query<Entity, With<Engage>>,
transform: Query<&Transform>,
players: Query<Entity, With<Player>>,
) {
for agent in agents.iter() {
if in_range(100., agent, &players, &transform).is_none() {
info!("[{agent}] Player out of range");
commands
.entity(agent)
.remove::<Engage>()
.insert(WaitForAnyPlayer);
}
}
}
fn detect_reload(mut commands: Commands, agents: Query<(Entity, &ActiveHeads), With<Engage>>) {
for (e, head) in agents.iter() {
if head.reloading() {
info!("[{e}] Reload started");
commands.entity(e).remove::<Engage>().insert(Reload);
}
}
}
fn detect_reload_done(mut commands: Commands, agents: Query<(Entity, &ActiveHeads), With<Reload>>) {
for (e, head) in agents.iter() {
if !head.reloading() {
info!("[{e}] Reload done");
commands
.entity(e)
.remove::<Reload>()
.insert(WaitForAnyPlayer);
}
}
}
fn in_range(
range: f32,
entity: Entity,
players: &Query<'_, '_, Entity, With<Player>>,
transform: &Query<'_, '_, &Transform>,
) -> Option<Entity> {
let Ok(pos) = transform.get(entity).map(|t| t.translation) else {
return None;
};
players
.iter()
.filter_map(|p| transform.get(p).ok().map(|t| (p, *t)))
.find(|(_, t)| t.translation.distance(pos) < range)
.map(|(e, _)| e)
}
fn rotate(agent: Query<(Entity, &Engage)>, mut transform: Query<&mut Transform>) {
for (agent, Engage(target)) in agent.iter() {
let Ok(target_pos) = transform.get(*target).map(|t| t.translation) else {
continue;
};
let Ok(mut agent_transform) = transform.get_mut(agent) else {
continue;
};
// Get the direction vector from the current position to the target
let direction = (target_pos - agent_transform.translation).normalize();
// Project the direction onto the XZ plane by zeroing out the Y component
let xz_direction = Vec3::new(direction.x, 0.0, direction.z).normalize();
agent_transform.rotation = Quat::from_rotation_arc(Vec3::Z, xz_direction);
}
}
fn engage_and_throw(
mut commands: Commands,
mut query: Query<(&mut ActiveHeads, &AimTarget, &Transform), With<Engage>>,
time: Res<Time>,
heads_db: Res<HeadsDatabase>,
) {

View File

@@ -43,8 +43,8 @@ fn init(mut commands: Commands, query: Query<(Entity, &EnemySpawn)>, heads_db: R
None,
None,
]),
Ai,
))
.insert_if(Ai, || !spawn.disable_ai)
.with_child((Name::from("body-rig"), AnimatedCharacter(id)))
.observe(on_kill);
}

View File

@@ -113,6 +113,7 @@ pub struct CutsceneCameraMovementEnd;
pub struct EnemySpawn {
pub head: String,
pub key: String,
pub disable_ai: bool,
}
impl EnemySpawn {