Player can loose head (#46)

This commit is contained in:
extrawurst
2025-06-20 17:35:00 +02:00
committed by GitHub
parent a8f2116804
commit 28ad53fd68
3 changed files with 118 additions and 42 deletions

View File

@@ -37,7 +37,13 @@ impl HeadDrops {
#[derive(Component, Reflect)] #[derive(Component, Reflect)]
#[reflect(Component)] #[reflect(Component)]
struct HeadDrop(pub usize); struct HeadDrop {
pub head_id: usize,
}
#[derive(Component, Reflect)]
#[reflect(Component)]
struct HeadDropEnableTime(f32);
#[derive(Component, Reflect)] #[derive(Component, Reflect)]
#[reflect(Component)] #[reflect(Component)]
@@ -47,6 +53,11 @@ struct SecretHeadMarker;
pub struct HeadCollected(pub usize); pub struct HeadCollected(pub usize);
pub fn plugin(app: &mut App) { pub fn plugin(app: &mut App) {
app.add_systems(
Update,
enable_collectible.run_if(in_state(GameState::Playing)),
);
app.add_systems(OnEnter(GameState::Playing), spawn); app.add_systems(OnEnter(GameState::Playing), spawn);
global_observer!(app, on_head_drop); global_observer!(app, on_head_drop);
@@ -69,6 +80,7 @@ fn on_head_drop(
assets: Res<HeadDropAssets>, assets: Res<HeadDropAssets>,
heads_db: Res<HeadsDatabase>, heads_db: Res<HeadsDatabase>,
gltf_assets: Res<Assets<Gltf>>, gltf_assets: Res<Assets<Gltf>>,
time: Res<Time>,
) -> Result<(), BevyError> { ) -> Result<(), BevyError> {
let drop = trigger.event(); let drop = trigger.event();
@@ -87,8 +99,6 @@ fn on_head_drop(
} }
.ok_or("asset not found")?; .ok_or("asset not found")?;
let head_id = drop.head_id;
commands commands
.spawn(( .spawn((
Name::new("headdrop"), Name::new("headdrop"),
@@ -102,16 +112,21 @@ fn on_head_drop(
LayerMask::ALL & !GameLayer::Player.to_bits(), LayerMask::ALL & !GameLayer::Player.to_bits(),
), ),
Restitution::new(0.6), Restitution::new(0.6),
Children::spawn(SpawnWith(move |parent: &mut RelatedSpawner<ChildOf>| { Children::spawn(SpawnWith({
parent let head_id = drop.head_id;
.spawn(( let now = time.elapsed_secs();
Collider::sphere(1.5), move |parent: &mut RelatedSpawner<ChildOf>| {
CollisionLayers::new(GameLayer::CollectibleSensors, GameLayer::Player), parent
Sensor, .spawn((
CollisionEventsEnabled, Collider::sphere(1.5),
HeadDrop(head_id), CollisionLayers::new(GameLayer::CollectibleSensors, LayerMask::NONE),
)) Sensor,
.observe(on_collect_head); CollisionEventsEnabled,
HeadDrop { head_id },
HeadDropEnableTime(now + 1.2),
))
.observe(on_collect_head);
}
})), })),
)) ))
.insert_if( .insert_if(
@@ -127,8 +142,28 @@ fn on_head_drop(
Ok(()) Ok(())
} }
fn enable_collectible(
mut commands: Commands,
query: Query<(Entity, &HeadDropEnableTime)>,
time: Res<Time>,
) {
let now = time.elapsed_secs();
for (e, enable_time) in query.iter() {
if now > enable_time.0 {
commands
.entity(e)
.insert(CollisionLayers::new(
LayerMask(GameLayer::CollectibleSensors.to_bits()),
LayerMask::ALL,
))
.remove::<HeadDropEnableTime>();
}
}
}
fn on_collect_head( fn on_collect_head(
trigger: Trigger<OnCollisionStart>, trigger: Trigger<OnCollisionStart>,
mut commands: Commands, mut commands: Commands,
query_player: Query<&Player>, query_player: Query<&Player>,
query_collectable: Query<(&HeadDrop, &ChildOf)>, query_collectable: Query<(&HeadDrop, &ChildOf)>,
@@ -148,7 +183,7 @@ fn on_collect_head(
commands.trigger(PlaySound::HeadCollect); commands.trigger(PlaySound::HeadCollect);
} }
commands.trigger(HeadCollected(drop.0)); commands.trigger(HeadCollected(drop.head_id));
commands.entity(child_of.parent()).despawn(); commands.entity(child_of.parent()).despawn();
} }
} }

View File

@@ -147,6 +147,26 @@ impl ActiveHeads {
(head.health, head.health_max) = hp.get() (head.health, head.health_max) = hp.get()
} }
// returns new current head id
pub fn loose_current(&mut self) -> Option<usize> {
self.heads[self.current_slot] = None;
self.next_head()
}
fn next_head(&mut self) -> Option<usize> {
let start_idx = self.current_slot;
for offset in 1..5 {
let new_idx = (start_idx + offset) % 5;
if let Some(head) = self.heads[new_idx] {
self.current_slot = new_idx;
return Some(head.head);
}
}
None
}
} }
#[derive(Event, Reflect)] #[derive(Event, Reflect)]

