diff --git a/assets/all.headsdb.ron b/assets/all.headsdb.ron index 0ac7730..6d74dd1 100644 --- a/assets/all.headsdb.ron +++ b/assets/all.headsdb.ron @@ -9,7 +9,7 @@ /*07*/(key:"green grocer", range:60, ammo: 10, damage: 25), /*08*/(key:"highland hammer thrower", ability:Thrown, ammo: 10, damage: 25, range:80, projectile:"hammer"), /*09*/(key:"legionnaire", ability:Gun, ammo: 25, range:60, damage: 13), - /*10*/(key:"mig pilot", ability:Gun, ammo: 5, range:60, damage:100, controls:Plane), + /*10*/(key:"mig pilot", ability:Missile, ammo: 5, range:60, damage:100, controls:Plane), /*11*/(key:"nanny", ability:Thrown, range:60), /*12*/(key:"panic attack"), /*13*/(key:"salty sea dog"), diff --git a/assets/models/projectiles/missile.glb b/assets/models/projectiles/missile.glb new file mode 100644 index 0000000..740df1a Binary files /dev/null and b/assets/models/projectiles/missile.glb differ diff --git a/assets/sfx/abilities/missile-explosion.ogg b/assets/sfx/abilities/missile-explosion.ogg new file mode 100644 index 0000000..f1eafd9 Binary files /dev/null and b/assets/sfx/abilities/missile-explosion.ogg differ diff --git a/src/abilities/missile.rs b/src/abilities/missile.rs new file mode 100644 index 0000000..50b9e69 --- /dev/null +++ b/src/abilities/missile.rs @@ -0,0 +1,224 @@ +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}, +}; +use avian3d::prelude::*; +use bevy::{pbr::NotShadowCaster, prelude::*}; +use bevy_polyline::prelude::*; +use bevy_sprite3d::{Sprite3dBuilder, Sprite3dParams}; +use std::f32::consts::PI; + +const MAX_SHOT_AGES: f32 = 5.; + +#[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, 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 Some(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