diff --git a/src/audio/mod.rs b/src/audio/mod.rs index 2bfcf09..466e22d 100644 --- a/src/audio/mod.rs +++ b/src/audio/mod.rs @@ -89,6 +89,9 @@ fn on_play_soundtrack_event( }; for audio_player_entity in audio_player.iter_mut() { + if audio_player_entity.1.0 == track_handle { + return; + } commands.entity(audio_player_entity.0).despawn(); } diff --git a/src/screens/game.rs b/src/screens/game.rs index 909e9af..487c6d9 100644 --- a/src/screens/game.rs +++ b/src/screens/game.rs @@ -1,13 +1,12 @@ -use bevy::{prelude::*, text::FontSmoothing}; +use bevy::prelude::*; use leafwing_input_manager::prelude::*; use rand::random_range; use crate::{ - PIXEL_PERFECT_LAYERS, ScaleFactor, + PIXEL_PERFECT_LAYERS, audio::{PlaySfxEvent, PlaySoundtrackEvent, SoundEffect, Soundtrack}, input::Action, - screens::Screen, - sundry::BLACK, + screens::{Screen, pause::PauseState}, }; #[derive(Resource)] @@ -27,11 +26,15 @@ pub enum GameState { GameOver, } -#[derive(Component)] -struct Thruster; +#[derive(SystemSet, Debug, Clone, Hash, Eq, PartialEq)] +pub enum GameSystems { + Play, + Environment, + GameOver, +} #[derive(Resource, PartialEq)] -struct HasEntered(bool); +pub struct HasEntered(pub bool); pub(super) fn plugin(app: &mut App) { app.init_state::(); @@ -43,17 +46,16 @@ pub(super) fn plugin(app: &mut App) { // time_played: 0.0, // successful_trip: false, }); - app.insert_resource::(HasEntered(false)); - - app.add_systems( - OnEnter(Screen::Game), - |mut has_entered: ResMut| { - has_entered.0 = false; - }, - ); + app.insert_resource(HasEntered(false)); + app.add_observer(restart_game); + app.add_observer(back_to_menu); app.add_systems(OnEnter(Screen::Game), trigger_music); - app.add_systems(OnEnter(GameState::Play), spawn_game_screen); + + app.add_systems(Startup, spawn_player); + app.configure_sets(Startup, GameSystems::Play); + app.configure_sets(Startup, GameSystems::Environment); + app.add_systems( FixedUpdate, enter_player.run_if( @@ -61,11 +63,6 @@ pub(super) fn plugin(app: &mut App) { .and(in_state(GameState::Play).and(resource_equals(HasEntered(false)))), ), ); - app.add_systems(OnEnter(GameState::GameOver), spawn_game_over); - app.add_systems(OnExit(GameState::GameOver), despawn_game_screen); - app.configure_sets(Startup, GameSystems::Play); - app.configure_sets(Startup, GameSystems::Environment); - app.configure_sets(Startup, GameSystems::GameOver); app.add_systems( FixedUpdate, @@ -79,20 +76,6 @@ pub(super) fn plugin(app: &mut App) { .run_if(in_state(Screen::Game)), ); - app.add_systems( - Update, - (pause) - .in_set(GameSystems::Play) - .run_if(in_state(Screen::Game).and(in_state(GameState::Play))), - ); - - app.add_systems( - Update, - (game_over_input) - .in_set(GameSystems::GameOver) - .run_if(in_state(Screen::Game)), - ); - app.add_systems( FixedUpdate, ( @@ -110,11 +93,10 @@ pub(super) fn plugin(app: &mut App) { ))); } -fn spawn_game_screen( - mut commands: Commands, - asset_server: Res, - mut time: ResMut>, -) { +#[derive(Component)] +struct Thruster; + +fn spawn_player(mut commands: Commands, asset_server: Res) { let default_speed = asset_server.load("sprites/ships/ship3-default.png"); commands.spawn(( Name::new("Player Ship"), @@ -122,7 +104,6 @@ fn spawn_game_screen( can_shoot: true, timer: Timer::from_seconds(0.15, TimerMode::Once), }, - DespawnOnExit(Screen::Game), Sprite { image: asset_server.load("sprites/ships/ship3.png"), custom_size: Some(Vec2::splat(32.0)), @@ -148,8 +129,6 @@ fn spawn_game_screen( } )], )); - - time.unpause(); } fn enter_player( @@ -180,33 +159,8 @@ fn enter_player( thruster_sprite.image = ship_speed_sprites.fast.clone(); } -fn despawn_game_screen( - mut commands: Commands, - player: Query>, - asteraids: Query>, - projectiles: Query>, -) { - let player = player.single().unwrap(); - commands.entity(player).despawn(); - - for asteroid in asteraids.iter() { - commands.entity(asteroid).despawn(); - } - - for projectile in projectiles.iter() { - commands.entity(projectile).despawn(); - } -} - -#[derive(SystemSet, Debug, Clone, Hash, Eq, PartialEq)] -enum GameSystems { - Play, - Environment, - GameOver, -} - #[derive(Component)] -struct Player { +pub struct Player { pub can_shoot: bool, pub timer: Timer, } @@ -313,17 +267,6 @@ fn player_input( } } -fn pause(mut time: ResMut>, input_query: Query<&ActionState>) { - let action_state = input_query.single().unwrap(); - if action_state.just_pressed(&Action::Pause) { - if time.is_paused() { - time.unpause(); - } else { - time.pause(); - } - } -} - fn update_projectiles(mut projectiles: Query<&mut Transform, With>) { for mut projectile in projectiles.iter_mut() { projectile.translation += Vec3::Y * 15.0; @@ -430,19 +373,20 @@ fn spawn_asteroid( } fn collide_with_asteroid_check( + mut commands: Commands, player: Query<&Transform, With>, asteroids: Query<&Transform, With>, mut time: ResMut>, mut state: ResMut>, + asset_server: Res, + mut texture_atlases: ResMut>, ) { let player = player.single(); if player.is_err() { return; } let player_transform = player.unwrap(); - let hitbox_size = 8.0; - for asteroid_transform in asteroids.iter() { let distance = player_transform .translation @@ -450,6 +394,29 @@ fn collide_with_asteroid_check( if distance < (hitbox_size / 2.0) + (30.0 / 2.0) { time.pause(); state.set(GameState::GameOver); + commands.trigger(PlaySfxEvent { + sfx: SoundEffect::Explosion, + }); + let texture_atlas_layout = + TextureAtlasLayout::from_grid(UVec2::splat(32), 8, 1, None, None); + let texture_atlas_handle = texture_atlases.add(texture_atlas_layout); + commands.trigger(PlaySfxEvent { + sfx: SoundEffect::Explosion, + }); + commands.spawn(( + Name::new("Explosion"), + Sprite::from_atlas_image( + asset_server.load("sprites/explosion.png"), + TextureAtlas::from(texture_atlas_handle), + ), + Transform { + translation: player_transform.translation, + ..default() + }, + DespawnOnExit(Screen::Game), + Explosion, + PIXEL_PERFECT_LAYERS, + )); } } } @@ -518,220 +485,50 @@ fn update_explosions( } } -fn spawn_game_over( - mut commands: Commands, - asset_server: Res, - scale_factor: Res, - game_stats: Res, -) { - let scale = scale_factor.0; - let font_handle: Handle = asset_server.load("font.ttf"); - - commands.spawn(( - Name::new("Game Over Screen"), - Transform { - translation: Vec3::new(0.0, 0.0, 100.0), - ..default() - }, - Node { - width: Val::Percent(70.0), - height: Val::Percent(70.0), - position_type: PositionType::Absolute, - justify_content: JustifyContent::Center, - margin: UiRect::all(Val::Auto), - ..default() - }, - DespawnOnExit(Screen::Game), - DespawnOnExit(GameState::GameOver), - PIXEL_PERFECT_LAYERS, - children![ - ( - Name::new("Game Over Text"), - Text::new("GAME OVER"), - TextFont { - font_size: 16.0 * scale, - font: font_handle.clone(), - font_smoothing: FontSmoothing::None, - - ..default() - }, - Node { - position_type: PositionType::Absolute, - top: Val::Px(8.0 * scale), - margin: UiRect { - left: Val::Auto, - right: Val::Auto, - ..default() - }, - ..default() - } - ), - ( - Name::new("Stats container"), - Node { - display: Display::Flex, - flex_direction: FlexDirection::Column, - ..default() - }, - children![ - stats_row( - "DISTANCE TRAVELED", - &game_stats.distance_traveled.to_string(), - scale, - font_handle.clone(), - ), - stats_row( - "SHOTS FIRED", - &game_stats.shots_fired.to_string(), - scale, - font_handle.clone(), - ), - stats_row( - "SHOTS HIT", - &game_stats.shots_hit.to_string(), - scale, - font_handle.clone() - ), - stats_row( - "ASTEROIDS DESTROYED", - &game_stats.asteroids_destroyed.to_string(), - scale, - font_handle.clone(), - ), - ] - ), - ( - Name::new("Replay Button"), - Button, - Node { - position_type: PositionType::Absolute, - bottom: Val::Px(32.0 * scale), - ..default() - }, - children![( - Name::new("Replay Button Text"), - Text::new("REPLAY"), - Node::default(), - TextFont { - font_size: 16.0 * scale, - font: font_handle.clone(), - font_smoothing: FontSmoothing::None, - ..default() - } - )], - ), - ( - Name::new("Back to Menu Button"), - Button, - Node { - position_type: PositionType::Absolute, - bottom: Val::Px(8.0 * scale), - ..default() - }, - children![( - Name::new("Back to Menu Text"), - Text::new("BACK TO MENU"), - Node::default(), - TextFont { - font_size: 16.0 * scale, - font: font_handle.clone(), - font_smoothing: FontSmoothing::None, - - ..default() - } - )] - ) - ], - )); +fn trigger_music(mut commands: Commands) { + commands.trigger(PlaySoundtrackEvent { + soundtrack: Soundtrack::BattleTheme, + }); } -fn game_over_input( - input_query: Query<&ActionState>, - current_game_state: Res>, - mut state: ResMut>, - interaction_query: Query<&Interaction, (Changed, With