use super::TriggerMissile; use crate::{ GameState, billboards::Billboard, heads_database::HeadsDatabase, hitpoints::Hit, loading_assets::GameAssets, physics_layers::GameLayer, sounds::PlaySound, utils::{global_observer, sprite_3d_animation::AnimationTimer, trail::Trail}, }; use avian3d::prelude::*; use bevy::{pbr::NotShadowCaster, prelude::*}; use bevy_sprite3d::{Sprite3dBuilder, Sprite3dParams}; use std::f32::consts::PI; const MAX_SHOT_AGES: f32 = 15.; #[derive(Component)] struct MissileProjectile { time: f32, damage: u32, } #[derive(Event, Debug)] struct Explosion { position: Vec3, radius: f32, damage: u32, } #[derive(Resource)] struct ShotAssets { image: Handle, layout: Handle, } pub fn plugin(app: &mut App) { app.add_systems(OnEnter(GameState::Playing), setup); app.add_systems(Update, shot_collision.run_if(in_state(GameState::Playing))); app.add_systems( FixedUpdate, (update, timeout).run_if(in_state(GameState::Playing)), ); global_observer!(app, on_trigger_missile); global_observer!(app, on_explosion); } fn on_explosion( explosion: Trigger, mut commands: Commands, spatial_query: SpatialQuery, ) { let explosion = explosion.event(); let intersections = { spatial_query.shape_intersections( &Collider::sphere(explosion.radius), explosion.position, Quat::default(), &SpatialQueryFilter::default().with_mask(LayerMask( GameLayer::Npc.to_bits() | GameLayer::Player.to_bits(), )), ) }; for entity in intersections.iter() { if let Ok(mut e) = commands.get_entity(*entity) { e.trigger(Hit { damage: explosion.damage, }); } } } fn setup(mut commands: Commands, assets: Res, mut sprite_params: Sprite3dParams) { let layout = TextureAtlasLayout::from_grid(UVec2::splat(256), 7, 6, None, None); let texture_atlas_layout = sprite_params.atlas_layouts.add(layout); commands.insert_resource(ShotAssets { image: assets.impact_atlas.clone(), layout: texture_atlas_layout, }); } fn on_trigger_missile( trigger: Trigger, mut commands: Commands, query_transform: Query<&Transform>, time: Res