diff --git a/src/damagable.rs b/src/damagable.rs index a39dc6d..2d7f1de 100644 --- a/src/damagable.rs +++ b/src/damagable.rs @@ -21,7 +21,9 @@ pub struct Damagable { } #[derive(Event)] -pub struct DamageableKilled(Option); +pub struct DamageableKilled { + pub killed_faction: Option, +} impl Default for Damagable { fn default() -> Self { @@ -58,7 +60,9 @@ pub fn broken_ships_cleanup( for (entity, damagable, faction) in query { if damagable.hp == 0 { commands.get_entity(entity).unwrap().despawn(); - commands.trigger(DamageableKilled(faction.cloned())); + commands.trigger(DamageableKilled { + killed_faction: faction.cloned(), + }); } } } diff --git a/src/main.rs b/src/main.rs index 1c0e031..317d36e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ use crate::asteroid::AsteroidPlugin; use crate::collision::CollisionPlugin; use crate::damagable::DamagablePlugin; use crate::projectile::ProjectilePlugin; +use crate::score::ScorePlugin; use crate::ships::ShipsPlugin; use crate::ui::UIPlugin; use crate::velocity::VelocityPlugin; @@ -14,6 +15,7 @@ mod asteroid; mod collision; mod damagable; mod projectile; +mod score; mod ships; mod ui; mod velocity; @@ -58,6 +60,7 @@ fn main() { ProjectilePlugin, AsteroidPlugin, UIPlugin, + ScorePlugin, )) .add_systems(Startup, |mut commands: Commands| { commands.spawn(Camera2d); diff --git a/src/score.rs b/src/score.rs new file mode 100644 index 0000000..1b7aea2 --- /dev/null +++ b/src/score.rs @@ -0,0 +1,38 @@ +use bevy::prelude::*; + +use crate::{damagable::DamageableKilled, ships::Factions}; + +const ENEMY_REWARD: u64 = 10; + +#[derive(Resource)] +pub struct Score { + pub score: u64, + pub max_score: u64, +} + +pub struct ScorePlugin; + +impl Plugin for ScorePlugin { + fn build(&self, app: &mut App) { + app.insert_resource(Score { + score: 0, + max_score: 0, + }) + .add_observer(on_something_died); + } +} + +pub fn on_something_died(killed: On, mut score: ResMut) { + let Some(faction) = killed.killed_faction else { + return; + }; + + if faction != Factions::EnemyFaction { + return; + } + + score.score += ENEMY_REWARD; + if score.max_score < score.score { + score.max_score = score.score; + } +} diff --git a/src/ships/mod.rs b/src/ships/mod.rs index 0c67994..80218e3 100644 --- a/src/ships/mod.rs +++ b/src/ships/mod.rs @@ -6,7 +6,7 @@ pub mod enemy; pub mod gun; pub mod player; -#[derive(Component, Copy, Clone)] +#[derive(Component, Copy, Clone, PartialEq, Eq)] pub enum Factions { PlayerFaction, EnemyFaction, @@ -39,7 +39,7 @@ impl Plugin for ShipsPlugin { player::player_movement_system.run_if(in_state(GameState::Game)), player::player_shooting_system.run_if(in_state(GameState::Game)), gun::gun_shooting_system.run_if(in_state(GameState::Game)), - enemy::enemy_ai_system, + enemy::enemy_ai_system.run_if(in_state(GameState::Game)), ), ); } diff --git a/src/ui/mod.rs b/src/ui/mod.rs index b719fae..770f582 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -4,9 +4,11 @@ use crate::GameState; use game_over::*; use main_menu::*; +use player_interface::*; mod game_over; mod main_menu; +mod player_interface; pub struct UIPlugin; @@ -14,6 +16,13 @@ impl Plugin for UIPlugin { fn build(&self, app: &mut App) { app.add_systems(OnEnter(GameState::InMenu), setup_menu) .add_systems(Update, button_system.run_if(in_state(GameState::InMenu))) - .add_systems(OnExit(GameState::InMenu), cleanup_menu); + .add_systems(OnExit(GameState::InMenu), cleanup_menu) + .add_systems(OnEnter(GameState::Game), setup_player_interface) + .add_systems( + Update, + (update_player_health_display, update_player_score_display) + .run_if(in_state(GameState::Game)), + ) + .add_systems(OnExit(GameState::Game), cleanup_player_interface); } } diff --git a/src/ui/player_interface.rs b/src/ui/player_interface.rs new file mode 100644 index 0000000..e408473 --- /dev/null +++ b/src/ui/player_interface.rs @@ -0,0 +1,69 @@ +use bevy::prelude::*; + +use crate::{damagable::Damagable, score::Score, ships::player::PlayerMovement}; + +#[derive(Component)] +pub struct PlayerHealthDisplay; +#[derive(Component)] +pub struct PlayerScoreDisplay; + +#[derive(Component)] +pub struct PlayerIntefrace; + +pub fn setup_player_interface(mut commands: Commands) { + commands.spawn(( + PlayerHealthDisplay, + Text::new("HP: 1"), + Node { + left: px(5), + top: px(5), + ..default() + }, + PlayerIntefrace, + )); + commands.spawn(( + PlayerScoreDisplay, + Text::new("Score: 0"), + Node { + left: px(5), + top: px(30), + ..default() + }, + PlayerIntefrace, + )); +} + +pub fn update_player_health_display( + text_query: Single<&mut Text, With>, + score_query: Res, +) { + let mut text = text_query.into_inner(); + let score = score_query.into_inner(); + + text.0 = "Score:".to_string() + (score.score).to_string().as_str(); +} +pub fn update_player_score_display( + text_query: Single<&mut Text, With>, + player_query: Single<&Damagable, With>, +) { + let mut text = text_query.into_inner(); + let player = player_query.into_inner(); + + text.0 = "Health:".to_string() + + ((player.health() as f32) / (player.max_health() as f32)) + .to_string() + .as_str(); +} + +pub fn cleanup_player_interface( + mut commands: Commands, + query: Query>, +) { + for entity in query { + let Ok(mut interface) = commands.get_entity(entity) else { + continue; + }; + + interface.despawn(); + } +}