use crate::{ GameState, abilities::{HeadAbility, TriggerData, TriggerThrow}, aim::AimTarget, heads::ActiveHeads, heads_database::HeadsDatabase, player::Player, }; use bevy::prelude::*; #[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, ( 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 on_ai_added(mut commands: Commands, query: Query>) { for entity in query.iter() { commands.entity(entity).insert(WaitForAnyPlayer); } } fn wait_for_player( mut commands: Commands, agents: Query>, transform: Query<&Transform>, players: Query>, ) { for agent in agents.iter() { if let Some(player) = in_range(50., agent, &players, &transform) { info!("[{agent}] Engage: {player}"); if let Ok(mut agent) = commands.get_entity(agent) { agent.remove::().insert(Engage(player)); } } } } fn out_of_range( mut commands: Commands, agents: Query>, transform: Query<&Transform>, players: Query>, ) { for agent in agents.iter() { if in_range(100., agent, &players, &transform).is_none() { info!("[{agent}] Player out of range"); commands .entity(agent) .remove::() .insert(WaitForAnyPlayer); } } } fn detect_reload(mut commands: Commands, agents: Query<(Entity, &ActiveHeads), With>) { for (e, head) in agents.iter() { if head.reloading() { info!("[{e}] Reload started"); commands.entity(e).remove::().insert(Reload); } } } fn detect_reload_done(mut commands: Commands, agents: Query<(Entity, &ActiveHeads), With>) { for (e, head) in agents.iter() { if !head.reloading() { info!("[{e}] Reload done"); commands .entity(e) .remove::() .insert(WaitForAnyPlayer); } } } fn in_range( range: f32, entity: Entity, players: &Query<'_, '_, Entity, With>, transform: &Query<'_, '_, &Transform>, ) -> Option { 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 = (agent_transform.translation - target_pos).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>, time: Res