View File

@@ -6,9 +6,10 @@ use crate::{
control::controller_common::{CharacterControllerBundle, PlayerMovement}, control::controller_common::{CharacterControllerBundle, PlayerMovement},
global_observer, global_observer,
head::ActiveHead, head::ActiveHead,
head_drop::HeadDrops,
heads::{ActiveHeads, HeadChanged, HeadState}, heads::{ActiveHeads, HeadChanged, HeadState},
heads_database::{HeadControls, HeadsDatabase}, heads_database::{HeadControls, HeadsDatabase},
hitpoints::Hitpoints, hitpoints::{Hitpoints, Kill},
loading_assets::AudioAssets, loading_assets::AudioAssets,
physics_layers::GameLayer, physics_layers::GameLayer,
sounds::PlaySound, sounds::PlaySound,
@@ -63,33 +64,35 @@ fn spawn(
let collider = Collider::capsule(0.9, 1.2); let collider = Collider::capsule(0.9, 1.2);
commands.spawn(( commands
Name::from("player"), .spawn((
Player, Name::from("player"),
ActiveHead(0), Player,
ActiveHeads::new([ ActiveHead(0),
Some(HeadState::new(0, heads_db.as_ref())), ActiveHeads::new([
Some(HeadState::new(3, heads_db.as_ref())), Some(HeadState::new(0, heads_db.as_ref())),
Some(HeadState::new(6, heads_db.as_ref())), Some(HeadState::new(3, heads_db.as_ref())),
Some(HeadState::new(10, heads_db.as_ref())), Some(HeadState::new(6, heads_db.as_ref())),
Some(HeadState::new(9, heads_db.as_ref())), Some(HeadState::new(10, heads_db.as_ref())),
]), Some(HeadState::new(9, heads_db.as_ref())),
Hitpoints::new(100), ]),
CameraTarget, Hitpoints::new(100),
transform, CameraTarget,
Visibility::default(), transform,
CollisionLayers::new( Visibility::default(),
GameLayer::Player, CollisionLayers::new(
LayerMask::ALL & !GameLayer::CollectiblePhysics.to_bits(), LayerMask(GameLayer::Player.to_bits()),
), LayerMask::ALL & !GameLayer::CollectiblePhysics.to_bits(),
CharacterControllerBundle::new(collider, heads_db.head_stats(0).controls), ),
children![( CharacterControllerBundle::new(collider, heads_db.head_stats(0).controls),
Name::new("player-rig"), children![(
PlayerBodyMesh, Name::new("player-rig"),
CameraArmRotation, PlayerBodyMesh,
children![AnimatedCharacter::new(0)] CameraArmRotation,
)], children![AnimatedCharacter::new(0)]
)); )],
))
.observe(on_kill);
commands.spawn(( commands.spawn((
AudioPlayer::new(asset_server.load("sfx/heads/angry demonstrator.ogg")), AudioPlayer::new(asset_server.load("sfx/heads/angry demonstrator.ogg")),
@@ -97,6 +100,24 @@ fn spawn(
)); ));
} }
fn on_kill(
trigger: Trigger<Kill>,
mut commands: Commands,
mut query: Query<(&Transform, &ActiveHead, &mut ActiveHeads, &mut Hitpoints)>,
) {
let Ok((transform, active, mut heads, mut hp)) = query.get_mut(trigger.target()) else {
return;
};
commands.trigger(HeadDrops::new(transform.translation, active.0));
if let Some(new_head) = heads.loose_current() {
hp.set_health(heads.current().unwrap().health);
commands.trigger(HeadChanged(new_head));
}
}
fn cursor_recenter(q_windows: Single<&mut Window, With<PrimaryWindow>>) { fn cursor_recenter(q_windows: Single<&mut Window, With<PrimaryWindow>>) {
let mut primary_window = q_windows; let mut primary_window = q_windows;
let center = Vec2::new( let center = Vec2::new(