use crate::{ billboards::Billboard, player::{Player, PlayerHead}, tb_entities::EnemySpawn, }; use bevy::prelude::*; use bevy_sprite3d::{Sprite3dBuilder, Sprite3dParams}; use std::f32::consts::PI; #[derive(Resource, Reflect)] #[reflect(Resource)] struct AimState { pub target: Option, pub range: f32, pub angle: f32, } impl Default for AimState { fn default() -> Self { Self { target: None, range: 40., angle: PI / 10., } } } #[derive(Component, Reflect)] #[reflect(Component)] struct Marker; #[derive(Event)] enum MarkerEvent { Spawn(Entity), Despawn, } pub fn plugin(app: &mut App) { app.init_resource::(); app.add_systems(Update, update); app.add_observer(marker_event); } fn marker_event( trigger: Trigger, mut commands: Commands, asset_server: Res, mut sprite_params: Sprite3dParams, marker: Query>, ) { for m in marker.iter() { commands.entity(m).despawn_recursive(); } let MarkerEvent::Spawn(target) = trigger.event() else { return; }; let selector = asset_server.load("ui/selector.png"); let id = commands .spawn(( Name::new("aim-marker"), Billboard, Marker, Transform::from_translation(Vec3::new(0., 3., 0.)), Sprite3dBuilder { image: selector, pixels_per_metre: 30., alpha_mode: AlphaMode::Blend, unlit: true, ..default() } .bundle(&mut sprite_params), )) .id(); commands.entity(*target).add_child(id); } fn update( mut commands: Commands, mut state: ResMut, query: Query<(Entity, &Transform), With>, player_pos: Query<&Transform, With>, player_rot: Query<&Transform, With>, ) { let Some(player_pos) = player_pos.iter().next().map(|t| t.translation) else { return; }; let Some(player_forward) = player_rot.iter().next().map(|t| t.forward()) else { return; }; let mut new_target = None; let mut target_distance = f32::MAX; for (e, t) in query.iter() { let delta = player_pos - t.translation; let distance = delta.length(); if distance > state.range { continue; } let angle = player_forward.angle_between(delta.normalize()); if angle < state.angle && distance < target_distance { new_target = Some(e); target_distance = distance; } } if let Some(e) = state.target { if commands.get_entity(e).is_none() { state.target = None; return; } } if new_target != state.target { if let Some(target) = new_target { commands.trigger(MarkerEvent::Spawn(target)); } else { commands.trigger(MarkerEvent::Despawn); } state.target = new_target; } }