Replicate Sounds (#68)

This commit is contained in:
PROMETHIA-27
2025-09-29 14:46:38 -04:00
committed by GitHub
parent a07dfb3840
commit a16ee231cc
47 changed files with 992 additions and 721 deletions

115
crates/client/src/player.rs Normal file
View File

@@ -0,0 +1,115 @@
use crate::{
global_observer,
heads_database::{HeadControls, HeadsDatabase},
loading_assets::AudioAssets,
};
use bevy::prelude::*;
use lightyear::prelude::MessageReceiver;
use shared::{
player::PlayerBodyMesh,
protocol::{PlaySound, PlayerId, events::ClientHeadChanged, messages::AssignClientPlayer},
};
pub fn plugin(app: &mut App) {
app.register_type::<ClientPlayerId>();
app.register_type::<LocalPlayer>();
app.init_state::<PlayerAssignmentState>();
app.add_systems(
Update,
receive_player_id.run_if(in_state(PlayerAssignmentState::Waiting)),
);
app.add_systems(
Update,
match_player_id.run_if(in_state(PlayerAssignmentState::IdReceived)),
);
global_observer!(app, on_update_head_mesh);
}
#[derive(Resource, Reflect)]
#[reflect(Resource)]
pub struct ClientPlayerId {
id: u8,
}
fn receive_player_id(
mut commands: Commands,
mut recv: Single<&mut MessageReceiver<AssignClientPlayer>>,
mut next: ResMut<NextState<PlayerAssignmentState>>,
) {
for AssignClientPlayer(id) in recv.receive() {
commands.insert_resource(ClientPlayerId { id });
next.set(PlayerAssignmentState::IdReceived);
info!("player id `{id}` received");
}
}
fn match_player_id(
mut commands: Commands,
players: Query<(Entity, &PlayerId), Changed<PlayerId>>,
client: Res<ClientPlayerId>,
mut next: ResMut<NextState<PlayerAssignmentState>>,
) {
for (entity, player) in players.iter() {
if player.id == client.id {
commands.entity(entity).insert(LocalPlayer);
next.set(PlayerAssignmentState::Confirmed);
info!("player entity {entity:?} confirmed with id `{}`", player.id);
break;
}
}
}
/// Various states while trying to assign and match an ID to the player character.
/// Every client is given an ID (its player index in the match) and every character controller
/// is given an ID matching the client controlling it. This way the client can easily see which
/// controller it owns.
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, States)]
pub enum PlayerAssignmentState {
/// Waiting for the server to send an [`AssignClientPlayer`] message
#[default]
Waiting,
/// Received an [`AssignClientPlayer`], querying for a matching controller
IdReceived,
/// Matching controller confirmed; a [`LocalPlayer`] exists
Confirmed,
}
#[derive(Component, Debug, Reflect)]
#[reflect(Component)]
pub struct LocalPlayer;
fn on_update_head_mesh(
trigger: Trigger<ClientHeadChanged>,
mut commands: Commands,
body_mesh: Single<(Entity, &Children), With<PlayerBodyMesh>>,
head_db: Res<HeadsDatabase>,
audio_assets: Res<AudioAssets>,
sfx: Query<&AudioPlayer>,
) -> Result {
let head = trigger.0 as usize;
let (body_mesh, mesh_children) = *body_mesh;
let head_str = head_db.head_key(head);
commands.trigger(PlaySound::Head(head_str.to_string()));
//TODO: make part of full character mesh later
for child in mesh_children.iter().filter(|child| sfx.contains(*child)) {
commands.entity(child).despawn();
}
if head_db.head_stats(head).controls == HeadControls::Plane {
commands.entity(body_mesh).with_child((
Name::new("sfx"),
AudioPlayer::new(audio_assets.jet.clone()),
PlaybackSettings {
mode: bevy::audio::PlaybackMode::Loop,
..Default::default()
},
));
}
Ok(())
}