diff --git a/src/core/bubble.rs b/src/core/bubble.rs index 44f3724..50ae394 100644 --- a/src/core/bubble.rs +++ b/src/core/bubble.rs @@ -5,6 +5,7 @@ use rand::Rng; use crate::{ core::{ + game_state::DispawnOnGameOver, kirby::Kirby, life::{DamageDealer, Life}, wave::BubbleSplash, @@ -13,7 +14,7 @@ use crate::{ }; #[derive(Component)] -#[require(Sprite, PhysicsBody, DensityObject, Life, DamageDealer, SphereCollider)] +#[require(Sprite, PhysicsBody, DensityObject, Life, DamageDealer, SphereCollider, DispawnOnGameOver)] pub struct Bubble { move_force: f32, } diff --git a/src/core/collectible.rs b/src/core/collectible.rs index 3c40553..db74610 100644 --- a/src/core/collectible.rs +++ b/src/core/collectible.rs @@ -1,6 +1,7 @@ use std::path::Path; use std::time::Duration; +use crate::core::game_state::{DispawnOnGameOver, GameStartupSet, GameState}; use crate::core::kirby::{Kirby, kirby_spawn}; use crate::physics::sphere_collider::SphereCollider; use bevy::prelude::*; @@ -17,7 +18,7 @@ pub enum CollectableType { } #[derive(Component)] -#[require(SphereCollider, Sprite)] +#[require(SphereCollider, Sprite, DispawnOnGameOver)] pub struct Collectable(CollectableType); // Not a system. @@ -83,6 +84,7 @@ fn new_kirby(kirby_pos: Vec3, commands: &mut Commands) { } #[derive(Component)] +#[require(DispawnOnGameOver)] struct KirbySpawnHandler { timer: Timer, position: Vec3, @@ -108,7 +110,7 @@ pub struct CollectablePlugin; impl Plugin for CollectablePlugin { fn build(&self, app: &mut App) { - app.add_systems(Startup, collectable_spawner_init) + app.add_systems(OnEnter(GameState::Starting), collectable_spawner_init.in_set(GameStartupSet)) .add_systems(FixedUpdate, collectable_actions_system) .add_systems(Update, kirby_spawn_timer); } diff --git a/src/core/game_state.rs b/src/core/game_state.rs new file mode 100644 index 0000000..8d80217 --- /dev/null +++ b/src/core/game_state.rs @@ -0,0 +1,87 @@ +use bevy::{ecs::system::SystemParam, prelude::*}; + +use crate::core::{kirby::Kirby, wave::BubbleWaves}; + +#[derive(Event)] +struct EndGameEvent { + pub reason: EndGameReason, +} + +#[derive(Clone, Copy, Debug)] +enum EndGameReason { + Die, + Victory, +} + +fn no_kirbies_emetter(mut commands: Commands, query: Query<(), With>) { + if query.is_empty() { + commands.trigger(EndGameEvent { reason: EndGameReason::Die }); + }; +} + +#[derive(States, Default, Debug, Clone, PartialEq, Eq, Hash)] +pub enum GameState { + #[default] + Starting, + Running, + Paused, +} + +#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)] +pub struct GameStartupSet; + +#[derive(Component, Default)] +pub struct DispawnOnGameOver; + +pub trait Resetable { + fn reset(&mut self); +} + +fn reset_resource(mut res: ResMut) { + res.reset(); +} + +#[derive(SystemParam)] +struct GameRes<'w> { + bubble_waves: ResMut<'w, BubbleWaves>, +} + +fn on_reset_world( + _: On, + mut commands: Commands, + entities: Query>, + game_res: GameRes, +) { + for e in &entities { + commands.entity(e).despawn(); + } + reset_resource(game_res.bubble_waves); + println!("Set state"); + commands.set_state(GameState::Starting); +} + +#[derive(Event)] +struct ResetWorldEvent; + +fn on_end_game(event: On, mut commands: Commands) { + let reason = event.reason; + println!("end game reason {:?}", reason); + commands.trigger(ResetWorldEvent); +} + +fn change_to_running_state(mut commands: Commands) { + println!("Running state !"); + commands.set_state(GameState::Running); +} + +pub struct GameEventsPlugin; + +impl Plugin for GameEventsPlugin { + fn build(&self, app: &mut App) { + app.init_state::() + .add_systems(Update, no_kirbies_emetter.run_if(in_state(GameState::Running))) + .add_systems(OnEnter(GameState::Starting), change_to_running_state.after(GameStartupSet)) + .add_observer(on_end_game) + .add_observer(on_reset_world); + } +} diff --git a/src/core/kirby.rs b/src/core/kirby.rs index bf63599..45b2d06 100644 --- a/src/core/kirby.rs +++ b/src/core/kirby.rs @@ -1,20 +1,23 @@ +use crate::core::game_state::{GameStartupSet, DispawnOnGameOver}; use bevy::prelude::*; const KIRBY_SCALE: f32 = 3.0; use crate::{ - core::life::{DamageDealer, Life}, + core::{ + game_state::GameState, + life::{DamageDealer, Life}, + }, juice::animation::{AnimationIndices, AnimationTimer}, - physics::physics_body::PhysicsBody, - physics::sphere_collider::SphereCollider, + physics::{physics_body::PhysicsBody, sphere_collider::SphereCollider}, }; #[derive(Component)] -#[require(Transform, SphereCollider, DamageDealer)] +#[require(Transform, SphereCollider, DamageDealer, DispawnOnGameOver)] pub struct SuckArea; #[derive(Component)] -#[require(Sprite, PhysicsBody, SphereCollider, AnimationIndices, Life)] +#[require(Sprite, PhysicsBody, SphereCollider, AnimationIndices, Life, DispawnOnGameOver)] pub struct Kirby { pub speed_force: f32, pub is_sucking: bool, @@ -26,6 +29,7 @@ pub fn kirby_spawn_start( asset_server: Res, mut texture_atlas_layouts: ResMut>, ) { + println!("Start Kirby"); kirby_spawn(&mut commands, &asset_server, &mut texture_atlas_layouts, Vec3::ZERO); // kirby_spawn(&mut commands, &asset_server, &mut texture_atlas_layouts, Vec3 { x: 500.0, y: 0.0, z: 0.0 }); } @@ -52,7 +56,7 @@ pub fn kirby_spawn( SphereCollider::new(20.0), animation_indices, AnimationTimer(Timer::from_seconds(0.1, TimerMode::Repeating)), - Life::new(1000.0), + Life::new(10.0), Transform::from_translation(pos).with_scale(Vec3::splat(KIRBY_SCALE)), )) .id(); @@ -119,6 +123,7 @@ pub struct KirbyPlugin; impl Plugin for KirbyPlugin { fn build(&self, app: &mut App) { - app.add_systems(Startup, kirby_spawn_start).add_systems(FixedUpdate, kirby_actions); + app.add_systems(OnEnter(GameState::Starting), kirby_spawn_start.in_set(GameStartupSet)) + .add_systems(FixedUpdate, kirby_actions); } } diff --git a/src/core/life.rs b/src/core/life.rs index dc3f3bd..838e907 100644 --- a/src/core/life.rs +++ b/src/core/life.rs @@ -1,13 +1,17 @@ use bevy::prelude::*; use crate::{ - core::bubble::Bubble, - core::counter::{BubbleExplodedCountThisFrame, BubbleSuckedCountThisFrame, KirbyHitCountThisFrame}, - core::kirby::{Kirby, SuckArea}, + core::{ + bubble::Bubble, + counter::{BubbleExplodedCountThisFrame, BubbleSuckedCountThisFrame, KirbyHitCountThisFrame}, + game_state::DispawnOnGameOver, + kirby::{Kirby, SuckArea}, + }, physics::sphere_collider::SphereCollider, }; #[derive(Component)] +#[require(DispawnOnGameOver)] pub struct Life { max_life: f32, current_life: f32, @@ -42,7 +46,7 @@ pub fn despawn_if_dead(mut commands: Commands, query: Query<(Entity, &Life)>) { } #[derive(Component, Default)] -#[require(SphereCollider)] +#[require(SphereCollider, DispawnOnGameOver)] pub struct DamageDealer { damage_strength: f32, } diff --git a/src/core/mod.rs b/src/core/mod.rs index b30d8d8..57d5538 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -1,6 +1,7 @@ pub mod bubble; pub mod collectible; pub mod counter; +pub mod game_state; pub mod kirby; pub mod life; pub mod wave; diff --git a/src/core/wave.rs b/src/core/wave.rs index 01def64..e4e4ffa 100644 --- a/src/core/wave.rs +++ b/src/core/wave.rs @@ -4,7 +4,12 @@ use bevy::prelude::*; use rand::distr::slice::Empty; use crate::{ - core::bubble::{Bubble, bubble_spawn_wave}, + core::{ + bubble::{Bubble, bubble_spawn_wave}, + game_state::Resetable, + kirby::Kirby, + life::Life, + }, map::map::{BOTTOM, LEFT, RIGHT, TOP}, }; @@ -50,7 +55,7 @@ const GREEN_BUBBLE: BubbleType = BubbleType { #[derive(Resource)] pub struct BubbleWaves { - pub waves: Vec>, + waves: Vec>, pub current_wave: usize, } @@ -75,6 +80,12 @@ fn get_bubble_waves() -> BubbleWaves { } } +impl Resetable for BubbleWaves { + fn reset(&mut self) { + *self = get_bubble_waves(); + } +} + fn no_ennemy_left(bubble_query: Query<(), With>) -> bool { bubble_query.is_empty() } @@ -88,10 +99,16 @@ fn change_wave(mut bubble_wave: ResMut, mut commands: Commands, ass bubble_spawn_wave(&mut commands, &asset_server, bubble_wave.waves[bubble_wave.current_wave].as_slice()); } +fn heal_kirby(query: Query<&mut Life, With>) { + for mut kirby_life in query { + kirby_life.heal(f32::MAX); + } +} + pub struct WavePlugin; impl Plugin for WavePlugin { fn build(&self, app: &mut App) { - app.insert_resource(get_bubble_waves()).add_systems(Update, change_wave.run_if(no_ennemy_left)); + app.insert_resource(get_bubble_waves()).add_systems(Update, (change_wave, heal_kirby).run_if(no_ennemy_left)); } } diff --git a/src/juice/animation.rs b/src/juice/animation.rs index 5ad1bc6..49cb4a5 100644 --- a/src/juice/animation.rs +++ b/src/juice/animation.rs @@ -1,7 +1,8 @@ +use crate::core::game_state::DispawnOnGameOver; use bevy::prelude::*; -// Source : https://bevy.org/examples/2d-rendering/sprite-sheet/ #[derive(Component, Default)] +#[require(DispawnOnGameOver)] pub struct AnimationIndices { pub first: usize, pub last: usize, @@ -15,6 +16,7 @@ impl AnimationIndices { } #[derive(Component, Deref, DerefMut)] +#[require(DispawnOnGameOver)] pub struct AnimationTimer(pub Timer); pub fn animate_sprite(time: Res