Files
HEDZReloaded/crates/server/src/head_drop.rs
PROMETHIA-27 b83e506a4d Bevy 0.17 Migration Final PR (#76)
* Get bevy 0.17 compiling and running (#72)

* get bevy 0.17 compiling and running

* try to fix CI breaking from const assertion for client/server features

* fix `bin` -> `lib` for `shared` in CI

* typo

* fix some collider issues (#73)

* Physics/controller improvements (#74)

* trying to fix physics prediction

* fixed prediction desync

* substantial controller improvements

* Finish off main bevy 0.17 migration (#75)

* fix lookdir issues
- airplane moving backwards
- player model facing backwards
- camera was technically backwards the whole time, and player models were facing the right way; camera is now facing forwards
- firing without a target now respects lookdir

* fix aim targeting

* migrate to bevy_trenchbroom 0.10 crates release

* fixed colliders not being adjusted out of worldspace

* predict platforms to stop constant rollbacks while riding them

* fix key/head drop visuals not working

* Fix key/head drop random initial force

* fixed static head drops duplicating

* fix platform velocity inheritance

* fix thrown projectiles not autorotating

* fix inconsistent explosion animations

* update avian3d to 0.4.1

* fix controller snapping to fixed angle upon switching heads

* clean up commented code

* fix broken physics positions

* Clean comments, fix warnings (#77)

* clean comments, fix warnings

* fix missing import

* steamworks 162 libs

* fix mouselook

---------

Co-authored-by: extrawurst <mail@rusticorn.com>
2025-11-15 09:16:38 -05:00

116 lines
3.5 KiB
Rust

use avian3d::prelude::*;
use bevy::{
ecs::{relationship::RelatedSpawner, spawn::SpawnWith},
prelude::*,
};
use lightyear::prelude::{NetworkTarget, Replicate};
use shared::{
global_observer,
head_drop::{HeadCollected, HeadDrop, HeadDropEnableTime, HeadDrops, SecretHeadMarker},
heads_database::HeadsDatabase,
physics_layers::GameLayer,
player::Player,
protocol::{GltfSceneRoot, PlaySound},
utils::{
billboards::Billboard, one_shot_force::OneShotForce, squish_animation::SquishAnimation,
},
};
use std::f32::consts::PI;
pub fn plugin(app: &mut App) {
global_observer!(app, on_head_drop);
}
fn on_head_drop(
trigger: On<HeadDrops>,
mut commands: Commands,
heads_db: Res<HeadsDatabase>,
time: Res<Time>,
) -> Result<(), BevyError> {
let drop = trigger.event();
let should_impulse = drop.impulse;
let angle = rand::random::<f32>() * PI * 2.;
let spawn_dir = Quat::from_rotation_y(angle) * Vec3::new(0.5, 0.6, 0.).normalize();
let spawn_force = if should_impulse {
spawn_dir * 180.0 / time.delta_secs()
} else {
Vec3::ZERO
};
if drop.impulse {
commands.trigger(PlaySound::HeadDrop);
}
let mesh_addr = format!("{:?}", heads_db.head_stats(drop.head_id).ability).to_lowercase();
commands
.spawn((
Name::new("headdrop"),
Transform::from_translation(drop.pos),
Visibility::default(),
Collider::sphere(1.5),
LockedAxes::ROTATION_LOCKED,
RigidBody::Dynamic,
OneShotForce(spawn_force),
CollisionLayers::new(
GameLayer::CollectiblePhysics,
LayerMask::ALL & !GameLayer::Player.to_bits(),
),
Restitution::new(0.6),
Children::spawn(SpawnWith({
let head_id = drop.head_id;
let now = time.elapsed_secs();
move |parent: &mut RelatedSpawner<ChildOf>| {
parent
.spawn((
Collider::sphere(1.5),
CollisionLayers::new(GameLayer::CollectibleSensors, LayerMask::NONE),
Sensor,
CollisionEventsEnabled,
HeadDrop { head_id },
HeadDropEnableTime(now + 1.2),
))
.observe(on_collect_head);
}
})),
Replicate::to_clients(NetworkTarget::All),
))
.with_child((
Billboard::All,
SquishAnimation(2.6),
GltfSceneRoot::HeadDrop(mesh_addr),
));
Ok(())
}
fn on_collect_head(
trigger: On<CollisionStart>,
mut commands: Commands,
query_player: Query<&Player>,
query_collectable: Query<(&HeadDrop, &ChildOf)>,
query_secret: Query<&SecretHeadMarker>,
) {
let collectable = trigger.event().collider1;
let collider = trigger.event().collider2;
if query_player.contains(collider) {
let (drop, child_of) = query_collectable.get(collectable).unwrap();
let is_secret = query_secret.contains(collectable);
if is_secret {
commands.trigger(PlaySound::SecretHeadCollect);
} else {
commands.trigger(PlaySound::HeadCollect);
}
commands.entity(collider).trigger(|entity| HeadCollected {
head: drop.head_id,
entity,
});
commands.entity(child_of.parent()).despawn();
}
}