Stephan/hez 2 npc should loose head if 0 health (#34)

This commit is contained in:
extrawurst
2025-04-20 11:26:06 +02:00
committed by GitHub
parent 7cd4b38ebd
commit 2dcc396666
7 changed files with 107 additions and 12 deletions

BIN
assets/models/head_drop.glb Normal file

Binary file not shown.

View File

@@ -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()
}, },
)) ))

View File

@@ -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
View 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();
}
}

View File

@@ -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>,

View File

@@ -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>();

View File

@@ -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() {