use bevy::prelude::*; use lightyear::prelude::{input::native::ActionState, *}; use shared::{ backpack::{Backpack, backpack_ui::BackpackUiState}, camera::{CameraArmRotation, CameraTarget}, cash::CashResource, character::AnimatedCharacter, control::{ControlState, controller_common::PlayerCharacterController}, global_observer, head::ActiveHead, head_drop::HeadDrops, heads::{ActiveHeads, HeadChanged, HeadState, heads_ui::UiActiveHeads}, heads_database::HeadsDatabase, hitpoints::{Hitpoints, Kill}, npc::SpawnCharacter, player::{Player, PlayerBodyMesh}, protocol::{ PlaySound, PlayerId, channels::UnorderedReliableChannel, events::ClientHeadChanged, }, tb_entities::SpawnPoint, }; pub fn plugin(app: &mut App) { global_observer!(app, on_update_head_mesh); } pub fn spawn( mut commands: Commands, owner: Entity, query: Query<&Transform, With>, heads_db: Res, ) -> Option { let spawn = query.iter().next()?; let transform = Transform::from_translation(spawn.translation + Vec3::new(0., 3., 0.)); let mut player = commands.spawn(( ( Name::from("player"), Player, ActiveHead(0), ActiveHeads::new([ Some(HeadState::new(0, heads_db.as_ref())), Some(HeadState::new(3, heads_db.as_ref())), Some(HeadState::new(6, heads_db.as_ref())), Some(HeadState::new(10, heads_db.as_ref())), Some(HeadState::new(9, heads_db.as_ref())), ]), Hitpoints::new(100), CashResource::default(), CameraTarget, transform, Visibility::default(), PlayerCharacterController, PlayerId { id: 0 }, ), ActionState::::default(), Backpack::default(), BackpackUiState::default(), UiActiveHeads::default(), Replicate::to_clients(NetworkTarget::All), PredictionTarget::to_clients(NetworkTarget::All), ControlledBy { owner, lifetime: Lifetime::SessionBased, }, children![( Name::new("player-rig"), PlayerBodyMesh, CameraArmRotation, children![AnimatedCharacter::new(0)], )], )); player.observe(on_kill); let id = player.id(); commands.trigger(PlaySound::Head("angry demonstrator".to_string())); commands.trigger(SpawnCharacter(transform.translation)); Some(id) } fn on_kill( trigger: Trigger, 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 on_update_head_mesh( trigger: Trigger, mut commands: Commands, mesh_children: Single<&Children, With>, mut sender: Single<&mut TriggerSender>, animated_characters: Query<&AnimatedCharacter>, mut player: Single<&mut ActiveHead, With>, ) -> Result { let animated_char = mesh_children .iter() .find(|child| animated_characters.contains(*child)) .ok_or("tried to update head mesh before AnimatedCharacter was readded")?; player.0 = trigger.0; commands .entity(animated_char) .insert(AnimatedCharacter::new(trigger.0)); sender.trigger::(ClientHeadChanged(trigger.0 as u64)); Ok(()) }