diff --git a/assets/models/head_drops/none.glb b/assets/models/head_drops/none.glb new file mode 100644 index 0000000..f1bb3c5 Binary files /dev/null and b/assets/models/head_drops/none.glb differ diff --git a/src/head_drop.rs b/src/head_drop.rs index 957c861..d94d644 100644 --- a/src/head_drop.rs +++ b/src/head_drop.rs @@ -1,6 +1,6 @@ use crate::{ - GameState, billboards::Billboard, global_observer, loading_assets::GameAssets, - physics_layers::GameLayer, player::Player, sounds::PlaySound, + GameState, billboards::Billboard, global_observer, heads_database::HeadsDatabase, + loading_assets::HeadDropAssets, physics_layers::GameLayer, player::Player, sounds::PlaySound, squish_animation::SquishAnimation, tb_entities::SecretHead, }; use avian3d::prelude::*; @@ -8,7 +8,29 @@ use bevy::prelude::*; use std::f32::consts::PI; #[derive(Event, Reflect)] -pub struct HeadDrops(pub Vec3, pub usize); +pub struct HeadDrops { + pos: Vec3, + head_id: usize, + impulse: bool, +} + +impl HeadDrops { + pub fn new(pos: Vec3, head_id: usize) -> Self { + Self { + pos, + head_id, + impulse: true, + } + } + + fn new_static(pos: Vec3, head_id: usize) -> Self { + Self { + pos, + head_id, + impulse: false, + } + } +} #[derive(Component, Reflect)] #[reflect(Component)] @@ -28,63 +50,65 @@ pub fn plugin(app: &mut App) { global_observer!(app, on_head_drop); } -fn spawn( - mut commands: Commands, - query: Query<(Entity, &GlobalTransform, &SecretHead)>, - assets: Res, -) { +fn spawn(mut commands: Commands, query: Query<(Entity, &GlobalTransform, &SecretHead)>) { for (e, t, head) in query { - commands - .spawn(( - Name::new("headdrop"), - SecretHeadMarker, - HeadDrop(head.head_id), - Transform::from_translation(t.translation() + Vec3::new(0., 2., 0.)), - Visibility::default(), - Collider::sphere(1.5), - 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()), - )); + commands.trigger(HeadDrops::new_static( + t.translation() + Vec3::new(0., 2., 0.), + head.head_id, + )); commands.entity(e).despawn(); } } -fn on_head_drop(trigger: Trigger, mut commands: Commands, assets: Res) { - let HeadDrops(position, id) = trigger.event(); +fn on_head_drop( + trigger: Trigger, + mut commands: Commands, + assets: Res, + heads_db: Res, + gltf_assets: Res>, +) -> Result<(), BevyError> { + let drop = 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); + if drop.impulse { + commands.trigger(PlaySound::HeadDrop); + } + + let ability = format!("{:?}.glb", heads_db.head_stats(drop.head_id).ability).to_lowercase(); + let mesh = if let Some(handle) = assets.meshes.get(ability.as_str()) { + gltf_assets.get(handle) + } else { + gltf_assets.get(&assets.meshes["none.glb"]) + } + .ok_or_else(|| "asset not found")?; commands .spawn(( Name::new("headdrop"), - HeadDrop(*id), - Transform::from_translation(*position), + HeadDrop(drop.head_id), + Transform::from_translation(drop.pos), 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), )) + .insert_if( + ExternalImpulse::new(spawn_dir * 180.).with_persistence(false), + || drop.impulse, + ) .with_child(( Billboard, SquishAnimation(2.6), - SceneRoot(assets.mesh_head_drop.clone()), + SceneRoot(mesh.scenes[0].clone()), )); + + Ok(()) } fn collect_head( diff --git a/src/loading_assets.rs b/src/loading_assets.rs index 1073d2f..5eb80a3 100644 --- a/src/loading_assets.rs +++ b/src/loading_assets.rs @@ -67,6 +67,12 @@ struct HeadsAssets { heads: Handle, } +#[derive(AssetCollection, Resource)] +pub struct HeadDropAssets { + #[asset(path = "models/head_drops", collection(mapped, typed))] + pub meshes: HashMap>, +} + #[derive(AssetCollection, Resource)] pub struct UIAssets { #[asset(path = "font.ttf")] @@ -93,9 +99,6 @@ pub struct GameAssets { #[asset(path = "models/key.glb#Scene0")] pub mesh_key: Handle, - #[asset(path = "models/head_drop.glb#Scene0")] - pub mesh_head_drop: Handle, - #[asset(path = "models/spawn.glb#Scene0")] pub mesh_spawn: Handle, @@ -122,6 +125,7 @@ impl Plugin for LoadingPlugin { .load_collection::() .load_collection::() .load_collection::() + .load_collection::() .load_collection::(), ); } diff --git a/src/npc.rs b/src/npc.rs index 7b1a6c4..12e6c2f 100644 --- a/src/npc.rs +++ b/src/npc.rs @@ -59,7 +59,7 @@ fn on_kill( return; }; - commands.trigger(HeadDrops(transform.translation, head.0)); + commands.trigger(HeadDrops::new(transform.translation, head.0)); commands.entity(trigger.target()).despawn();