use bevy::prelude::*; use bevy_spine::prelude::*; use crate::bullet::{BulletSpawnEvent, BulletSystem}; const PLAYER_TRACK_PORTAL: usize = 0; const PLAYER_TRACK_IDLE: usize = 0; const PLAYER_TRACK_RUN: usize = 1; const PLAYER_TRACK_JUMP: usize = 2; const PLAYER_TRACK_AIM: usize = 3; const PLAYER_TRACK_SHOOT: usize = 4; #[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)] pub enum PlayerSystem { Spawn, SpineReady, SpineEvents, Aim, Shoot, Move, Jump, } pub struct PlayerPlugin; impl Plugin for PlayerPlugin { fn build(&self, app: &mut App) { app.add_event::().add_systems( Update, ( player_spawn.in_set(PlayerSystem::Spawn), player_spine_ready.in_set(PlayerSystem::SpineReady), player_spine_events .in_set(PlayerSystem::SpineEvents) .in_set(SpineSyncSet::BeforeSync), player_aim .in_set(PlayerSystem::Aim) .in_set(SpineSyncSet::DuringSync), player_shoot .in_set(PlayerSystem::Shoot) .in_set(SpineSyncSet::AfterSync) .before(BulletSystem::Spawn), player_move .in_set(PlayerSystem::Move) .in_set(SpineSyncSet::BeforeSync), player_jump .in_set(PlayerSystem::Jump) .in_set(SpineSyncSet::BeforeSync), ), ); } } #[derive(Event)] pub struct PlayerSpawnEvent { pub skeleton: Handle, } #[derive(Component)] pub struct Player { spawned: bool, movement_velocity: f32, } #[derive(Component)] pub struct CrosshairController { bone: Entity, } #[derive(Component)] pub struct ShootController { cooldown: f32, spine: Entity, bone: Entity, } fn player_spawn(mut commands: Commands, mut player_spawn_events: EventReader) { for event in player_spawn_events.read() { commands .spawn(SpineBundle { skeleton: event.skeleton.clone(), transform: Transform::from_xyz(-300., -200., 0.).with_scale(Vec3::ONE * 0.25), ..Default::default() }) .insert(SpineSync) .insert(Player { spawned: false, movement_velocity: 0., }); } } fn player_spine_ready( mut spine_ready_events: EventReader, mut spine_query: Query<(&mut Spine, Entity), With>, mut spine_bone_query: Query<(&mut SpineBone, Entity)>, mut commands: Commands, ) { for event in spine_ready_events.read() { if let Ok((mut spine, spine_entity)) = spine_query.get_mut(event.entity) { let Spine(SkeletonController { animation_state, skeleton, .. }) = spine.as_mut(); let _ = animation_state.set_animation_by_name(PLAYER_TRACK_PORTAL, "portal", false); for (bone, bone_entity) in spine_bone_query.iter_mut() { if let Some(bone) = bone.handle.get(skeleton) { if bone.data().name() == "crosshair" { commands .entity(spine_entity) .insert(CrosshairController { bone: bone_entity }); } else if bone.data().name() == "gun-tip" { commands.entity(spine_entity).insert(ShootController { cooldown: 0., spine: spine_entity, bone: bone_entity, }); } } } } } } fn player_spine_events( mut spine_events: EventReader, mut spine_query: Query<(&mut Spine, &mut Player)>, ) { for event in spine_events.read() { if let SpineEvent::Complete { entity, animation } = event { if let Ok((mut spine, mut player)) = spine_query.get_mut(*entity) { let Spine(controller) = spine.as_mut(); if animation == "portal" { let _ = controller.animation_state.set_animation_by_name( PLAYER_TRACK_IDLE, "idle", true, ); let _ = controller.animation_state.set_animation_by_name( PLAYER_TRACK_AIM, "aim", true, ); let mut run_track = controller .animation_state .set_animation_by_name(PLAYER_TRACK_RUN, "run", true) .unwrap(); run_track.set_shortest_rotation(true); controller .animation_state .track_at_index_mut(PLAYER_TRACK_AIM) .unwrap() .set_alpha(0.); controller .animation_state .track_at_index_mut(PLAYER_TRACK_RUN) .unwrap() .set_alpha(0.); player.spawned = true; } else if animation == "jump" { controller.animation_state.clear_track(PLAYER_TRACK_JUMP); } } } } } fn player_aim( mut crosshair_query: Query<(&mut Spine, Entity, &CrosshairController, &Player)>, bone_query: Query<(Entity, &Parent), With>, mut transform_query: Query<&mut Transform>, global_transform_query: Query<&GlobalTransform>, window_query: Query<&Window>, camera_query: Query<(Entity, &Camera)>, time: Res