Stephan/hez 2 npc should loose head if 0 health (#34)
This commit is contained in:
BIN
assets/models/head_drop.glb
Normal file
BIN
assets/models/head_drop.glb
Normal file
Binary file not shown.
@@ -181,7 +181,7 @@ fn spawn_head_ui(
|
|||||||
top: Val::Px(0.),
|
top: Val::Px(0.),
|
||||||
left: Val::Px(0.),
|
left: Val::Px(0.),
|
||||||
right: Val::Px(0.),
|
right: Val::Px(0.),
|
||||||
height: Val::Percent(25.),
|
height: Val::Percent(0.),
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ mod backpack_ui;
|
|||||||
mod ui_head_state;
|
mod ui_head_state;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
GameState,
|
cash::CashCollectEvent, global_observer, head_drop::HeadCollected, heads::HeadState,
|
||||||
heads::{HEAD_COUNT, HeadState},
|
|
||||||
heads_database::HeadsDatabase,
|
heads_database::HeadsDatabase,
|
||||||
};
|
};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
@@ -26,21 +25,38 @@ impl Backpack {
|
|||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn contains(&self, head_id: usize) -> bool {
|
||||||
|
self.heads.iter().any(|head| head.head == head_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert(&mut self, head_id: usize, heads_db: &HeadsDatabase) {
|
||||||
|
self.heads.push(HeadState::new(head_id, heads_db));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Event)]
|
#[derive(Event)]
|
||||||
pub struct BackbackSwapEvent(pub usize);
|
pub struct BackbackSwapEvent(pub usize);
|
||||||
|
|
||||||
pub fn plugin(app: &mut App) {
|
pub fn plugin(app: &mut App) {
|
||||||
|
app.init_resource::<Backpack>();
|
||||||
|
|
||||||
app.add_plugins(backpack_ui::plugin);
|
app.add_plugins(backpack_ui::plugin);
|
||||||
|
|
||||||
app.add_systems(OnEnter(GameState::Playing), setup);
|
global_observer!(app, on_head_collect);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup(mut commands: Commands, heads: Res<HeadsDatabase>) {
|
fn on_head_collect(
|
||||||
commands.insert_resource(Backpack {
|
trigger: Trigger<HeadCollected>,
|
||||||
heads: (0usize..HEAD_COUNT)
|
mut cmds: Commands,
|
||||||
.map(|i| HeadState::new(i, heads.as_ref()))
|
mut backpack: ResMut<Backpack>,
|
||||||
.collect(),
|
heads_db: Res<HeadsDatabase>,
|
||||||
});
|
) {
|
||||||
|
let HeadCollected(head) = *trigger.event();
|
||||||
|
|
||||||
|
if backpack.contains(head) {
|
||||||
|
cmds.trigger(CashCollectEvent);
|
||||||
|
} else {
|
||||||
|
backpack.insert(head, heads_db.as_ref());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
71
src/head_drop.rs
Normal file
71
src/head_drop.rs
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
use crate::{
|
||||||
|
GameState, billboards::Billboard, global_observer, loading_assets::GameAssets, player::Player,
|
||||||
|
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<HeadDrops>, mut commands: Commands, assets: Res<GameAssets>) {
|
||||||
|
let HeadDrops(position, id) = trigger.event();
|
||||||
|
|
||||||
|
let angle = rand::random::<f32>() * PI * 2.;
|
||||||
|
let spawn_dir = Quat::from_rotation_y(angle) * Vec3::new(0.5, 0.6, 0.).normalize();
|
||||||
|
|
||||||
|
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,
|
||||||
|
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<CollisionStarted>,
|
||||||
|
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::KeyCollect);
|
||||||
|
commands.trigger(HeadCollected(key.0));
|
||||||
|
commands.entity(collectable).despawn_recursive();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -82,6 +82,9 @@ pub struct GameAssets {
|
|||||||
#[asset(path = "models/key.glb#Scene0")]
|
#[asset(path = "models/key.glb#Scene0")]
|
||||||
pub mesh_key: Handle<Scene>,
|
pub mesh_key: Handle<Scene>,
|
||||||
|
|
||||||
|
#[asset(path = "models/head_drop.glb#Scene0")]
|
||||||
|
pub mesh_head_drop: Handle<Scene>,
|
||||||
|
|
||||||
#[asset(path = "models/spawn.glb#Scene0")]
|
#[asset(path = "models/spawn.glb#Scene0")]
|
||||||
pub mesh_spawn: Handle<Scene>,
|
pub mesh_spawn: Handle<Scene>,
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ mod cutscene;
|
|||||||
mod debug;
|
mod debug;
|
||||||
mod gates;
|
mod gates;
|
||||||
mod head;
|
mod head;
|
||||||
|
mod head_drop;
|
||||||
mod heads;
|
mod heads;
|
||||||
mod heads_database;
|
mod heads_database;
|
||||||
mod hitpoints;
|
mod hitpoints;
|
||||||
@@ -151,6 +152,7 @@ fn main() {
|
|||||||
app.add_plugins(debug::plugin);
|
app.add_plugins(debug::plugin);
|
||||||
app.add_plugins(utils::observers::plugin);
|
app.add_plugins(utils::observers::plugin);
|
||||||
app.add_plugins(water::plugin);
|
app.add_plugins(water::plugin);
|
||||||
|
app.add_plugins(head_drop::plugin);
|
||||||
|
|
||||||
app.init_state::<GameState>();
|
app.init_state::<GameState>();
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ use crate::{
|
|||||||
GameState,
|
GameState,
|
||||||
ai::Ai,
|
ai::Ai,
|
||||||
head::ActiveHead,
|
head::ActiveHead,
|
||||||
|
head_drop::HeadDrops,
|
||||||
heads::{ActiveHeads, HEAD_COUNT, HeadState},
|
heads::{ActiveHeads, HEAD_COUNT, HeadState},
|
||||||
heads_database::HeadsDatabase,
|
heads_database::HeadsDatabase,
|
||||||
hitpoints::{Hitpoints, Kill},
|
hitpoints::{Hitpoints, Kill},
|
||||||
@@ -50,12 +51,14 @@ fn init(mut commands: Commands, query: Query<(Entity, &EnemySpawn)>, heads_db: R
|
|||||||
fn on_kill(
|
fn on_kill(
|
||||||
trigger: Trigger<Kill>,
|
trigger: Trigger<Kill>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
query: Query<(&Transform, &EnemySpawn)>,
|
query: Query<(&Transform, &EnemySpawn, &ActiveHead)>,
|
||||||
) {
|
) {
|
||||||
let Ok((transform, enemy)) = query.get(trigger.entity()) else {
|
let Ok((transform, enemy, head)) = query.get(trigger.entity()) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
commands.trigger(HeadDrops(transform.translation, head.0));
|
||||||
|
|
||||||
commands.entity(trigger.entity()).despawn_recursive();
|
commands.entity(trigger.entity()).despawn_recursive();
|
||||||
|
|
||||||
if !enemy.key.is_empty() {
|
if !enemy.key.is_empty() {
|
||||||
|
|||||||
Reference in New Issue
Block a user