use crate::{ GameState, billboards::Billboard, global_observer, loading_assets::GameAssets, physics_layers::GameLayer, player::Player, sounds::PlaySound, squish_animation::SquishAnimation, }; use avian3d::prelude::*; use bevy::prelude::*; use std::f32::consts::PI; #[derive(Event, Reflect)] pub struct HeadDrops(pub Vec3, pub usize); #[derive(Component, Reflect)] #[reflect(Component)] struct HeadDrop(pub usize); #[derive(Event, Reflect)] pub struct HeadCollected(pub usize); pub fn plugin(app: &mut App) { app.add_systems(Update, collect_head.run_if(in_state(GameState::Playing))); global_observer!(app, on_head_drop); } fn on_head_drop(trigger: Trigger, mut commands: Commands, assets: Res) { let HeadDrops(position, id) = trigger.event(); let angle = rand::random::() * PI * 2.; let spawn_dir = Quat::from_rotation_y(angle) * Vec3::new(0.5, 0.6, 0.).normalize(); commands.trigger(PlaySound::HeadDrop); commands .spawn(( Name::new("headdrop"), HeadDrop(*id), Transform::from_translation(*position), Visibility::default(), Collider::sphere(1.5), ExternalImpulse::new(spawn_dir * 180.).with_persistence(false), LockedAxes::ROTATION_LOCKED, RigidBody::Dynamic, CollisionLayers::new(LayerMask(GameLayer::Collectibles.to_bits()), LayerMask::ALL), CollisionEventsEnabled, Restitution::new(0.6), )) .with_child(( Billboard, SquishAnimation(2.6), SceneRoot(assets.mesh_head_drop.clone()), )); } fn collect_head( mut commands: Commands, mut collision_event_reader: EventReader, query_player: Query<&Player>, query_collectable: Query<&HeadDrop>, ) { for CollisionStarted(e1, e2) in collision_event_reader.read() { let collectable = if query_player.contains(*e1) && query_collectable.contains(*e2) { *e2 } else if query_player.contains(*e2) && query_collectable.contains(*e1) { *e1 } else { continue; }; let key = query_collectable.get(collectable).unwrap(); commands.trigger(PlaySound::HeadCollect); commands.trigger(HeadCollected(key.0)); commands.entity(collectable).despawn(); } }